newrelic 11.17.0 → 11.18.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.
package/NEWS.md CHANGED
@@ -1,3 +1,36 @@
1
+ ### v11.18.0 (2024-05-29)
2
+
3
+ #### Features
4
+
5
+ * Added support for redis v4 legacyMode `client.v4.<command>` ([#2200](https://github.com/newrelic/node-newrelic/pull/2200)) ([b2284c5](https://github.com/newrelic/node-newrelic/commit/b2284c58eb798ab00a141ce08f4072584795c728))
6
+
7
+ #### Code refactoring
8
+
9
+ * Updated `shim.recordConsume` to use `shim.record` and added ability to invoke an after hook with callback args ([#2207](https://github.com/newrelic/node-newrelic/pull/2207)) ([4f48fc3](https://github.com/newrelic/node-newrelic/commit/4f48fc3bbd5afce4954019620b112f600b2dfb53))
10
+
11
+ #### Documentation
12
+
13
+ * Updated compatibility report ([#2223](https://github.com/newrelic/node-newrelic/pull/2223)) ([a256ca0](https://github.com/newrelic/node-newrelic/commit/a256ca0f2060efe576018527d37718a041ffc925))
14
+
15
+ #### Miscellaneous chores
16
+
17
+ * Removed outdated Slack reference ([#2198](https://github.com/newrelic/node-newrelic/pull/2198)) ([8110d42](https://github.com/newrelic/node-newrelic/commit/8110d426f402659d44bf786fec0503ca2413ee6b))
18
+ * Removed package-lock.json ([#2208](https://github.com/newrelic/node-newrelic/pull/2208)) ([b267695](https://github.com/newrelic/node-newrelic/commit/b26769502b4cb38f9133f76f427ee96ce718c15b))
19
+ * Removed unused express5 feature flag ([#2197](https://github.com/newrelic/node-newrelic/pull/2197)) ([366504b](https://github.com/newrelic/node-newrelic/commit/366504ba1e3bfed477a37be5d61f4e30e46b690d))
20
+
21
+ #### Tests
22
+
23
+ * Updated langchain versioned tests to separate the vectorstore and other tests. This is to avoid peer resolution of `@langchain/community` package that is being used to test the elasticsearch vectorstore ([#2216](https://github.com/newrelic/node-newrelic/pull/2216)) ([e768955](https://github.com/newrelic/node-newrelic/commit/e7689554aec2a038d5b80c54d4b6b603075856ac))
24
+ * Fixed aws-sdk-v3 bedrock tests ([#2211](https://github.com/newrelic/node-newrelic/pull/2211)) ([050469e](https://github.com/newrelic/node-newrelic/commit/050469ecc59ccc9ba6a6e78527ac392f262ff939))
25
+ * Fixed aws-sdk-v3 bedrock tests (again) ([#2212](https://github.com/newrelic/node-newrelic/pull/2212)) ([330cc4b](https://github.com/newrelic/node-newrelic/commit/330cc4b6ff8e3777b1dfa0fda7f5b4c5b20005af))
26
+
27
+ #### Continuous integration
28
+
29
+ * Updated the langchain vectorstore test to reference the correct version of `@langchain/core` getting tested ([#2202](https://github.com/newrelic/node-newrelic/pull/2202)) ([ed81852](https://github.com/newrelic/node-newrelic/commit/ed81852a4b3c582a54c0d274ba9d5887041ebeef))
30
+ * Added updating of docs site with compatibility table ([#2205](https://github.com/newrelic/node-newrelic/pull/2205)) ([c28a938](https://github.com/newrelic/node-newrelic/commit/c28a9387f68feb8fa59d4134828662b3babeb9d0))
31
+ * Fixed CI jobs ([#2215](https://github.com/newrelic/node-newrelic/pull/2215)) ([8b030fb](https://github.com/newrelic/node-newrelic/commit/8b030fbf479cd348e9b75544bc753c1d618bfca7))
32
+ * Fixed should-run CI step ([#2214](https://github.com/newrelic/node-newrelic/pull/2214)) ([d0a481d](https://github.com/newrelic/node-newrelic/commit/d0a481d4b58706c4793fa12fa7706091eaffa0a4))
33
+
1
34
  ### v11.17.0 (2024-05-15)
2
35
 
3
36
  #### Features
@@ -7,7 +7,10 @@
7
7
 
8
8
  // unreleased flags gating an active feature
9
9
  exports.prerelease = {
10
- express5: false,
10
+ // internal_test_only is used for testing our feature flag implementation.
11
+ // It is not used to gate any features.
12
+ internal_test_only: false,
13
+
11
14
  promise_segments: false,
12
15
  reverse_naming_rules: false,
13
16
  undici_async_tracking: true,
@@ -21,6 +21,10 @@ module.exports = function initialize(_agent, redis, _moduleName, shim) {
21
21
  const client = original.apply(this, arguments)
22
22
  client[opts] = getRedisParams(client.options)
23
23
  CMDS_TO_INSTRUMENT.forEach(instrumentClientCommand.bind(null, shim, client))
24
+ if (client.options.legacyMode) {
25
+ client.v4[opts] = getRedisParams(client.options)
26
+ CMDS_TO_INSTRUMENT.forEach(instrumentClientCommand.bind(null, shim, client.v4))
27
+ }
24
28
  return client
25
29
  }
26
30
  })
@@ -266,18 +266,20 @@ function wrapModel(shim, Model, promiseMode) {
266
266
  destinationName: shim.FIRST,
267
267
  callback: setCallback(shim, promiseMode),
268
268
  promise: promiseMode,
269
- messageHandler: function handleConsumedMessage(shim, fn, name, message) {
269
+ after: function handleConsumedMessage({ shim, result, args, segment }) {
270
+ if (!shim.agent.config.message_tracer.segment_parameters.enabled) {
271
+ shim.logger.trace('Not capturing segment parameters')
272
+ return
273
+ }
274
+
270
275
  // the message is the param when using the promised based model
271
- message = promiseMode ? message : message[1]
276
+ const message = promiseMode ? result : args?.[1]
272
277
  if (!message) {
273
278
  shim.logger.trace('No results from consume.')
274
279
  return null
275
280
  }
276
281
  const parameters = getParametersFromMessage(message)
277
-
278
- const headers = message?.properties?.headers
279
-
280
- return { parameters, headers }
282
+ shim.copySegmentParameters(segment, parameters)
281
283
  }
282
284
  })
283
285
  )
@@ -312,12 +314,10 @@ function wrapModel(shim, Model, promiseMode) {
312
314
  * Extracts the appropriate messageHandler parameters for the consume method.
313
315
  *
314
316
  * @param {Shim} shim instance of shim
315
- * @param {object} _consumer not used
316
- * @param {string} _name not used
317
317
  * @param {Array} args arguments passed to the consume method
318
318
  * @returns {object} message params
319
319
  */
320
- function describeMessage(shim, _consumer, _name, args) {
320
+ function describeMessage(shim, args) {
321
321
  const [message] = args
322
322
 
323
323
  if (!message?.properties) {
@@ -197,8 +197,7 @@ function getBedrockSpec({ commandName }, shim, _original, _name, args) {
197
197
  return new RecorderSpec({
198
198
  promise: true,
199
199
  name: `Llm/${modelType}/Bedrock/${commandName}`,
200
- // eslint-disable-next-line max-params
201
- after: (shim, _fn, _fnName, err, response, segment) => {
200
+ after: ({ shim, error: err, result: response, segment }) => {
202
201
  const passThroughParams = {
203
202
  shim,
204
203
  err,
@@ -16,7 +16,7 @@ function initialize(agent, inspector, name, shim) {
16
16
  shim.wrap(sessionProto, 'post', function wrapPost(shim, fn) {
17
17
  return function wrappedPost() {
18
18
  const args = shim.argsToArray.apply(shim, arguments)
19
- shim.bindCallbackSegment(args, shim.LAST)
19
+ shim.bindCallbackSegment(null, args, shim.LAST)
20
20
  return fn.apply(this, args)
21
21
  }
22
22
  })
@@ -61,8 +61,7 @@ function instrumentInvokeChain({ langchain, shim }) {
61
61
  return new RecorderSpec({
62
62
  name: `${LANGCHAIN.CHAIN}/${fnName}`,
63
63
  promise: true,
64
- // eslint-disable-next-line max-params
65
- after(_shim, _fn, _name, err, output, segment) {
64
+ after({ error: err, result: output, segment }) {
66
65
  recordChatCompletionEvents({
67
66
  segment,
68
67
  messages: [output],
@@ -97,8 +96,7 @@ function instrumentStream({ langchain, shim }) {
97
96
  return new RecorderSpec({
98
97
  name: `${LANGCHAIN.CHAIN}/${fnName}`,
99
98
  promise: true,
100
- // eslint-disable-next-line max-params
101
- after(_shim, _fn, _name, err, output, segment) {
99
+ after({ error: err, result: output, segment }) {
102
100
  // Input error occurred which means a stream was not created.
103
101
  // Skip instrumenting streaming and create Llm Events from
104
102
  // the data we have
@@ -31,8 +31,7 @@ module.exports = function initialize(shim, tools) {
31
31
  return new RecorderSpec({
32
32
  name: `${LANGCHAIN.TOOL}/${name}`,
33
33
  promise: true,
34
- // eslint-disable-next-line max-params
35
- after(_shim, _fn, _name, err, output, segment) {
34
+ after({ error: err, result: output, segment }) {
36
35
  const metadata = mergeMetadata(instanceMeta, paramsMeta)
37
36
  const tags = mergeTags(instanceTags, paramsTags)
38
37
  segment.end()
@@ -89,8 +89,7 @@ module.exports = function initialize(shim, vectorstores) {
89
89
  return new RecorderSpec({
90
90
  name: `${LANGCHAIN.VECTORSTORE}/${fnName}`,
91
91
  promise: true,
92
- // eslint-disable-next-line max-params
93
- after(_shim, _fn, _name, err, output, segment) {
92
+ after({ error: err, result: output, segment }) {
94
93
  if (!output) {
95
94
  // If we get an error, it is possible that `output = null`.
96
95
  // In that case, we define it to be an empty array.
@@ -76,7 +76,7 @@ module.exports = function initialize(agent, memcached, moduleName, shim) {
76
76
  return new OperationSpec({
77
77
  name: metacall.type || 'Unknown',
78
78
  callback: function wrapCallback(shim, fn, fnName, opSegment) {
79
- shim.bindCallbackSegment(metacall, 'callback', opSegment)
79
+ shim.bindCallbackSegment(null, metacall, 'callback', opSegment)
80
80
  },
81
81
  parameters
82
82
  })
@@ -262,8 +262,7 @@ module.exports = function initialize(agent, openai, moduleName, shim) {
262
262
  return new RecorderSpec({
263
263
  name: OPENAI.COMPLETION,
264
264
  promise: true,
265
- // eslint-disable-next-line max-params
266
- after(_shim, _fn, _name, err, response, segment) {
265
+ after({ error: err, result: response, segment }) {
267
266
  if (request.stream) {
268
267
  instrumentStream({ agent, shim, request, response, segment })
269
268
  } else {
@@ -294,8 +293,7 @@ module.exports = function initialize(agent, openai, moduleName, shim) {
294
293
  return new RecorderSpec({
295
294
  name: OPENAI.EMBEDDING,
296
295
  promise: true,
297
- // eslint-disable-next-line max-params
298
- after(_shim, _fn, _name, err, response, segment) {
296
+ after({ error: err, result: response, segment }) {
299
297
  addLlmMeta({ agent, segment })
300
298
 
301
299
  if (!response) {
@@ -49,7 +49,7 @@ function registerInternalSendCommand(shim, proto) {
49
49
  parameters,
50
50
  callback: function bindCallback(shim, _f, _n, segment) {
51
51
  if (shim.isFunction(commandObject.callback)) {
52
- shim.bindCallbackSegment(commandObject, 'callback', segment)
52
+ shim.bindCallbackSegment(null, commandObject, 'callback', segment)
53
53
  } else {
54
54
  const self = this
55
55
  commandObject.callback = shim.bindSegment(
@@ -87,9 +87,9 @@ function registerSendCommand(shim, proto) {
87
87
  callback: function bindCallback(shim, _f, _n, segment) {
88
88
  const last = args[args.length - 1]
89
89
  if (shim.isFunction(last)) {
90
- shim.bindCallbackSegment(args, shim.LAST, segment)
90
+ shim.bindCallbackSegment(null, args, shim.LAST, segment)
91
91
  } else if (shim.isArray(last) && shim.isFunction(last[last.length - 1])) {
92
- shim.bindCallbackSegment(last, shim.LAST, segment)
92
+ shim.bindCallbackSegment(null, last, shim.LAST, segment)
93
93
  }
94
94
  }
95
95
  })
@@ -50,7 +50,7 @@ function wrapCallback(shim, callback) {
50
50
  return function wrappedCallback() {
51
51
  const segment = shim.getSegment(this)
52
52
  if (segment && segment.transaction.isActive()) {
53
- shim.bindCallbackSegment(this, '_callback', segment)
53
+ shim.bindCallbackSegment(null, this, '_callback', segment)
54
54
  }
55
55
  return callback.apply(this, arguments)
56
56
  }
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  'use strict'
7
- const TraceSegment = require('../../transaction/trace/segment')
8
7
  const genericRecorder = require('../../metrics/recorders/generic')
9
8
  const { _nameMessageSegment } = require('./common')
10
9
  const specs = require('../specs')
@@ -25,7 +24,7 @@ module.exports = createRecorder
25
24
  function updateSpecFromArgs({ shim, fn, fnName, args, spec }) {
26
25
  let msgDesc = null
27
26
  if (shim.isFunction(spec)) {
28
- msgDesc = spec.call(this, shim, fn, fnName, args)
27
+ msgDesc = spec(shim, fn, fnName, args)
29
28
  } else {
30
29
  msgDesc = spec
31
30
  const destIdx = shim.normalizeIndex(args.length, spec.destinationName)
@@ -37,81 +36,6 @@ function updateSpecFromArgs({ shim, fn, fnName, args, spec }) {
37
36
  return msgDesc
38
37
  }
39
38
 
40
- /**
41
- * Binds the consumer callback to the active segment.
42
- *
43
- * @private
44
- * @param {object} params to function
45
- * @param {MessageShim} params.shim instance of shim
46
- * @param {Array} params.args arguments passed to original consume function
47
- * @param {specs.MessageSpec} params.msgDesc spec for the wrapped consume function
48
- * @param {TraceSegment} params.segment active segment to bind callback
49
- * @param {boolean} params.getParams flag to copy message parameters to segment
50
- * @param {Function} params.resHandler function to handle response from callback to obtain the message parameters
51
- */
52
- function bindCallback({ shim, args, msgDesc, segment, getParams, resHandler }) {
53
- const cbIdx = shim.normalizeIndex(args.length, msgDesc.callback)
54
- if (cbIdx !== null) {
55
- shim.bindCallbackSegment(args, cbIdx, segment)
56
-
57
- // If we have a callback and a results handler, then wrap the callback so
58
- // we can call the results handler and get the message properties.
59
- if (resHandler) {
60
- shim.wrap(args, cbIdx, function wrapCb(shim, cb, cbName) {
61
- if (shim.isFunction(cb)) {
62
- return function cbWrapper() {
63
- const cbArgs = shim.argsToArray.apply(shim, arguments)
64
- const msgProps = resHandler.call(this, shim, cb, cbName, cbArgs)
65
- if (getParams && msgProps && msgProps.parameters) {
66
- shim.copySegmentParameters(segment, msgProps.parameters)
67
- }
68
-
69
- return cb.apply(this, arguments)
70
- }
71
- }
72
- })
73
- }
74
- }
75
- }
76
-
77
- /**
78
- * Binds the consumer function to the async context and checks return to possibly
79
- * bind the promise
80
- *
81
- * @private
82
- * @param {object} params to function
83
- * @param {MessageShim} params.shim instance of shim
84
- * @param {Function} params.fn consumer function
85
- * @param {string} params.fnName name of function
86
- * @param {Array} params.args arguments passed to original consume function
87
- * @param {specs.MessageSpec} params.msgDesc spec for the wrapped consume function
88
- * @param {TraceSegment} params.segment active segment to bind callback
89
- * @param {boolean} params.getParams flag to copy message parameters to segment
90
- * @param {Function} params.resHandler function to handle response from callback to obtain the message parameters
91
- * @returns {Promise|*} response from consume function
92
- */
93
- function bindConsumer({ shim, fn, fnName, args, msgDesc, segment, getParams, resHandler }) {
94
- // Call the method in the context of our segment.
95
- let ret = shim.applySegment(fn, segment, true, this, args)
96
-
97
- if (ret && msgDesc.promise && shim.isPromise(ret)) {
98
- ret = shim.bindPromise(ret, segment)
99
-
100
- // Intercept the promise to handle the result.
101
- if (resHandler) {
102
- ret = ret.then(function interceptValue(res) {
103
- const msgProps = resHandler.call(this, shim, fn, fnName, res)
104
- if (getParams && msgProps && msgProps.parameters) {
105
- shim.copySegmentParameters(segment, msgProps.parameters)
106
- }
107
- return res
108
- })
109
- }
110
- }
111
-
112
- return ret
113
- }
114
-
115
39
  /**
116
40
  *
117
41
  * @private
@@ -119,48 +43,14 @@ function bindConsumer({ shim, fn, fnName, args, msgDesc, segment, getParams, res
119
43
  * @param {MessageShim} params.shim instance of shim
120
44
  * @param {Function} params.fn function that is being wrapped
121
45
  * @param {string} params.fnName name of function
46
+ * @param params.args
122
47
  * @param {specs.MessageSpec} params.spec spec for the wrapped consume function
123
- * @returns {Function} recorder for consume function
48
+ * @returns {specs.MessageSpec} updated spec with logic to name segment and apply the genericRecorder
124
49
  */
125
- function createRecorder({ shim, fn, fnName, spec }) {
126
- return function consumeRecorder() {
127
- const parent = shim.getSegment()
128
- if (!parent || !parent.transaction.isActive()) {
129
- shim.logger.trace('Not recording consume, no active transaction.')
130
- return fn.apply(this, arguments)
131
- }
132
-
133
- // Process the message args.
134
- const args = shim.argsToArray.apply(shim, arguments)
135
- const msgDesc = updateSpecFromArgs.call(this, { shim, fn, fnName, args, spec })
136
-
137
- // Make the segment if we can.
138
- if (!msgDesc) {
139
- shim.logger.trace('Not recording consume, no message descriptor.')
140
- return fn.apply(this, args)
141
- }
142
-
143
- const name = _nameMessageSegment(shim, msgDesc, shim._metrics.CONSUME)
144
-
145
- // Adds details needed by createSegment when used with a spec
146
- msgDesc.name = name
147
- msgDesc.recorder = genericRecorder
148
- msgDesc.parent = parent
149
-
150
- const segment = shim.createSegment(msgDesc)
151
- const getParams = shim.agent.config.message_tracer.segment_parameters.enabled
152
- const resHandler = shim.isFunction(msgDesc.messageHandler) ? msgDesc.messageHandler : null
153
-
154
- bindCallback({ shim, args, msgDesc, segment, getParams, resHandler })
155
- return bindConsumer.call(this, {
156
- shim,
157
- fn,
158
- fnName,
159
- args,
160
- msgDesc,
161
- segment,
162
- getParams,
163
- resHandler
164
- })
165
- }
50
+ function createRecorder({ spec, shim, fn, fnName, args }) {
51
+ const msgDesc = updateSpecFromArgs({ shim, fn, fnName, args, spec })
52
+ // Adds details needed by createSegment when used with a spec
53
+ msgDesc.name = _nameMessageSegment(shim, msgDesc, shim._metrics.CONSUME)
54
+ msgDesc.recorder = genericRecorder
55
+ return msgDesc
166
56
  }
@@ -287,17 +287,8 @@ function recordConsume(nodule, properties, spec) {
287
287
  properties = null
288
288
  }
289
289
 
290
- // This is using wrap instead of record because the spec allows for a messageHandler
291
- // which is being used to handle the result of the callback or promise of the
292
- // original wrapped consume function.
293
- // TODO: https://github.com/newrelic/node-newrelic/issues/981
294
- return this.wrap(nodule, properties, function wrapConsume(shim, fn, fnName) {
295
- if (!shim.isFunction(fn)) {
296
- shim.logger.debug('Not wrapping %s (%s) as consume', fn, fnName)
297
- return fn
298
- }
299
-
300
- return createRecorder({ shim, fn, fnName, spec })
290
+ return this.record(nodule, properties, function wrapConsume(shim, fn, fnName, args) {
291
+ return createRecorder({ spec, shim, fn, fnName, args })
301
292
  })
302
293
  }
303
294
 
@@ -84,12 +84,12 @@ function makeWrapConsumer({ spec, queue, destinationName, destNameIsArg }) {
84
84
  spec.queue = queue
85
85
  }
86
86
 
87
- return function wrapConsumer(shim, consumer, cName) {
87
+ return function wrapConsumer(shim, consumer) {
88
88
  if (!shim.isFunction(consumer)) {
89
89
  return consumer
90
90
  }
91
91
 
92
- const consumerWrapper = createConsumerWrapper({ shim, consumer, cName, spec })
92
+ const consumerWrapper = createConsumerWrapper({ shim, consumer, spec })
93
93
  return shim.bindCreateTransaction(
94
94
  consumerWrapper,
95
95
  new specs.TransactionSpec({
@@ -108,10 +108,9 @@ function makeWrapConsumer({ spec, queue, destinationName, destNameIsArg }) {
108
108
  * @param {MessageShim} params.shim instance of shim
109
109
  * @param {specs.MessageSubscribeSpec} params.spec spec for function
110
110
  * @param {Function} params.consumer function for consuming message
111
- * @param {string} params.cName name of consumer function
112
111
  * @returns {Function} handler for the transaction being created
113
112
  */
114
- function createConsumerWrapper({ shim, spec, consumer, cName }) {
113
+ function createConsumerWrapper({ shim, spec, consumer }) {
115
114
  return function createConsumeTrans() {
116
115
  // If there is no transaction or we're in a pre-existing transaction,
117
116
  // then don't do anything. Note that the latter should never happen.
@@ -123,7 +122,7 @@ function createConsumerWrapper({ shim, spec, consumer, cName }) {
123
122
  return consumer.apply(this, args)
124
123
  }
125
124
 
126
- const msgDesc = spec.messageHandler.call(this, shim, consumer, cName, args)
125
+ const msgDesc = spec.messageHandler.call(this, shim, args)
127
126
 
128
127
  // If message could not be handled, immediately kill this transaction.
129
128
  if (!msgDesc) {
package/lib/shim/shim.js CHANGED
@@ -799,12 +799,13 @@ function _applyRecorderSegment({ segment, ctx, args, segDesc, shim, fn, name })
799
799
  return ret.then(
800
800
  function onThen(val) {
801
801
  segment.touch()
802
- segDesc.after(shim, fn, name, null, val, segment)
802
+ // passing in error as some instrumentation checks if it's not equal to `null`
803
+ segDesc.after({ shim, fn, name, error, result: val, segment })
803
804
  return val
804
805
  },
805
806
  function onCatch(err) {
806
807
  segment.touch()
807
- segDesc.after(shim, fn, name, err, null, segment)
808
+ segDesc.after({ shim, fn, name, error: err, segment })
808
809
  throw err // NOTE: This is not an error from our instrumentation.
809
810
  }
810
811
  )
@@ -815,7 +816,7 @@ function _applyRecorderSegment({ segment, ctx, args, segDesc, shim, fn, name })
815
816
  throw err // Just rethrowing this error, not our error!
816
817
  } finally {
817
818
  if (segDesc.after && (error || !promised)) {
818
- segDesc.after(shim, fn, name, error, ret, segment)
819
+ segDesc.after({ shim, fn, name, error, result: ret, segment })
819
820
  }
820
821
  }
821
822
  }
@@ -977,10 +978,11 @@ function bindSegment(nodule, property, segment, full) {
977
978
  * Replaces the callback in an arguments array with one that has been bound to
978
979
  * the given segment.
979
980
  *
980
- * - `bindCallbackSegment(args, cbIdx [, segment])`
981
- * - `bindCallbackSegment(obj, property [, segment])`
981
+ * - `bindCallbackSegment(spec, args, cbIdx [, segment])`
982
+ * - `bindCallbackSegment(spec, obj, property [, segment])`
982
983
  *
983
984
  * @memberof Shim.prototype
985
+ * @param {Spec} spec spec to original wrapped function, used to call after method with arguments passed to callback
984
986
  * @param {Array | object} args
985
987
  * The arguments array to pull the cb from.
986
988
  * @param {number|string} cbIdx
@@ -990,7 +992,7 @@ function bindSegment(nodule, property, segment, full) {
990
992
  * currently active segment.
991
993
  * @see Shim#bindSegment
992
994
  */
993
- function bindCallbackSegment(args, cbIdx, parentSegment) {
995
+ function bindCallbackSegment(spec, args, cbIdx, parentSegment) {
994
996
  if (!args) {
995
997
  return
996
998
  }
@@ -1009,35 +1011,59 @@ function bindCallbackSegment(args, cbIdx, parentSegment) {
1009
1011
  cbIdx = normalizedCBIdx
1010
1012
  }
1011
1013
 
1012
- // Pull out the callback and make sure it is a function.
1014
+ // Make sure cb is function before wrapping
1015
+ if (this.isFunction(args[cbIdx])) {
1016
+ wrapCallback({ shim: this, args, cbIdx, parentSegment, spec })
1017
+ }
1018
+ }
1019
+
1020
+ /**
1021
+ * Wraps the callback and creates a segment for the callback function.
1022
+ * It will also call an after hook with the arguments passed to callback
1023
+ *
1024
+ * @private
1025
+ * @param {Object} params to function
1026
+ * @param {Shim} params.shim instance of shim
1027
+ * @param {Array | object} params.args
1028
+ * The arguments array to pull the cb from.
1029
+ * @param {number|string} params.cbIdx
1030
+ * The index of the callback.
1031
+ * @param {TraceSegment} [params.parentSegment]
1032
+ * The segment to use as the callback segment's parent. Defaults to the
1033
+ * currently active segment.
1034
+ * @param {Spec} params.spec spec to original wrapped function, used to call after method with arguments passed to callback
1035
+ *
1036
+ */
1037
+ function wrapCallback({ shim, args, cbIdx, parentSegment, spec }) {
1013
1038
  const cb = args[cbIdx]
1014
- if (this.isFunction(cb)) {
1015
- const shim = this
1016
- const realParent = parentSegment || shim.getSegment()
1017
- args[cbIdx] = shim.wrap(cb, null, function callbackWrapper(shim, fn, name) {
1018
- return function wrappedCallback() {
1019
- if (realParent) {
1020
- realParent.opaque = false
1021
- }
1022
- const segment = _rawCreateSegment(
1023
- shim,
1024
- new specs.SegmentSpec({
1025
- name: 'Callback: ' + name,
1026
- parent: realParent
1027
- })
1028
- )
1029
-
1030
- if (segment) {
1031
- segment.async = false
1032
- }
1039
+ const realParent = parentSegment || shim.getSegment()
1040
+ args[cbIdx] = shim.wrap(cb, null, function callbackWrapper(shim, fn, name) {
1041
+ return function wrappedCallback() {
1042
+ if (realParent) {
1043
+ realParent.opaque = false
1044
+ }
1045
+ const segment = _rawCreateSegment(
1046
+ shim,
1047
+ new specs.SegmentSpec({
1048
+ name: 'Callback: ' + name,
1049
+ parent: realParent
1050
+ })
1051
+ )
1033
1052
 
1034
- // CB may end the transaction so update the parent's time preemptively.
1035
- realParent && realParent.touch()
1036
- return shim.applySegment(cb, segment, true, this, arguments)
1053
+ if (segment) {
1054
+ segment.async = false
1037
1055
  }
1038
- })
1039
- shim.storeSegment(args[cbIdx], realParent)
1040
- }
1056
+
1057
+ if (spec?.after) {
1058
+ spec.after({ shim, fn, name, args: arguments, segment: realParent })
1059
+ }
1060
+
1061
+ // CB may end the transaction so update the parent's time preemptively.
1062
+ realParent && realParent.touch()
1063
+ return shim.applySegment(cb, segment, true, this, arguments)
1064
+ }
1065
+ })
1066
+ shim.storeSegment(args[cbIdx], realParent)
1041
1067
  }
1042
1068
 
1043
1069
  /**
@@ -1792,7 +1818,7 @@ function _bindAllCallbacks(shim, fn, name, args, spec) {
1792
1818
  _bindCallback({
1793
1819
  context: this,
1794
1820
  callback: spec.spec.callback,
1795
- binder: shim.bindCallbackSegment,
1821
+ binder: shim.bindCallbackSegment.bind(shim, spec.spec),
1796
1822
  shim,
1797
1823
  fn,
1798
1824
  args,
@@ -1806,7 +1832,7 @@ function _bindAllCallbacks(shim, fn, name, args, spec) {
1806
1832
  _bindCallback({
1807
1833
  context: this,
1808
1834
  callback: spec.spec.rowCallback,
1809
- binder: shim.bindRowCallbackSegment || shim.bindCallbackSegment,
1835
+ binder: shim?.bindRowCallbackSegment || shim?.bindCallbackSegment?.bind(shim, spec.spec),
1810
1836
  shim,
1811
1837
  fn,
1812
1838
  args,
@@ -1954,7 +1980,7 @@ function wrapStreamListeners({ stream, shim, segment, specEvent }) {
1954
1980
  return function wrappedOn(onEvent) {
1955
1981
  if (onEvent !== specEvent && (onEvent === 'end' || onEvent === 'error')) {
1956
1982
  const args = argsToArray.apply(shim, arguments)
1957
- shim.bindCallbackSegment(args, shim.LAST, segment)
1983
+ shim.bindCallbackSegment(specEvent, args, shim.LAST, segment)
1958
1984
  return fn.apply(this, args)
1959
1985
  }
1960
1986
  return fn.apply(this, arguments)
@@ -24,14 +24,17 @@ const WrapSpec = require('./wrap')
24
24
  * The instrumented function must have been invoked synchronously.
25
25
  *
26
26
  * @typedef {Function} SpecAfterFunction
27
- * @param {object} shim The shim used to instrument the external library.
28
- * @param {Function} fn The function/method from the external library being
27
+ * @param {Object} params params to function
28
+ * @param {object} params.shim The shim used to instrument the external library.
29
+ * @param {Function} params.fn The function/method from the external library being
29
30
  * instrumented.
30
- * @param {string} name The name of the current function.
31
- * @param {Error|null} error If the instrumented function threw an error, this
32
- * will be that error.
33
- * @param {*} value The result returned by the instrumented function.
34
- * @param {TraceSegment} segment The segment used while instrumenting the
31
+ * @param {string} params.name The name of the current function.
32
+ * @param {Error|null} [params.error] If the instrumented function threw an error, this
33
+ * will be that error. In the case of a callback this will be omitted.
34
+ * @param {Array} [params.args] arguments passed to a callback function getting instrumented.
35
+ * @param {*} [params.result] The result returned by the instrumented function. In the case of a wrapped
36
+ * callback, the data needed is in `params.args`.
37
+ * @param {TraceSegment} params.segment The segment used while instrumenting the
35
38
  * function.
36
39
  */
37
40
 
@@ -176,10 +176,10 @@ function middlewareWithCallbackRecorder({ spec, typeDetails, metricName, isError
176
176
  parent: txInfo.segmentStack[txInfo.segmentStack.length - 1],
177
177
  recorder,
178
178
  parameters: params,
179
- after: function afterExec(shim, _fn, _name, err) {
180
- const errIsError = isError(shim, err)
179
+ after: function afterExec({ shim, error }) {
180
+ const errIsError = isError(shim, error)
181
181
  if (errIsError) {
182
- assignError(txInfo, err)
182
+ assignError(txInfo, error)
183
183
  } else if (!nextWrapper && !isErrorWare && spec.appendPath) {
184
184
  txInfo.transaction.nameState.popPath(route)
185
185
  }
@@ -243,12 +243,12 @@ function middlewareWithPromiseRecorder({ spec, typeDetails, metricName, isErrorW
243
243
  callback: nextWrapper,
244
244
  recorder,
245
245
  parameters: params,
246
- after: function afterExec(shim, _fn, _name, err, result) {
246
+ after: function afterExec({ shim, error, result }) {
247
247
  if (shim._responsePredicate(args, result)) {
248
248
  txInfo.transaction.nameState.freeze()
249
249
  }
250
- if (isError(shim, err)) {
251
- assignError(txInfo, err)
250
+ if (isError(shim, error)) {
251
+ assignError(txInfo, error)
252
252
  } else {
253
253
  txInfo.errorHandled = true
254
254
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "11.17.0",
3
+ "version": "11.18.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -161,7 +161,6 @@
161
161
  "prepare-test": "npm run ssl && npm run docker-env",
162
162
  "lint": "eslint ./*.{js,mjs} lib test bin examples",
163
163
  "lint:fix": "eslint --fix, ./*.{js,mjs} lib test bin examples",
164
- "lint:lockfile": "lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https --validate-integrity",
165
164
  "public-docs": "jsdoc -c ./jsdoc-conf.jsonc && cp examples/shim/*.png out/",
166
165
  "publish-docs": "./bin/publish-docs.sh",
167
166
  "services": "docker compose up -d --wait",