newrelic 13.9.2 → 13.11.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.
Files changed (49) hide show
  1. package/NEWS.md +66 -0
  2. package/lib/instrumentation/aws-sdk/v3/bedrock.js +2 -1
  3. package/lib/instrumentation/when/index.js +0 -1
  4. package/lib/llm-events/aws-bedrock/chat-completion-message.js +5 -1
  5. package/lib/llm-events/google-genai/chat-completion-message.js +5 -0
  6. package/lib/llm-events/langchain/chat-completion-message.js +16 -10
  7. package/lib/llm-events/openai/chat-completion-message.js +6 -1
  8. package/lib/llm-events/openai/chat-completion-summary.js +4 -2
  9. package/lib/metrics/names.js +5 -2
  10. package/lib/otel/setup.js +14 -10
  11. package/lib/otel/traces/span-processor.js +9 -2
  12. package/lib/shimmer.js +1 -1
  13. package/lib/spans/span-event.js +2 -0
  14. package/lib/subscribers/ai-monitoring/base.js +113 -0
  15. package/lib/subscribers/ai-monitoring/chat.js +117 -0
  16. package/lib/subscribers/ai-monitoring/embedding.js +72 -0
  17. package/lib/subscribers/ai-monitoring/index.js +14 -0
  18. package/lib/subscribers/base.js +32 -14
  19. package/lib/subscribers/dc-base.js +47 -6
  20. package/lib/subscribers/fastify/index.js +5 -2
  21. package/lib/subscribers/google-genai/embed-content.js +18 -44
  22. package/lib/subscribers/google-genai/generate-content-stream.js +16 -39
  23. package/lib/subscribers/google-genai/generate-content.js +40 -29
  24. package/lib/subscribers/ioredis/index.js +2 -12
  25. package/lib/subscribers/langchain/runnable-stream.js +33 -66
  26. package/lib/subscribers/langchain/runnable.js +73 -32
  27. package/lib/subscribers/langchain/tool-callback-manager.js +4 -3
  28. package/lib/subscribers/langchain/tool.js +9 -19
  29. package/lib/subscribers/langchain/vectorstore.js +6 -26
  30. package/lib/subscribers/mcp-sdk/client-request.js +6 -12
  31. package/lib/subscribers/middleware-wrapper.js +5 -1
  32. package/lib/subscribers/openai/chat.js +204 -44
  33. package/lib/subscribers/openai/client.js +20 -7
  34. package/lib/subscribers/openai/embeddings.js +22 -34
  35. package/lib/subscribers/pg/connect.js +6 -1
  36. package/lib/subscribers/pg/native-connect.js +1 -1
  37. package/lib/subscribers/pg/native-query.js +1 -1
  38. package/lib/subscribers/pg/query.js +6 -1
  39. package/lib/subscribers/record-supportability-metric.js +39 -0
  40. package/lib/subscribers/resolve-package-version.js +84 -0
  41. package/lib/subscribers/utils.js +38 -0
  42. package/lib/tracking-packages.js +1 -9
  43. package/lib/transaction/trace/partial-trace.js +62 -4
  44. package/lib/transaction/trace/segment.js +19 -1
  45. package/package.json +1 -1
  46. package/lib/subscribers/google-genai/base.js +0 -123
  47. package/lib/subscribers/langchain/base.js +0 -164
  48. package/lib/subscribers/openai/base.js +0 -46
  49. package/lib/subscribers/openai/utils.js +0 -358
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ const AiMonitoringChatSubscriber = require('./chat')
7
+ const AiMonitoringEmbeddingSubscriber = require('./embedding')
8
+ const AiMonitoringSubscriber = require('./base')
9
+
10
+ module.exports = {
11
+ AiMonitoringSubscriber,
12
+ AiMonitoringChatSubscriber,
13
+ AiMonitoringEmbeddingSubscriber
14
+ }
@@ -7,6 +7,8 @@
7
7
  // eslint-disable-next-line n/no-unsupported-features/node-builtins
8
8
  const { tracingChannel } = require('node:diagnostics_channel')
9
9
  const cat = require('#agentlib/util/cat.js')
10
+ const recordSupportabilityMetric = require('./record-supportability-metric.js')
11
+
10
12
  // Used for the `traceCallback` work.
11
13
  // This can be removed when we add true support into orchestrion
