newrelic 13.17.0 → 13.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.
Files changed (38) hide show
  1. package/NEWS.md +62 -31
  2. package/lib/context-manager/context.js +14 -1
  3. package/lib/feature_flags.js +1 -1
  4. package/lib/instrumentations.js +1 -6
  5. package/lib/spans/span-link.js +4 -2
  6. package/lib/subscriber-configs.js +6 -1
  7. package/lib/subscribers/README.md +10 -0
  8. package/lib/subscribers/base.js +12 -8
  9. package/lib/subscribers/bluebird/config.js +21 -0
  10. package/lib/subscribers/bluebird/instrumentation.js +59 -0
  11. package/lib/subscribers/grpcjs/client.js +126 -0
  12. package/lib/subscribers/grpcjs/config.js +63 -0
  13. package/lib/subscribers/grpcjs/http2-stream.js +14 -0
  14. package/lib/subscribers/grpcjs/resolving-call.js +14 -0
  15. package/lib/subscribers/grpcjs/server.js +206 -0
  16. package/lib/subscribers/grpcjs/should-track-error.js +12 -0
  17. package/lib/subscribers/index.js +37 -0
  18. package/lib/subscribers/langchain/config.js +22 -2
  19. package/lib/subscribers/memcached/command.js +69 -0
  20. package/lib/subscribers/memcached/config.js +23 -0
  21. package/lib/subscribers/message-consumer.js +8 -6
  22. package/lib/subscribers/q/config.js +32 -0
  23. package/lib/subscribers/q/next-tick.js +21 -0
  24. package/lib/subscribers/q/run-after.js +14 -0
  25. package/lib/subscribers/when/config.js +21 -0
  26. package/lib/subscribers/when/instrumentation.js +33 -0
  27. package/lib/transaction/index.js +3 -1
  28. package/load-externals.js +13 -1
  29. package/package.json +1 -1
  30. package/lib/instrumentation/bluebird.js +0 -109
  31. package/lib/instrumentation/grpc-js/grpc.js +0 -271
  32. package/lib/instrumentation/grpc-js/nr-hooks.js +0 -32
  33. package/lib/instrumentation/memcached.js +0 -84
  34. package/lib/instrumentation/q.js +0 -29
  35. package/lib/instrumentation/when/constants.js +0 -66
  36. package/lib/instrumentation/when/contextualizer.js +0 -181
  37. package/lib/instrumentation/when/index.js +0 -363
  38. package/lib/instrumentation/when/nr-hooks.js +0 -16
