newrelic 10.6.2 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ const {
15
15
  } = require('../../util/application-logging')
16
16
  const semver = require('semver')
17
17
 
18
- module.exports = function instrument(shim) {
18
+ module.exports = function instrument(shim, tools) {
19
19
  const pinoVersion = shim.require('./package.json').version
20
20
 
21
21
  if (semver.lt(pinoVersion, '7.0.0')) {
@@ -23,7 +23,6 @@ module.exports = function instrument(shim) {
23
23
  return
24
24
  }
25
25
 
26
- const tools = shim.require('./lib/tools')
27
26
  const agent = shim.agent
28
27
  const config = agent.config
29
28
 
@@ -35,18 +34,6 @@ module.exports = function instrument(shim) {
35
34
  const metrics = agent.metrics
36
35
  createModuleUsageMetric('pino', metrics)
37
36
 
38
- const levelUtils = shim.require('./lib/levels')
39
-
40
- /**
41
- * Creates an object where the keys are the level labels and values are the level number.
42
- * Pino passes in level as number but our spec needs it to be the label
43
- */
44
- const levelMap = Object.entries(levelUtils.levels).reduce((levels, level) => {
45
- const [label, number] = level
46
- levels[number] = label
47
- return levels
48
- }, {})
49
-
50
37
  const symbols = shim.require('./lib/symbols')
51
38
 
52
39
  shim.wrap(tools, 'asJson', function wrapJson(shim, asJson) {
@@ -62,10 +49,10 @@ module.exports = function instrument(shim) {
62
49
  */
63
50
  return function wrappedAsJson() {
64
51
  const args = shim.argsToArray.apply(shim, arguments)
52
+ const level = this?.levels?.labels?.[args[2]]
65
53
 
66
54
  if (isMetricsEnabled(config)) {
67
- const level = args[2]
68
- incrementLoggingLinesMetrics(levelMap[level], metrics)
55
+ incrementLoggingLinesMetrics(level, metrics)
69
56
  }
70
57
 
71
58
  if (isLocalDecoratingEnabled(config)) {
@@ -86,7 +73,7 @@ module.exports = function instrument(shim) {
86
73
  logLine,
87
74
  agent,
88
75
  chindings,
89
- levelMap,
76
+ level,
90
77
  logger: shim.logger
91
78
  })
92
79
 
@@ -110,10 +97,10 @@ module.exports = function instrument(shim) {
110
97
  * @param logLine.agent
111
98
  * @param logLine.chindings
112
99
  * @param logLine.msg
113
- * @param logLine.levelMap
100
+ * @param logLine.level
114
101
  * @param logLine.logger
115
102
  */
116
- function reformatLogLine({ logLine, msg, agent, chindings = '', levelMap, logger }) {
103
+ function reformatLogLine({ logLine, msg, agent, chindings = '', level, logger }) {
117
104
  const metadata = agent.getLinkingMetadata()
118
105
 
119
106
  /**
@@ -145,7 +132,7 @@ function reformatLogLine({ logLine, msg, agent, chindings = '', levelMap, logger
145
132
  reformatError(formattedLog)
146
133
  }
147
134
  Object.assign(formattedLog, agentMeta)
148
- formattedLog.level = levelMap[formattedLog.level]
135
+ formattedLog.level = level
149
136
  delete formattedLog.time
150
137
  delete formattedLog.msg
151
138
  return formattedLog
@@ -12,48 +12,38 @@ const NAMES = require('../metrics/names')
12
12
  const NEWRELIC_SYNTHETICS_HEADER = 'x-newrelic-synthetics'
13
13
  const symbols = require('../symbols')
14
14
  const { executionAsyncResource } = require('async_hooks')
15
- const tls = require('tls')
16
- const net = require('net')
17
-
18
- let diagnosticsChannel = null
19
- try {
20
- diagnosticsChannel = require('diagnostics_channel')
21
- } catch (e) {
22
- // quick check to see if module exists
23
- // module was not added until v15.x
24
- }
15
+ const diagnosticsChannel = require('diagnostics_channel')
25
16
 
26
- module.exports = function addUndiciChannels(agent, _undici, _modName, shim) {
27
- if (!diagnosticsChannel || !agent.config.feature_flag.undici_instrumentation) {
28
- logger.warn(
29
- 'diagnostics_channel or feature_flag.undici_instrumentation = false. Skipping undici instrumentation.'
30
- )
31
- return
32
- }
33
-
34
- registerHookPoints(shim)
35
- }
17
+ const channels = [
18
+ { channel: diagnosticsChannel.channel('undici:request:create'), hook: requestCreateHook },
19
+ { channel: diagnosticsChannel.channel('undici:request:headers'), hook: requestHeadersHook },
20
+ { channel: diagnosticsChannel.channel('undici:request:trailers'), hook: endAndRestoreSegment },
21
+ { channel: diagnosticsChannel.channel('undici:request:error'), hook: endAndRestoreSegment }
22
+ ]
36
23
 
37
24
  /**
38
25
  * Subscribes to all relevant undici hook points
39
26
  * See: https://github.com/nodejs/undici/blob/main/docs/api/DiagnosticsChannel.md
40
27
  *
41
- * @param {Shim} shim instance of shim
28
+ * @param agent
29
+ * @param _undici
30
+ * @param _modName
31
+ * @param shim
42
32
  */
43
- function registerHookPoints(shim) {
44
- diagnosticsChannel.channel('undici:request:create').subscribe(requestCreateHook.bind(null, shim))
45
- diagnosticsChannel
46
- .channel('undici:client:sendHeaders')
47
- .subscribe(responseHeadersHook.bind(null, shim))
48
- diagnosticsChannel
49
- .channel('undici:request:headers')
50
- .subscribe(requestHeadersHook.bind(null, shim))
51
- diagnosticsChannel
52
- .channel('undici:request:trailers')
53
- .subscribe(endAndRestoreSegment.bind(null, shim))
54
- diagnosticsChannel
55
- .channel('undici:request:error')
56
- .subscribe(endAndRestoreSegment.bind(null, shim))
33
+ module.exports = function addUndiciChannels(agent, _undici, _modName, shim) {
34
+ channels.forEach(({ channel, hook }) => {
35
+ if (!channel.hasSubscribers) {
36
+ channel.subscribe(hook.bind(null, shim))
37
+ }
38
+ })
39
+ }
40
+
41
+ module.exports.unsubscribe = function unsubscribe() {
42
+ channels.forEach(({ channel, hook }) => {
43
+ if (channel.hasSubscribers) {
44
+ channel.unsubscribe(hook)
45
+ }
46
+ })
57
47
  }
58
48
 
59
49
  /**
@@ -90,29 +80,14 @@ function getParentSegment(shim) {
90
80
  }
91
81
 
92
82
  /**
93
- * This event occurs after the Undici Request is created
94
- * We will check current segment for opaque and also attach
95
- * relevant headers to outgoing http request
83
+ * Injects relevant DT headers for the external request
96
84
  *
97
- * @param {Shim} shim instance of shim
98
- * @param {object} params object from undici hook
85
+ * @param {object} params object to fn
86
+ * @param {Shim} params.transaction current transaction
99
87
  * @param {object} params.request undici request object
88
+ * @param {object} params.config agent config
100
89
  */
101
- function requestCreateHook(shim, { request }) {
102
- const { config } = shim.agent
103
- const parent = getParentSegment(shim)
104
- request[symbols.parentSegment] = parent
105
- if (!parent || (parent && parent.opaque)) {
106
- logger.trace(
107
- 'Not capturing data for outbound request (%s) because parent segment opaque (%s)',
108
- request.path,
109
- parent && parent.name
110
- )
111
-
112
- return
113
- }
114
-
115
- const transaction = parent.transaction
90
+ function addDTHeaders({ transaction, config, request }) {
116
91
  const outboundHeaders = Object.create(null)
117
92
  if (config.encoding_key && transaction.syntheticsHeader) {
118
93
  outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
@@ -133,37 +108,19 @@ function requestCreateHook(shim, { request }) {
133
108
  }
134
109
 
135
110
  /**
136
- * This event occurs right before the data is written to the socket.
137
- * Undici has some abstracted headers that are only created at this time, one
138
- * is the `host` header which we need to name the Undici segment. So in this
139
- * handler we create, start and set the segment active, name it, and
140
- * attach the url/procedure/request.parameters
111
+ * Creates the external segment with url, procedure and request.parameters attributes
141
112
  *
142
- * @param {Shim} shim instance of shim
143
- * @param {object} params object from undici hook
113
+ * @param {object} params object to fn
114
+ * @param {Shim} params.shim instance of shim
144
115
  * @param {object} params.request undici request object
145
- * @param {tls.TLSSocket | net.Socket} params.socket active socket connection
116
+ * @param {object} params.parentSegment current active, about to be parent of external segment
146
117
  */
147
- function responseHeadersHook(shim, { request, socket }) {
148
- const parentSegment = request[symbols.parentSegment]
149
- if (!parentSegment || (parentSegment && parentSegment.opaque)) {
150
- return
151
- }
152
-
153
- const port = socket.remotePort
154
- const isHttps = socket.servername
155
- let urlString
156
- if (isHttps) {
157
- urlString = `https://${socket.servername}`
158
- urlString += port === 443 ? request.path : `:${port}${request.path}`
159
- } else {
160
- urlString = `http://${socket._host}`
161
- urlString += port === 80 ? request.path : `:${port}${request.path}`
162
- }
163
-
164
- const url = new URL(urlString)
165
-
118
+ function createExternalSegment({ shim, request, parentSegment }) {
119
+ const url = new URL(request.origin + request.path)
166
120
  const name = NAMES.EXTERNAL.PREFIX + url.host + url.pathname
121
+ // Metrics for `External/<host>` will have a suffix of undici
122
+ // We will have to see if this matters for people only using fetch
123
+ // It's undici under the hood so ¯\_(ツ)_/¯
167
124
  const segment = shim.createSegment(name, recordExternal(url.host, 'undici'), parentSegment)
168
125
  if (segment) {
169
126
  segment.start()
@@ -178,6 +135,38 @@ function responseHeadersHook(shim, { request, socket }) {
178
135
  }
179
136
  }
180
137
 
138
+ /**
139
+ * This event occurs after the Undici Request is created.
140
+ * We will check current segment for opaque before creating the
141
+ * external segment with the standard url/procedure/request.parameters
142
+ * attributes. We will also attach relevant DT headers to outgoing http request.
143
+ *
144
+ * @param {Shim} shim instance of shim
145
+ * @param {object} params object from undici hook
146
+ * @param {object} params.request undici request object
147
+ */
148
+ function requestCreateHook(shim, { request }) {
149
+ const { config } = shim.agent
150
+ const parentSegment = getParentSegment(shim)
151
+ request[symbols.parentSegment] = parentSegment
152
+ if (!parentSegment || (parentSegment && parentSegment.opaque)) {
153
+ logger.trace(
154
+ 'Not capturing data for outbound request (%s) because parent segment opaque (%s)',
155
+ request.path,
156
+ parentSegment && parentSegment.name
157
+ )
158
+
159
+ return
160
+ }
161
+
162
+ try {
163
+ addDTHeaders({ transaction: parentSegment.transaction, config, request })
164
+ createExternalSegment({ shim, request, parentSegment })
165
+ } catch (err) {
166
+ logger.warn(err, 'Unable to create external segment')
167
+ }
168
+ }
169
+
181
170
  /**
182
171
  * This event occurs after the response headers have been received.
183
172
  * We will add the relevant http response attributes to active segment.
@@ -11,7 +11,7 @@ const MODULE_TYPE = require('./shim/constants').MODULE_TYPE
11
11
  module.exports = function instrumentations() {
12
12
  return {
13
13
  'aws-sdk': { module: '@newrelic/aws-sdk' },
14
- 'amqplib': { type: MODULE_TYPE.MESSAGE },
14
+ 'amqplib': { module: './instrumentation/amqplib' },
15
15
  'cassandra-driver': { type: MODULE_TYPE.DATASTORE },
16
16
  'connect': { type: MODULE_TYPE.WEB_FRAMEWORK },
17
17
  'bluebird': { type: MODULE_TYPE.PROMISE },
@@ -36,7 +36,6 @@ module.exports = function instrumentations() {
36
36
  '@redis/client': { type: MODULE_TYPE.DATASTORE },
37
37
  'restify': { type: MODULE_TYPE.WEB_FRAMEWORK },
38
38
  'superagent': { module: '@newrelic/superagent' },
39
- 'undici': { type: MODULE_TYPE.TRANSACTION },
40
39
  '@hapi/vision': { type: MODULE_TYPE.WEB_FRAMEWORK },
41
40
  'when': { module: './instrumentation/when' },
42
41
  'winston': { type: MODULE_TYPE.GENERIC },
@@ -272,8 +272,7 @@ const INFINITE_TRACING = {
272
272
  const FEATURES = {
273
273
  ESM: {
274
274
  LOADER: `${SUPPORTABILITY.FEATURES}/ESM/Loader`,
275
- UNSUPPORTED_LOADER: `${SUPPORTABILITY.FEATURES}/ESM/UnsupportedLoader`,
276
- CUSTOM_INSTRUMENTATION: `${SUPPORTABILITY.FEATURES}/ESM/CustomInstrumentation`
275
+ UNSUPPORTED_LOADER: `${SUPPORTABILITY.FEATURES}/ESM/UnsupportedLoader`
277
276
  },
278
277
  CJS: {
279
278
  PRELOAD: `${SUPPORTABILITY.FEATURES}/CJS/Preload`,
@@ -736,7 +736,7 @@ function recordSubscribedConsume(nodule, properties, spec) {
736
736
  }
737
737
  }
738
738
  if (msgDesc.headers) {
739
- shim.handleCATHeaders(msgDesc.headers, tx.baseSegment, shim._transportType)
739
+ shim.handleMqTracingHeaders(msgDesc.headers, tx.baseSegment, shim._transportType)
740
740
  }
741
741
 
742
742
  shim.logger.trace('Started message transaction %s named %s', tx.id, txName)
@@ -66,7 +66,7 @@ TransactionShim.prototype.bindCreateTransaction = bindCreateTransaction
66
66
  TransactionShim.prototype.pushTransactionName = pushTransactionName
67
67
  TransactionShim.prototype.popTransactionName = popTransactionName
68
68
  TransactionShim.prototype.setTransactionName = setTransactionName
69
- TransactionShim.prototype.handleCATHeaders = handleCATHeaders
69
+ TransactionShim.prototype.handleMqTracingHeaders = handleMqTracingHeaders
70
70
  TransactionShim.prototype.insertCATReplyHeader = insertCATReplyHeader
71
71
  TransactionShim.prototype.insertCATRequestHeaders = insertCATRequestHeaders
72
72
 
@@ -204,7 +204,7 @@ function setTransactionName(name) {
204
204
  /**
205
205
  * Retrieves whatever CAT headers may be in the given headers.
206
206
  *
207
- * - `handleCATHeaders(headers [, segment [, transportType]])`
207
+ * - `handleMqTracingHeaders(headers [, segment [, transportType]])`
208
208
  *
209
209
  * @memberof TransactionShim.prototype
210
210
  *
@@ -218,8 +218,8 @@ function setTransactionName(name) {
218
218
  * @param {string} [transportType='Unknown']
219
219
  * The transport type that brought the headers. Usually `HTTP` or `HTTPS`.
220
220
  */
221
- function handleCATHeaders(headers, segment, transportType) {
222
- // TODO: rename function or replace functionality when CAT fully removed.
221
+ function handleMqTracingHeaders(headers, segment, transportType) {
222
+ // TODO: replace functionality when CAT fully removed.
223
223
 
224
224
  if (!headers) {
225
225
  this.logger.debug('No headers for CAT or DT processing.')