12
14
  const makeCall = (fn) => (...args) => fn.call(...args)
@@ -28,38 +30,45 @@ const ArrayPrototypeSplice = makeCall(Array.prototype.splice)
28
30
 
29
31
  /**
30
32
  * @property {object} agent A New Relic Node.js agent instance.
31
- * @property {object} logger An agent logger instance.
33
+ * @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
34
+ * @property {string} channelName A unique name for the diagnostics channel
35
+ * that will be registered.
32
36
  * @property {object} config The agent configuration object.
37
+ * @property {string} id A unique identifier for the subscriber, combining the prefix, package
38
+ * name, and channel name.
39
+ * @property {object} logger An agent logger instance.
33
40
  * @property {string} packageName The name of the module being instrumented.
34
41
  * This is the same string one would pass to the `require` function.
35
- * @property {string} channelName A unique name for the diagnostics channel
36
- * that will be registered.
42
+ * @property {AsyncLocalStorage} store The async local storage instance used for context management.
43
+ * @property {number} [callback=null] Position of callback if it needs to be wrapped for instrumentation.
44
+ * -1 means last argument.
37
45
  * @property {string[]} [events=[]] Set of tracing channel event names to
38
46
  * register handlers for. For any name in the set, a corresponding method
39
47
  * must exist on the subscriber instance. The method will be passed the
40
48
  * event object. Possible event names are `start`, `end`, `asyncStart`,
41
49
  * `asyncEnd`, and `error`.
42
- *
43
- * See {@link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel}
44
- * @property {boolean} [opaque=false] If true, any children segments will not be created.
50
+ * See {@link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel}
45
51
  * @property {boolean} [internal=false] If true, any children segments from the same library
46
52
  * will not be created.
53
+ * @property {boolean} [opaque=false] If true, any children segments will not be created.
47
54
  * @property {string} [prefix='orchestrion:'] String to prepend to diagnostics
48
55
  * channel event names. This provides a namespace for the events we are
49
56
  * injecting into a module.
50
- * @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events
51
- * when there is an active transaction.
52
57
  * @property {boolean} [propagateContext=false] If true, it will bind `asyncStart` to the store
53
58
  * and re-propagate the active context. It will also attach the `transaction` to the event in
54
59
  * `start.bindStore`. This is used for functions that queue async code and context is lost.
55
- * @property {string} id A unique identifier for the subscriber, combining the prefix, package
56
- * name, and channel name.
57
- * @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
58
- * @property {AsyncLocalStorage} store The async local storage instance used for context management.
59
- * @property {number} [callback=null] Position of callback if it needs to be wrapped for instrumentation.
60
- * -1 means last argument.
60
+ * @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events
61
+ * when there is an active transaction.
62
+ * @property {object} [targetModuleMeta] Defines the target module's name and
63
+ * version string, i.e. is an object `{ name, version }`. This is only necessary
64
+ * when target instrumentation can surface an unexpected name for the
65
+ * `packageName` property. For example, `express` uses multiple modules to
66
+ * compose its core functionality. We want to track things under the `express`
67
+ * name, but `packageName` will be set to `router` is most cases.
61
68
  */
