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.
- package/NEWS.md +62 -31
- package/lib/context-manager/context.js +14 -1
- package/lib/feature_flags.js +1 -1
- package/lib/instrumentations.js +1 -6
- package/lib/spans/span-link.js +4 -2
- package/lib/subscriber-configs.js +6 -1
- package/lib/subscribers/README.md +10 -0
- package/lib/subscribers/base.js +12 -8
- package/lib/subscribers/bluebird/config.js +21 -0
- package/lib/subscribers/bluebird/instrumentation.js +59 -0
- package/lib/subscribers/grpcjs/client.js +126 -0
- package/lib/subscribers/grpcjs/config.js +63 -0
- package/lib/subscribers/grpcjs/http2-stream.js +14 -0
- package/lib/subscribers/grpcjs/resolving-call.js +14 -0
- package/lib/subscribers/grpcjs/server.js +206 -0
- package/lib/subscribers/grpcjs/should-track-error.js +12 -0
- package/lib/subscribers/index.js +37 -0
- package/lib/subscribers/langchain/config.js +22 -2
- package/lib/subscribers/memcached/command.js +69 -0
- package/lib/subscribers/memcached/config.js +23 -0
- package/lib/subscribers/message-consumer.js +8 -6
- package/lib/subscribers/q/config.js +32 -0
- package/lib/subscribers/q/next-tick.js +21 -0
- package/lib/subscribers/q/run-after.js +14 -0
- package/lib/subscribers/when/config.js +21 -0
- package/lib/subscribers/when/instrumentation.js +33 -0
- package/lib/transaction/index.js +3 -1
- package/load-externals.js +13 -1
- package/package.json +1 -1
- package/lib/instrumentation/bluebird.js +0 -109
- package/lib/instrumentation/grpc-js/grpc.js +0 -271
- package/lib/instrumentation/grpc-js/nr-hooks.js +0 -32
- package/lib/instrumentation/memcached.js +0 -84
- package/lib/instrumentation/q.js +0 -29
- package/lib/instrumentation/when/constants.js +0 -66
- package/lib/instrumentation/when/contextualizer.js +0 -181
- package/lib/instrumentation/when/index.js +0 -363
- package/lib/instrumentation/when/nr-hooks.js +0 -16
package/NEWS.md
CHANGED
|
@@ -1,34 +1,65 @@
|
|
|
1
|
-
### v13.
|
|
2
|
-
|
|
3
|
-
#### Features
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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 {
|
|
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 })
|
package/lib/feature_flags.js
CHANGED
|
@@ -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',
|
package/lib/instrumentations.js
CHANGED
|
@@ -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
|
-
|
|
28
|
-
restify: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
|
|
29
|
-
when: { module: './instrumentation/when' }
|
|
24
|
+
restify: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK }
|
|
30
25
|
}
|
|
31
26
|
}
|
package/lib/spans/span-link.js
CHANGED
|
@@ -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
|
-
|
|
70
|
-
|
|
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)).
|
package/lib/subscribers/base.js
CHANGED
|
@@ -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
|
|
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
|
|
190
|
-
*
|
|
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
|
-
|
|
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 {
|
|
234
|
-
* @param {
|
|
235
|
-
* @returns {
|
|
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
|
+
}
|