package/NEWS.md CHANGED
@@ -1,34 +1,65 @@
1
- ### v13.17.0 (2026-03-19)
2
-
3
- #### Features
4
-
5
- * Added support for Continuous Profiling ([#3834](https://github.com/newrelic/node-newrelic/pull/3834)) ([6d8d5b5](https://github.com/newrelic/node-newrelic/commit/6d8d5b5df2f459ffca39cc14ef0cdabcf7f22cea))
6
- * Added server-side configuration for `profiling.enabled` ([#3790](https://github.com/newrelic/node-newrelic/pull/3790)) ([0d44e5b](https://github.com/newrelic/node-newrelic/commit/0d44e5b3b24b60d8b920f7d6a762f208fa4139ca)) ([#3847](https://github.com/newrelic/node-newrelic/pull/3847)) ([e57223c](https://github.com/newrelic/node-newrelic/commit/e57223c7c66ab7fe1f464725efe0474e23326159))
7
- * Added delay + duration to `ProfilingAggregator` ([#3815](https://github.com/newrelic/node-newrelic/pull/3815)) ([8000665](https://github.com/newrelic/node-newrelic/commit/8000665b1714275f8e019a2f7c911d62ee8dd127))
8
- * Added supportability metrics for profiling ([#3801](https://github.com/newrelic/node-newrelic/pull/3801)) ([19bbb7e](https://github.com/newrelic/node-newrelic/commit/19bbb7ef426e835a13ec38fc543637bdae57a5fc))
9
- * Prevented ProfilingAggregator from running in serverless mode ([#3806](https://github.com/newrelic/node-newrelic/pull/3806)) ([e4db0f4](https://github.com/newrelic/node-newrelic/commit/e4db0f469fde9ae6aa4984cb312ee1de269e5c7b))
10
- * Disabled `profiling.enabled` when high security mode is enabled ([#3803](https://github.com/newrelic/node-newrelic/pull/3803)) ([9d4eebb](https://github.com/newrelic/node-newrelic/commit/9d4eebb11df00fc5729ad178063712734449741a))
11
- * Added Heap profiler ([#3751](https://github.com/newrelic/node-newrelic/pull/3751)) ([8262f6b](https://github.com/newrelic/node-newrelic/commit/8262f6bb5d7bf0a1c996455d6829883a7ff3b696))
12
- * Added Cpu Profiler ([#3747](https://github.com/newrelic/node-newrelic/pull/3747)) ([2c2097f](https://github.com/newrelic/node-newrelic/commit/2c2097fdfd4ec924bf24901eecb8ad62a9e4c4f5))
13
-
14
- **Note**: Continuous Profiling UI is coming soon! While the Node.js agent now supports Continuous Profiling data collection, the visualization experience will debut in Limited Preview starting next week. If you would like to get early access to the profiling dashboards and UI features, please reach out to your Account Executive (AE) about how to opt-in to the preview.
15
-
16
- #### Code refactoring
17
-
18
- * Refactor `winston` instrumentation to subscribe to events emitted ([#3808](https://github.com/newrelic/node-newrelic/pull/3808)) ([4af2f48](https://github.com/newrelic/node-newrelic/commit/4af2f48d99b0842085044deb36d14af9e96ac8ed))
19
-
20
- #### Documentation
21
-
22
- * Updated compatibility report ([#3824](https://github.com/newrelic/node-newrelic/pull/3824)) ([0cfb356](https://github.com/newrelic/node-newrelic/commit/0cfb356c487fbe47c4ac11095704846d948168fb))
23
-
24
- #### Tests
25
-
26
- * Added segment duration tests for `ioredis`/`iovalkey` ([#3825](https://github.com/newrelic/node-newrelic/pull/3825)) ([9738bc8](https://github.com/newrelic/node-newrelic/commit/9738bc88b59c219d896d80e0f8ccb93866cd7f0b))
27
- * Added segment duration timing tests for `mysql2` ([#3820](https://github.com/newrelic/node-newrelic/pull/3820)) ([bb424ff](https://github.com/newrelic/node-newrelic/commit/bb424ffa59fe55912f1ad711d84b57518dbd778f))
28
- * Changed structure of bedrock tests to avoid getting instrumented core libraries from first test ([#3838](https://github.com/newrelic/node-newrelic/pull/3838)) ([886a36c](https://github.com/newrelic/node-newrelic/commit/886a36c1607e1a7c0ddc0688f7b8ee62f9a8bf10))
29
- * Changed structure of lambda and sns tests to avoid getting instrumented core libraries from first test ([#3836](https://github.com/newrelic/node-newrelic/pull/3836)) ([414044d](https://github.com/newrelic/node-newrelic/commit/414044d184be885843a3267097c87954daf408bd))
30
- * Updated `@aws-sdk/lib-dynamodb` version range ([#3829](https://github.com/newrelic/node-newrelic/pull/3829)) ([d28c938](https://github.com/newrelic/node-newrelic/commit/d28c938fbe823155fedeb5e5f85bd6a98e04ceed))
31
-
1
+ ### v13.18.0 (2026-03-30)
2
+
3
+ #### Features
4
+
5
+ * Instrumented grpc server stream to bind async context to stream event handlers ([#3855](https://github.com/newrelic/node-newrelic/pull/3855)) ([92c70a8](https://github.com/newrelic/node-newrelic/commit/92c70a8c81c7bf26f63cf3f5b94d993ebc88bf51))
6
+
7
+ #### Code refactoring
8
+
9
+ * Refactored `when` instrumentation to subscribe to events emitted ([#3866](https://github.com/newrelic/node-newrelic/pull/3866)) ([1b55c5a](https://github.com/newrelic/node-newrelic/commit/1b55c5ae5df8eb8d20d499e31b80014acd9b7833))
10
+ * Refactored `memcached` instrumentation to subscribe to events emitted ([#3849](https://github.com/newrelic/node-newrelic/pull/3849)) ([915b320](https://github.com/newrelic/node-newrelic/commit/915b320e1b01944fe89754305e146c0c69dca0c6))
11
+ * Refactored `bluebird` instrumentation to subscribe to events emitted ([#3858](https://github.com/newrelic/node-newrelic/pull/3858)) ([e3d84c2](https://github.com/newrelic/node-newrelic/commit/e3d84c285b9b73c2d2e7f690fe449371e92a5b1a))
12
+ * Refactored `q` instrumentation to subscribe to events emitted ([#3853](https://github.com/newrelic/node-newrelic/pull/3853)) ([9fdb0b8](https://github.com/newrelic/node-newrelic/commit/9fdb0b80130998d283d3ec6a2f0e1b39916bd9e5))
13
+ * Refactored `grpc-js` client instrumentation to subscriber type ([#3856](https://github.com/newrelic/node-newrelic/pull/3856)) ([057fca2](https://github.com/newrelic/node-newrelic/commit/057fca295e31a952795d688e7a12228c58d839ba))
14
+ * Refactored `grpc-js` server instrumentation to subscriber type ([#3805](https://github.com/newrelic/node-newrelic/pull/3805)) ([ca3010a](https://github.com/newrelic/node-newrelic/commit/ca3010aca73cf69786794824063b4f2d5ce38d41))
15
+ * Updated `@langchain/core` subscribers to properly instrument the tool class methods ([#3850](https://github.com/newrelic/node-newrelic/pull/3850)) ([2d17eed](https://github.com/newrelic/node-newrelic/commit/2d17eeda540ef99f5ce829499ca1f4b0ef65e1a6))
16
+
17
+ #### Documentation
18
+
19
+ * Updated compatibility report ([#3831](https://github.com/newrelic/node-newrelic/pull/3831)) ([9156a6a](https://github.com/newrelic/node-newrelic/commit/9156a6a239dd57c46524d5d1a43a654dc7a89cb5))
20
+
21
+ #### Miscellaneous chores
22
+
23
+ * Added subscriber packages to `load-externals` ([#3868](https://github.com/newrelic/node-newrelic/pull/3868)) ([83adcc4](https://github.com/newrelic/node-newrelic/commit/83adcc43b061e55b2d9d3df6fafa152b2ed201df))
24
+
25
+ #### Tests
26
+
27
+ * Removed unused context manager feature flag for running versioned tests ([#3851](https://github.com/newrelic/node-newrelic/pull/3851)) ([15c9757](https://github.com/newrelic/node-newrelic/commit/15c9757339e0d811c96ff58fc275386b9fc2a7a4))
28
+ * Updated core tracing cross agent tests ([#3860](https://github.com/newrelic/node-newrelic/pull/3860)) ([08e4570](https://github.com/newrelic/node-newrelic/commit/08e45703852ea556c8518acd0560c57f56ea9c8c))
29
+ * Fixed issue with span links test ([#3863](https://github.com/newrelic/node-newrelic/pull/3863)) ([23a8594](https://github.com/newrelic/node-newrelic/commit/23a8594f7b0078b62c73f73e7afa0411e96ade69))
30
+ * Removed segment duration timing threshold in `memcached` tests ([#3869](https://github.com/newrelic/node-newrelic/pull/3869)) ([bd24410](https://github.com/newrelic/node-newrelic/commit/bd24410f135868a4e86f4c5375baf67cee2ccbc7))
31
+
32
+ ### v13.17.0 (2026-03-19)
33
+
34
+ #### Features
35
+
36
+ * Added support for Continuous Profiling ([#3834](https://github.com/newrelic/node-newrelic/pull/3834)) ([6d8d5b5](https://github.com/newrelic/node-newrelic/commit/6d8d5b5df2f459ffca39cc14ef0cdabcf7f22cea))
37
+ * Added server-side configuration for `profiling.enabled` ([#3790](https://github.com/newrelic/node-newrelic/pull/3790)) ([0d44e5b](https://github.com/newrelic/node-newrelic/commit/0d44e5b3b24b60d8b920f7d6a762f208fa4139ca)) ([#3847](https://github.com/newrelic/node-newrelic/pull/3847)) ([e57223c](https://github.com/newrelic/node-newrelic/commit/e57223c7c66ab7fe1f464725efe0474e23326159))
38
+ * Added delay + duration to `ProfilingAggregator` ([#3815](https://github.com/newrelic/node-newrelic/pull/3815)) ([8000665](https://github.com/newrelic/node-newrelic/commit/8000665b1714275f8e019a2f7c911d62ee8dd127))
39
+ * Added supportability metrics for profiling ([#3801](https://github.com/newrelic/node-newrelic/pull/3801)) ([19bbb7e](https://github.com/newrelic/node-newrelic/commit/19bbb7ef426e835a13ec38fc543637bdae57a5fc))
40
+ * Prevented ProfilingAggregator from running in serverless mode ([#3806](https://github.com/newrelic/node-newrelic/pull/3806)) ([e4db0f4](https://github.com/newrelic/node-newrelic/commit/e4db0f469fde9ae6aa4984cb312ee1de269e5c7b))
41
+ * Disabled `profiling.enabled` when high security mode is enabled ([#3803](https://github.com/newrelic/node-newrelic/pull/3803)) ([9d4eebb](https://github.com/newrelic/node-newrelic/commit/9d4eebb11df00fc5729ad178063712734449741a))
42
+ * Added Heap profiler ([#3751](https://github.com/newrelic/node-newrelic/pull/3751)) ([8262f6b](https://github.com/newrelic/node-newrelic/commit/8262f6bb5d7bf0a1c996455d6829883a7ff3b696))
43
+ * Added Cpu Profiler ([#3747](https://github.com/newrelic/node-newrelic/pull/3747)) ([2c2097f](https://github.com/newrelic/node-newrelic/commit/2c2097fdfd4ec924bf24901eecb8ad62a9e4c4f5))
44
+
45
+ **Note**: Continuous Profiling UI is coming soon! While the Node.js agent now supports Continuous Profiling data collection, the visualization experience will debut in Limited Preview starting next week. If you would like to get early access to the profiling dashboards and UI features, please reach out to your Account Executive (AE) about how to opt-in to the preview.
46
+
47
+ #### Code refactoring
48
+
49
+ * Refactor `winston` instrumentation to subscribe to events emitted ([#3808](https://github.com/newrelic/node-newrelic/pull/3808)) ([4af2f48](https://github.com/newrelic/node-newrelic/commit/4af2f48d99b0842085044deb36d14af9e96ac8ed))
50
+
51
+ #### Documentation
52
+
53
+ * Updated compatibility report ([#3824](https://github.com/newrelic/node-newrelic/pull/3824)) ([0cfb356](https://github.com/newrelic/node-newrelic/commit/0cfb356c487fbe47c4ac11095704846d948168fb))
54
+
55
+ #### Tests
56
+
57
+ * Added segment duration tests for `ioredis`/`iovalkey` ([#3825](https://github.com/newrelic/node-newrelic/pull/3825)) ([9738bc8](https://github.com/newrelic/node-newrelic/commit/9738bc88b59c219d896d80e0f8ccb93866cd7f0b))
58
+ * Added segment duration timing tests for `mysql2` ([#3820](https://github.com/newrelic/node-newrelic/pull/3820)) ([bb424ff](https://github.com/newrelic/node-newrelic/commit/bb424ffa59fe55912f1ad711d84b57518dbd778f))
59
+ * Changed structure of bedrock tests to avoid getting instrumented core libraries from first test ([#3838](https://github.com/newrelic/node-newrelic/pull/3838)) ([886a36c](https://github.com/newrelic/node-newrelic/commit/886a36c1607e1a7c0ddc0688f7b8ee62f9a8bf10))
60
+ * Changed structure of lambda and sns tests to avoid getting instrumented core libraries from first test ([#3836](https://github.com/newrelic/node-newrelic/pull/3836)) ([414044d](https://github.com/newrelic/node-newrelic/commit/414044d184be885843a3267097c87954daf408bd))
61
+ * Updated `@aws-sdk/lib-dynamodb` version range ([#3829](https://github.com/newrelic/node-newrelic/pull/3829)) ([d28c938](https://github.com/newrelic/node-newrelic/commit/d28c938fbe823155fedeb5e5f85bd6a98e04ceed))
62
+
32
63
  ### v13.16.0 (2026-03-11)
33
64
 
34
65
  #### Bug fixes
@@ -5,6 +5,19 @@
5
5
 
6
6
  'use strict'
7
7
 
8
+ /**
9
+ * Data used across {@link AsyncLocalStorage} invocations.
10
+ *
11
+ * @typedef {object} AsyncContext
12
+ * @property {Transaction} transaction The current transaction or `null`.
13
+ * @property {TraceSegment} segment The current segment or `null`.
14
+ * @property {object} extras Miscellaneous data used during the handling of
15
+ * this context.
16
+ */
17
+
18
+ /**
19
+ * Implementation of an {@link AsyncContext}.
20
+ */
8
21
  module.exports = class Context {
9
22
  constructor({ transaction, segment, extras = {} } = {}) {
10
23
  this._transaction = transaction
@@ -35,7 +48,7 @@ module.exports = class Context {
35
48
  * @param {object} params to function
36
49
  * @param {TraceSegment} params.segment segment to bind to context
37
50
  * @param {Transaction} params.transaction active transaction
38
- * @returns {Context} a newly constructed context
51
+ * @returns {AsyncContext} a newly constructed context
39
52
  */
40
53
  enterSegment({ segment, transaction = this.transaction }) {
41
54
  return new this.constructor({ transaction, segment, extras: this.extras })
@@ -10,7 +10,6 @@ exports.prerelease = {
10
10
  // internal_test_only is used for testing our feature flag implementation.
11
11
  // It is not used to gate any features.
12
12
  internal_test_only: false,
13
- promise_segments: false,
14
13
  reverse_naming_rules: false,
15
14
  unresolved_promise_cleanup: true,
16
15
  kafkajs_instrumentation: false,
@@ -33,6 +32,7 @@ exports.released = [
33
32
  'native_metrics',
34
33
  'new_promise_tracking',
35
34
  'opentelemetry',
35
+ 'promise_segments',
36
36
  'protocol_17',
37
37
  'released',
38
38
  'send_request_uri_attribute',
@@ -11,21 +11,16 @@ const InstrumentationDescriptor = require('./instrumentation-descriptor')
11
11
  module.exports = function instrumentations() {
12
12
  return {
13
13
  '@azure/functions': { type: InstrumentationDescriptor.TYPE_GENERIC },
14
- '@grpc/grpc-js': { module: './instrumentation/grpc-js' },
15
14
  '@hapi/hapi': { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
16
15
  '@hapi/vision': { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
17
16
  '@prisma/client': { type: InstrumentationDescriptor.TYPE_DATASTORE },
18
17
  'aws-sdk': { module: './instrumentation/aws-sdk' },
19
- bluebird: { type: InstrumentationDescriptor.TYPE_PROMISE },
20
18
  connect: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
21
19
  fastify: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
22
20
  kafkajs: { type: InstrumentationDescriptor.TYPE_MESSAGE },
23
21
  koa: { module: './instrumentation/koa' },
24
- memcached: { type: InstrumentationDescriptor.TYPE_DATASTORE },
25
22
  mongodb: { type: InstrumentationDescriptor.TYPE_DATASTORE },
26
23
  next: { module: './instrumentation/nextjs' },
27
- q: { type: null },
28
- restify: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
29
- when: { module: './instrumentation/when' }
24
+ restify: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK }
30
25
  }
31
26
  }
@@ -66,8 +66,10 @@ class SpanLink {
66
66
  this.intrinsics.linkedSpanId = link.context.spanId
67
67
  this.intrinsics.linkedTraceId = link.context.traceId
68
68
 
69
- for (const [key, value] of Object.entries(link.attributes)) {
70
- this.userAttributes.addAttribute(DESTINATIONS.TRANS_SEGMENT, key, value)
69
+ if (Object.hasOwn(link, 'attributes') === true) {
70
+ for (const [key, value] of Object.entries(link.attributes)) {
71
+ this.userAttributes.addAttribute(DESTINATIONS.TRANS_SEGMENT, key, value)
72
+ }
71
73
  }
72
74
  }
73
75
 
@@ -9,27 +9,32 @@
9
9
  // 'package-name': [ { path: 'subscriberPath', instrumentations: [] }, ... ]
10
10
  const subscribers = {
11
11
  ...require('./subscribers/amqplib/config'),
12
+ ...require('./subscribers/bluebird/config'),
12
13
  ...require('./subscribers/bunyan/config'),
13
14
  ...require('./subscribers/cassandra-driver/config'),
14
15
  ...require('./subscribers/elasticsearch/config'),
15
16
  ...require('./subscribers/express/config'),
16
17
  ...require('./subscribers/fastify/config'),
17
18
  ...require('./subscribers/google-genai/config'),
19
+ ...require('./subscribers/grpcjs/config'),
18
20
  ...require('./subscribers/ioredis/config'),
19
21
  ...require('./subscribers/iovalkey/config'),
20
22
  ...require('./subscribers/langchain/config'),
21
23
  ...require('./subscribers/langgraph/config'),
22
24
  ...require('./subscribers/mcp-sdk/config'),
25
+ ...require('./subscribers/memcached/config'),
23
26
  ...require('./subscribers/mysql/config'),
24
27
  ...require('./subscribers/mysql2/config'),
25
28
  ...require('./subscribers/nestjs/config'),
26
29
  ...require('./subscribers/node-redis-client/config'),
27
30
  ...require('./subscribers/openai/config'),
28
- ...require('./subscribers/pino/config'),
29
31
  ...require('./subscribers/pg/config'),
32
+ ...require('./subscribers/pino/config'),
33
+ ...require('./subscribers/q/config'),
30
34
  ...require('./subscribers/redis/config'),
31
35
  ...require('./subscribers/redis-client/config'),
32
36
  ...require('./subscribers/undici/config'),
37
+ ...require('./subscribers/when/config'),
33
38
  ...require('./subscribers/winston/config')
34
39
  }
35
40
 
@@ -84,6 +84,16 @@ module.exports = {
84
84
  }
85
85
  ```
86
86
 
87
+ #### Function Query
88
+
89
+ | Query type | Use when |
90
+ | ----------------------------------- | ---------------------------------------------------------------------------------- |
91
+ | `expressionName` | Function expression assigned to a variable/property (`x.y = function name() {}`) |
92
+ | `functionName` | Standalone function declaration (`function foo() {}`) |
93
+ | `className` + `methodName` | Class method (`class Foo { bar() {} }`) |
94
+ | `methodName` alone | Any method with that name across all classes |
95
+ | `moduleName` + `expressionName` | Disambiguate by the object it's assigned to |
96
+
87
97
  ### Creating the Subscribers
88
98
 
89
99
  Now that you have the config specified for the function that you are instrumenting, you'll then need to create a subscriber for it. All subscribers should at least inherit from the base [`Subscriber`](./base.js) with the exception of subscribers that do not rely on `orchestrion` to create their tracing channels (they inherit from the `node:diagnostics_channel` `Subscriber` in [`dc-base.js`](./dc-base.js)).
@@ -182,12 +182,17 @@ class Subscriber {
182
182
 
183
183
  /**
184
184
  * Creates a segment with a name, parent, transaction and optional recorder.
185
- * If the segment is successfully created, it will be started and added to the context.
185
+ * If the segment is successfully created, it will be started and added to the
186
+ * context.
187
+ *
186
188
  * @param {object} params - Parameters for creating the segment
187
189
  * @param {string} params.name - The name of the segment
188
190
  * @param {object} [params.recorder] - Optional recorder for the segment
189
- * @param {Context} params.ctx - The context containing the parent segment and transaction
190
- * @returns {Context} - The updated context with the new segment or existing context if segment creation fails
191
+ * @param {Context} params.ctx - The context containing the parent segment and
192
+ * transaction
193
+ *
194
+ * @returns {AsyncContext} - The updated context with the new segment or
195
+ * existing context if segment creation fails
191
196
  */
192
197
  createSegment({ name, recorder, ctx }) {
193
198
  const parent = ctx?.segment
@@ -210,8 +215,7 @@ class Subscriber {
210
215
  segment.start()
211
216
  this.logger.trace('Created segment %s, returning new context', name)
212
217
  this.addAttributes(segment)
213
- const newCtx = ctx.enterSegment({ segment })
214
- return newCtx
218
+ return ctx.enterSegment({ segment })
215
219
  } else {
216
220
  this.logger.trace('Failed to create segment for %s, returning existing context', name)
217
221
  return ctx
@@ -230,9 +234,9 @@ class Subscriber {
230
234
  /**
231
235
  * Not all subscribers need to change the context on an event.
232
236
  * This is defined on base to fulfill those use cases.
233
- * @param {object} data event passed to handler
234
- * @param {Context} ctx context passed to handler
235
- * @returns {Context} either new context or existing
237
+ * @param {SubscriberHandlerData} data event passed to handler
238
+ * @param {SubscriberHandlerContext} ctx context passed to handler
239
+ * @returns {AsyncContext} either new context or existing
236
240
  */
237
241
  handler(data, ctx) {
238
242
  return ctx
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ 'use strict'
6
+
7
+ module.exports = {
8
+ bluebird: [
9
+ {
10
+ path: './bluebird/instrumentation',
11
+ instrumentations: [{
12
+ channelName: 'nr_then',
13
+ module: { name: 'bluebird', versionRange: '>=2.02', filePath: 'js/release/promise.js' },
14
+ functionQuery: {
15
+ functionName: 'Promise',
16
+ kind: 'Sync'
17
+ }
18
+ }]
19
+ },
20
+ ]
21
+ }
@@ -0,0 +1,59 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const BaseSubscriber = require('../base')
9
+
10
+ module.exports = class BluebirdPromiseSubscriber extends BaseSubscriber {
11
+ constructor({ agent, logger }) {
12
+ super({ agent, logger, channelName: 'nr_then', packageName: 'bluebird' })
13
+ }
14
+
15
+ /**
16
+ * We have to wrap the Promise prototype, not just the _then method.
17
+ * This is because we need to get the current context from when the promise was constructed.
18
+ * If we only wrapped `_then`, the context is not the same, you can see from the versioned tests
19
+ * Where it will defer promise resolution
20
+ *
21
+ * @param {object} data event data
22
+ * @param {Context} ctx the current context
23
+ * @returns {Context} in this case it is the same context, just with both `_then` and its respective callbacks bound to the current context
24
+ */
25
+ handler(data, ctx) {
26
+ const { self: BluebirdPromise } = data
27
+ const origThen = BluebirdPromise._then
28
+ BluebirdPromise._then = this.#wrapThenHandler({ ctx, origThen })
29
+ return ctx
30
+ }
31
+
32
+ /**
33
+ * wraps the current `_then` method on the instance.
34
+ * It binds the context from when promise was constructed to the callbacks(didFulfill, didReject, didProgress)
35
+ *
36
+ * @param {object} params to function
37
+ * @param {Context} params.ctx active context
38
+ * @param {Function} params.origThen the original `_then` method on promise instance
39
+ * @returns {Function} wrapped `_then` function
40
+ */
41
+ #wrapThenHandler({ ctx, origThen }) {
42
+ const self = this
43
+ return function wrappedThenHandler(...args) {
44
+ const [didFulfill, didReject, didProgress] = args
45
+ // only bind the callback if the transaction from context is still active
46
+ if (ctx.transaction.isActive() === false) {
47
+ return origThen.apply(this, args)
48
+ }
49
+ args[0] = self.agent.tracer.bindFunction(didFulfill, ctx)
50
+ args[1] = self.agent.tracer.bindFunction(didReject, ctx)
51
+
52
+ // didProgress is only applicable to v2.x
53
+ if (didProgress) {
54
+ args[2] = self.agent.tracer.bindFunction(didProgress, ctx)
55
+ }
56
+ return self.agent.tracer.bindFunction(origThen, ctx).apply(this, args)
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,126 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const Subscriber = require('../base.js')
9
+ const shouldTrackError = require('./should-track-error.js')
10
+ const recordExternal = require('#agentlib/metrics/recorders/http_external.js')
11
+
12
+ module.exports = class GrpcClientSubscriber extends Subscriber {
13
+ constructor({ agent, logger, channelName }) {
14
+ super({
15
+ agent,
16
+ logger,
17
+ channelName,
18
+ packageName: '@grpc/grpc-js',
19
+ })
20
+
21
+ this.opaque = true
22
+ }
23
+
24
+ handler(data, ctx) {
25
+ const { arguments: args, self: client } = data
26
+ const { transaction } = ctx
27
+
28
+ const { channel } = client
29
+ const authority = channel.target?.path || channel.getDefaultAuthority
30
+ // In grpc-js>=1.8.0 `.methodName` becomes `.method`.
31
+ const method = client.methodName || client.method
32
+
33
+ const newCtx = this.createSegment({
34
+ name: `External/${authority}${method}`,
35
+ recorder: recordExternal(authority, 'gRPC'),
36
+ ctx
37
+ })
38
+
39
+ // Acquire the original parameters to the handler, create patched
40
+ // versions of them, and update the parameters list with the patched
41
+ // instances.
42
+ const origMetadata = args[0]
43
+ const origListener = args[1]
44
+ const nrMetadata = origMetadata.clone()
45
+ const nrListener = Object.assign({}, origListener)
46
+ args[0] = nrMetadata
47
+ args[1] = nrListener
48
+
49
+ this.#addDistributedTraceHeaders(transaction, nrMetadata)
50
+ this.#wrapListenerCallback(nrListener, origListener, authority, method, newCtx)
51
+
52
+ return newCtx
53
+ }
54
+
55
+ /**
56
+ * When distributed tracing (DT) is enabled, pull DT headers from the
57
+ * provided transaction and add them to the metadata object used by gRPC
58
+ * when delivering data.
59
+ *
60
+ * @param {Transaction} transaction The transaction that contains the DT data.
61
+ * @param {object} destination The gRPC metadata object to add headers to.
62
+ * This object will be mutated.
63
+ */
64
+ #addDistributedTraceHeaders(transaction, destination) {
65
+ if (this.agent.config.distributed_tracing.enabled === false) {
66
+ this.logger.debug('Distributed tracing disabled by instrumentation.')
67
+ return
68
+ }
69
+
70
+ const outboundAgentHeaders = Object.create(null)
71
+ transaction.insertDistributedTraceHeaders(outboundAgentHeaders)
72
+ for (const [key, value] of Object.entries(outboundAgentHeaders)) {
73
+ destination.add(key, value)
74
+ }
75
+ }
76
+
77
+ /**
78
+ * When the original gRPC "listener" object has an `onReceiveStatus` method
79
+ * defined, wrap that method to capture tracing data and attach the wrapped
80
+ * method to the patched `nrListener` instance.
81
+ *
82
+ * @param {object} nrListener New Relic clone of the original listener.
83
+ * @param {object} origListener Original gRPC listener object.
84
+ * @param {string} authority Value of the `:authority` header.
85
+ * @param {string} method The gRPC method name to be invoked.
86
+ * @param {AsyncContext} ctx The context to execute the `onReceiveStatus`
87
+ * handler under.
88
+ */
89
+ #wrapListenerCallback(nrListener, origListener, authority, method, ctx) {
90
+ if (typeof origListener?.onReceiveStatus !== 'function') {
91
+ return
92
+ }
93
+
94
+ const agent = this.agent
95
+ const [hostname, port] = authority.split(':')
96
+
97
+ nrListener.onReceiveStatus = function nrOnReceiveStatus(status) {
98
+ const { segment, transaction } = ctx
99
+ const { code, details } = status
100
+ segment.addAttribute('grpc.statusCode', code)
101
+ segment.addAttribute('grpc.statusText', details)
102
+ segment.addAttribute('component', 'gRPC')
103
+
104
+ if (shouldTrackError(code, agent.config) === true) {
105
+ agent.errors.add(transaction, details)
106
+ }
107
+
108
+ const protocol = 'grpc:'
109
+ segment.captureExternalAttributes({
110
+ protocol,
111
+ host: authority,
112
+ port,
113
+ hostname,
114
+ method,
115
+ path: method
116
+ })
117
+
118
+ const boundFn = agent.tracer.bindFunction(
119
+ origListener.onReceiveStatus,
120
+ ctx,
121
+ true
122
+ )
123
+ boundFn(status)
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const modName = '@grpc/grpc-js'
9
+
10
+ module.exports = {
11
+ [modName]: [
12
+ {
13
+ path: './grpcjs/server.js',
14
+ instrumentations: [{
15
+ channelName: 'nr_grpc_server',
16
+ module: {
17
+ name: modName,
18
+ filePath: 'build/src/server.js',
19
+ versionRange: '>=1.4.0'
20
+ },
21
+ functionQuery: {
22
+ className: 'Server',
23
+ methodName: 'register',
24
+ kind: 'Sync'
25
+ }
26
+ }]
27
+ },
28
+
29
+ {
30
+ path: './grpcjs/http2-stream.js',
31
+ instrumentations: [{
32
+ channelName: 'nr_grpc_stream',
33
+ module: {
34
+ name: modName,
35
+ filePath: 'build/src/call-stream.js',
36
+ versionRange: '>=1.4.0 <1.8.0'
37
+ },
38
+ functionQuery: {
39
+ className: 'Http2CallStream',
40
+ methodName: 'start',
41
+ kind: 'Sync'
42
+ }
43
+ }]
44
+ },
45
+
46
+ {
47
+ path: './grpcjs/resolving-call.js',
48
+ instrumentations: [{
49
+ channelName: 'nr_grpc_resolving',
50
+ module: {
51
+ name: modName,
52
+ filePath: 'build/src/resolving-call.js',
53
+ versionRange: '>=1.8.0'
54
+ },
55
+ functionQuery: {
56
+ className: 'ResolvingCall',
57
+ methodName: 'start',
58
+ kind: 'Sync'
59
+ }
60
+ }]
61
+ }
62
+ ]
63
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const GCS = require('./client.js')
9
+
10
+ module.exports = class Http2StreamSubscriber extends GCS {
11
+ constructor({ agent, logger }) {
12
+ super({ agent, logger, channelName: 'nr_grpc_stream' })
13
+ }
14
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2026 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const GCS = require('./client.js')
9
+
10
+ module.exports = class ResolvingCallSubscriber extends GCS {
11
+ constructor({ agent, logger }) {
12
+ super({ agent, logger, channelName: 'nr_grpc_resolving' })
13
+ }
14
+ }