62
69
  class Subscriber {
70
+ #usageMetricRecorded = false
71
+
63
72
  /**
64
73
  * @param {SubscriberParams} params the subscriber constructor params
65
74
  */
@@ -234,6 +243,15 @@ class Subscriber {
234
243
  * @returns {Context} The context after processing the event
235
244
  */
236
245
  const handler = (data) => {
246
+ if (this.#usageMetricRecorded === false) {
247
+ recordSupportabilityMetric({
248
+ agent: this.agent,
249
+ moduleName: this.packageName,
250
+ moduleVersion: data.moduleVersion
251
+ })
252
+ this.#usageMetricRecorded = true
253
+ }
254
+
237
255
  // only wrap the callback if a subscriber has a callback property defined
238
256
  if (this.callback !== null) {
239
257
  this.traceCallback(this.callback, data)
@@ -4,8 +4,11 @@
4
4
  */
5
5
 
6
6
  'use strict'
7
+
7
8
  // eslint-disable-next-line n/no-unsupported-features/node-builtins
8
9
  const dc = require('node:diagnostics_channel')
10
+ const recordSupportabilityMetric = require('./record-supportability-metric.js')
11
+ const resolvePackageVersion = require('./resolve-package-version.js')
9
12
 
10
13
  /**
11
14
  * The baseline parameters available to all subscribers.
@@ -15,6 +18,13 @@ const dc = require('node:diagnostics_channel')
15
18
  * @property {object} logger An agent logger instance.
16
19
  * @property {string} packageName The package name being instrumented.
17
20
  * This is what a developer would provide to the `require` function.
21
+ * @property {boolean} [skipUsageMetricRecording=false] When set to `true`, the
22
+ * instrumentation will not attempt to record the usage metric. This is useful
23
+ * when the module being instrumented is also being instrumented via the
24
+ * Orchestrion based subscriber system. It is much cheaper to record the metric
25
+ * via Orchestrion based subscribers than through this direct diagnostics
26
+ * channel method (Orchestrion provides the module version, whereas we have
27
+ * to perform expensive operations here to get the same information).
18
28
  */
19
29
 
20
30
  /**
@@ -34,19 +44,25 @@ const dc = require('node:diagnostics_channel')
34
44
  * This is the same string one would pass to the `require` function.
35
45
  */
36
46
  class Subscriber {
47
+ #usageMetricRecorded = false
48
+
37
49
  /**
38
50
  * @param {SubscriberParams} params to function
39
51
  */
40
- constructor({ agent, logger, packageName }) {
52
+ constructor({ agent, logger, packageName, skipUsageMetricRecording = false }) {
41
53
  this.agent = agent
42
54
  this.logger = logger.child({ component: `${packageName}-subscriber` })
43
55
  this.config = agent.config
44
56
  this.id = packageName
57
+
58
+ if (skipUsageMetricRecording === true) {
59
+ this.#usageMetricRecorded = true
60
+ }
45
61
  }
46
62
 
47
63
  set channels(channels) {
48
64
  if (!Array.isArray(channels)) {
49
- throw new Error('channels must be a collection of with propertiesof channel and hook')
65
+ throw new Error('channels must be a collection of objects with properties channel and hook')
50
66
  }
51
67
  this._channels = channels
52
68
  }
@@ -73,18 +89,43 @@ class Subscriber {
73
89
 
74
90
  subscribe() {
75
91
  for (let index = 0; index < this.channels.length; index++) {
92
+ const chan = this.channels[index]
76
93
  const { hook, channel } = this.channels[index]
77
94
  const boundHook = hook.bind(this)
78
- dc.subscribe(channel, boundHook)
79
- this.channels[index].boundHook = boundHook
95
+ chan.boundHook = boundHook
96
+ chan.eventHandler = (message, name) => {
97
+ this.#supportability()
98
+ boundHook(message, name)
99
+ }
100
+ dc.subscribe(channel, chan.eventHandler)
80
101
  }
81
102
  }
82
103
 
83
104
  unsubscribe() {
84
105
  for (let index = 0; index < this.channels.length; index++) {
85
- const { channel, boundHook } = this.channels[index]
86
- dc.unsubscribe(channel, boundHook)
106
+ const { channel, eventHandler } = this.channels[index]
107
+ dc.unsubscribe(channel, eventHandler)
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Since this class subscribes to diagnostics channels natively published by
113
+ * target modules, we do not get the package metadata that Orchestrion
114
+ * provides in its channel events. So we have to try and find the package
115
+ * manifest and get the version out of it in order to record our
116
+ * supportability metric.
117
+ */
118
+ #supportability() {
119
+ if (this.#usageMetricRecorded === true) {
120
+ return
87
121
  }
122
+ const version = resolvePackageVersion(this.id)
123
+ recordSupportabilityMetric({
124
+ agent: this.agent,
125
+ moduleName: this.id,
126
+ moduleVersion: version
127
+ })
128
+ this.#usageMetricRecorded = true
88
129
  }
89
130
  }
90
131
 
@@ -10,7 +10,7 @@ const MiddlewareWrapper = require('../middleware-wrapper')
10
10
 
11
11
  class FastifyInitialization extends DcBase {
12
12
  constructor({ agent, logger }) {
13
- super({ agent, logger, packageName: 'fastify' })
13
+ super({ agent, logger, packageName: 'fastify', skipUsageMetricRecording: true })
14
14
  this.channels = [
15
15
  { channel: initChannel, hook: this.handler }
16
16
  ]
@@ -24,7 +24,10 @@ class FastifyInitialization extends DcBase {
24
24
  return
25
25
  }
26
26
 
27
- routeOptions.handler = self.wrapper.wrap({ handler: routeOptions.handler, route: routeOptions.path })
27
+ routeOptions.handler = self.wrapper.wrap({
28
+ handler: routeOptions.handler,
29
+ route: routeOptions.path
30
+ })
28
31
  })
29
32
  }
30
33
  }
@@ -3,67 +3,41 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- const GoogleGenAISubscriber = require('./base')
6
+ const { AiMonitoringEmbeddingSubscriber } = require('../ai-monitoring')
7
7
  const { AI } = require('../../../lib/metrics/names')
8
8
  const { GEMINI } = AI
9
- const {
10
- LlmErrorMessage,
11
- LlmEmbedding
12
- } = require('../../../lib/llm-events/google-genai')
9
+ const { LlmEmbedding } = require('#agentlib/llm-events/google-genai/index.js')
13
10
 
14
- class GoogleGenAIEmbedContentSubscriber extends GoogleGenAISubscriber {
11
+ class GoogleGenAIEmbedContentSubscriber extends AiMonitoringEmbeddingSubscriber {
15
12
  constructor ({ agent, logger }) {
16
- super({ agent, logger, channelName: 'nr_embedContent' })
13
+ super({ agent, logger, packageName: '@google/genai', channelName: 'nr_embedContent', trackingPrefix: GEMINI.TRACKING_PREFIX, name: GEMINI.EMBEDDING })
14
+ this.events = ['asyncEnd']
17
15
  }
18
16
 
19
- handler(data, ctx) {
20
- if (!this.enabled) {
21
- this.logger.debug('`ai_monitoring.enabled` is set to false, not creating segment.')
22
- return ctx
23
- }
24
- const segment = this.agent.tracer.createSegment({
25
- name: GEMINI.EMBEDDING,
26
- parent: ctx.segment,
27
- transaction: ctx.transaction
17
+ createEmbedding({ ctx, request, response = {}, err }) {
18
+ const { segment, transaction } = ctx
19
+ return new LlmEmbedding({
20
+ agent: this.agent,
21
+ segment,
22
+ transaction,
23
+ request,
24
+ response,
25
+ withError: !!err
28
26
  })
29
- return ctx.enterSegment({ segment })
30
27
  }
31
28
 
32
29
  asyncEnd(data) {
33
- const agent = this.agent
34
- if (!this.enabled) {
35
- this.logger.debug('`ai_monitoring.enabled` is set to false, not recording Llm events.')
36
- return
37
- }
38
- const ctx = agent.tracer.getContext()
39
- if (!ctx?.segment || !ctx?.transaction) {
40
- return
41
- }
42
- this.addLlmMeta({ transaction: ctx.transaction, version: data.moduleVersion })
30
+ const ctx = this.agent.tracer.getContext()
43
31
  // If we get an error, it is possible that `response = null`.
44
32
  // In that case, we define it to be an empty object.
45
33
  const { result: response = {}, arguments: args, error: err } = data
46
34
  const [request] = args
47
-
48
- // Explicitly end segment to get consistent duration
49
- // for both LLM events and the segment
50
- ctx.segment.end()
51
-
52
- const embedding = new LlmEmbedding({
53
- agent,
54
- segment: ctx.segment,
55
- transaction: ctx.transaction,
35
+ this.recordEmbedding({
36
+ ctx,
56
37
  request,
57
38
  response,
58
- withError: err != null
39
+ err
59
40
  })
60
-
61
- this.recordEvent({ type: 'LlmEmbedding', msg: embedding })
62
-
63
- if (err) {
64
- const llmError = new LlmErrorMessage({ cause: err, embedding, response })
65
- agent.errors.add(ctx.transaction, err, llmError)
66
- }
67
41
  }
68
42
  }
69
43
 
@@ -11,32 +11,19 @@ class GoogleGenAIGenerateContentStreamSubscriber extends GoogleGenAIGenerateCont
11
11
  super({ agent, logger, channelName: 'nr_generateContentStreamInternal' })
12
12
  }
13
13
 
14
- get enabled() {
15
- return super.enabled && this.streamingEnabled
16
- }
17
-
18
- get streamingEnabled() {
19
- return this.agent.config.ai_monitoring.streaming.enabled
20
- }
21
-
22
- handler(data, ctx) {
23
- if (!this.enabled) {
24
- this.logger.debug('`ai_monitoring.enabled` or `ai_monitoring.streaming.enabled` is set to false, not creating segment.')
25
- return ctx
26
- }
27
-
28
- return super.handler(data, ctx)
29
- }
30
-
31
14
  /**
32
15
  * Instruments the streaming response by wrapping the next function.
33
16
  * @param {object} params function params
34
17
  * @param {object} params.request the original request object
35
18
  * @param {object} params.response the original response object
36
- * @param {TraceSegment} params.segment the active trace segment
37
- * @param {Transaction} params.transaction the active transaction
19
+ * @param {object} params.ctx active context
38
20
  */
39
- instrumentStream({ request, response, segment, transaction }) {
21
+ instrumentStream({ request, response, ctx }) {
22
+ if (!(ctx?.segment || ctx?.transaction)) {
23
+ this.logger.debug('Empty context, not instrumenting stream')
24
+ return
25
+ }
26
+
40
27
  const self = this
41
28
  const originalNext = response.next
42
29
  let isDone = false
@@ -65,7 +52,7 @@ class GoogleGenAIGenerateContentStreamSubscriber extends GoogleGenAIGenerateCont
65
52
  } finally {
66
53
  // Update segment duration since we want to extend the
67
54
  // time it took to handle the stream
68
- segment.touch()
55
+ ctx.segment.touch()
69
56
 
70
57
  // also need to enter this block if there was an
71
58
  // error, so we can record it
@@ -73,9 +60,8 @@ class GoogleGenAIGenerateContentStreamSubscriber extends GoogleGenAIGenerateCont
73
60
  if (cachedResult?.candidates?.[0]?.content?.parts) {
74
61
  cachedResult.candidates[0].content.parts[0].text = entireMessage
75
62
  }
76
- self.recordChatCompletionMessages({
77
- segment,
78
- transaction,
63
+ self.recordChatCompletionEvents({
64
+ ctx,
79
65
  request,
80
66
  response: cachedResult,
81
67
  err
@@ -87,7 +73,11 @@ class GoogleGenAIGenerateContentStreamSubscriber extends GoogleGenAIGenerateCont
87
73
  }
88
74
 
89
75
  asyncEnd(data) {
90
- // Check config is ai_monitoring is still enabled
76
+ // Check config to see if ai_monitoring is still enabled
77
+ if (!this.enabled) {
78
+ this.logger.debug('`ai_monitoring.enabled` is set to false, stream will not be instrumented.')
79
+ return
80
+ }
91
81
  if (!this.streamingEnabled) {
92
82
  this.logger.warn(
93
83
  '`ai_monitoring.streaming.enabled` is set to `false`, stream will not be instrumented.'
@@ -95,29 +85,16 @@ class GoogleGenAIGenerateContentStreamSubscriber extends GoogleGenAIGenerateCont
95
85
  this.agent.metrics.getOrCreateMetric(AI.STREAMING_DISABLED).incrementCallCount()
96
86
  return
97
87
  }
98
- if (!this.enabled) {
99
- this.logger.debug('`ai_monitoring.enabled` is set to false, not recording Llm events.')
100
- return
101
- }
102
88
 
103
89
  // Instrument the stream
104
90
  const ctx = this.agent.tracer.getContext()
105
- if (!ctx?.segment || !ctx?.transaction) {
106
- return
107
- }
108
91
  const { result: response, arguments: args } = data
109
92
  const [request] = args
110
93
 
111
94
  this.instrumentStream({
95
+ ctx,
112
96
  request,
113
97
  response,
114
- segment: ctx.segment,
115
- transaction: ctx.transaction,
116
- })
117
-
118
- this.addLlmMeta({
119
- transaction: ctx.transaction,
120
- version: data.moduleVersion,
121
98
  })
122
99
  }
123
100
  }
@@ -3,51 +3,62 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- const GoogleGenAISubscriber = require('./base')
6
+ const { AiMonitoringChatSubscriber } = require('../ai-monitoring')
7
7
  const { AI } = require('../../metrics/names')
8
8
  const { GEMINI } = AI
9
+ const { LlmChatCompletionSummary, LlmChatCompletionMessage } = require('#agentlib/llm-events/google-genai/index.js')
9
10
 
10
- class GoogleGenAIGenerateContentSubscriber extends GoogleGenAISubscriber {
11
+ class GoogleGenAIGenerateContentSubscriber extends AiMonitoringChatSubscriber {
11
12
  constructor({ agent, logger, channelName = 'nr_generateContentInternal' }) {
12
- super({ agent, logger, channelName })
13
- }
14
-
15
- handler(data, ctx) {
16
- if (!this.enabled) {
17
- this.logger.debug('`ai_monitoring.enabled` is set to false, not creating segment.')
18
- return ctx
19
- }
20
- const segment = this.agent.tracer.createSegment({
21
- name: GEMINI.COMPLETION,
22
- parent: ctx.segment,
23
- transaction: ctx.transaction
24
- })
25
- return ctx.enterSegment({ segment })
13
+ super({ agent, logger, channelName, packageName: '@google/genai', name: GEMINI.COMPLETION, trackingPrefix: GEMINI.TRACKING_PREFIX })
14
+ this.events = ['asyncEnd']
26
15
  }
27
16
 
28
17
  asyncEnd(data) {
29
- if (!this.enabled) {
30
- this.logger.debug('`ai_monitoring.enabled` is set to false, not recording Llm events.')
31
- return
32
- }
33
18
  const ctx = this.agent.tracer.getContext()
34
- if (!ctx?.segment || !ctx?.transaction) {
35
- return
36
- }
37
19
  const { result: response, arguments: args, error: err } = data
38
20
  const [request] = args
39
21
 
40
- this.recordChatCompletionMessages({
41
- segment: ctx.segment,
42
- transaction: ctx.transaction,
22
+ this.recordChatCompletionEvents({
23
+ ctx,
43
24
  request,
44
25
  response,
45
26
  err
46
27
  })
28
+ }
47
29
 
48
- this.addLlmMeta({
49
- transaction: ctx.transaction,
50
- version: data.moduleVersion
30
+ getMessages({ request, response }) {
31
+ // Only take the first response message and append to input messages
32
+ // request.contents can be a string or an array of strings
33
+ // response.candidates is an array of candidates (choices); we only take the first one
34
+ const inputMessages = Array.isArray(request.contents) ? request.contents : [request.contents]
35
+ const responseMessage = response?.candidates?.[0]?.content
36
+ return responseMessage !== undefined ? [...inputMessages, responseMessage] : inputMessages
37
+ }
38
+
39
+ createCompletionSummary({ ctx, request, response = {}, err }) {
40
+ const { transaction, segment } = ctx
41
+ return new LlmChatCompletionSummary({
42
+ agent: this.agent,
43
+ segment,
44
+ transaction,
45
+ request,
46
+ response,
47
+ withError: !!err
48
+ })
49
+ }
50
+
51
+ createCompletionMessage({ ctx, request, response, index, completionId, message }) {
52
+ const { segment, transaction } = ctx
53
+ return new LlmChatCompletionMessage({
54
+ agent: this.agent,
55
+ segment,
56
+ transaction,
57
+ request,
58
+ response,
59
+ index,
60
+ completionId,
61
+ message
51
62
  })
52
63
  }
53
64
  }
@@ -6,6 +6,7 @@
6
6
  'use strict'
7
7
  const DbOperationSubscriber = require('../db-operation')
8
8
  const stringify = require('json-stringify-safe')
9
+ const { wrapPromise } = require('../utils')
9
10
 
10
11
  class IoRedisSubscriber extends DbOperationSubscriber {
11
12
  constructor({ agent, logger, packageName = 'ioredis', system = 'Redis' }) {
@@ -30,18 +31,7 @@ class IoRedisSubscriber extends DbOperationSubscriber {
30
31
  * @param {object} data the data associated with the `end` event
31
32
  */
32
33
  end(data) {
33
- const promise = data?.result
34
- if (typeof promise.then !== 'function') {
35
- return promise
36
- }
37
-
38
- promise.then((result) => {
39
- data.result = result
40
- this.channel.asyncEnd.publish(data)
41
- }, (err) => {
42
- data.error = err
43
- this.channel.asyncEnd.publish(data)
44
- })
34
+ wrapPromise.call(this, data)
45
35
  }
46
36
 
47
37
  setParameters(self, command) {