newrelic 13.6.0 → 13.6.2
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 +22 -0
- package/lib/subscribers/README.md +101 -0
- package/lib/subscribers/amqplib/consume.js +2 -2
- package/lib/subscribers/application-logs.js +7 -3
- package/lib/subscribers/base.js +24 -13
- package/lib/subscribers/db-operation.js +3 -0
- package/lib/subscribers/db-query.js +17 -0
- package/lib/subscribers/db.js +23 -1
- package/lib/subscribers/express/route.js +10 -0
- package/lib/subscribers/express/use.js +4 -2
- package/lib/subscribers/message-consumer.js +11 -6
- package/lib/subscribers/message-producer.js +9 -5
- package/lib/subscribers/meta-subscriber.js +14 -7
- package/lib/subscribers/middleware-wrapper.js +9 -0
- package/lib/subscribers/propagation.js +13 -2
- package/package.json +1 -1
package/NEWS.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
### v13.6.2 (2025-10-29)
|
|
2
|
+
|
|
3
|
+
#### Bug fixes
|
|
4
|
+
|
|
5
|
+
* Updated express instrumentation to properly instrument an array of middleware defined on a route ([#3473](https://github.com/newrelic/node-newrelic/pull/3473)) ([e383c4e](https://github.com/newrelic/node-newrelic/commit/e383c4efc7171f02aef41d6c657455338b4b7258))
|
|
6
|
+
|
|
7
|
+
#### Documentation
|
|
8
|
+
|
|
9
|
+
* documented how to use the subscriber based instrumentation ([#3432](https://github.com/newrelic/node-newrelic/pull/3432)) ([3b3ab72](https://github.com/newrelic/node-newrelic/commit/3b3ab72eee3b4c1a12592d92eab59da2a48b1f46))
|
|
10
|
+
* Updated compatibility report ([#3471](https://github.com/newrelic/node-newrelic/pull/3471)) ([964543b](https://github.com/newrelic/node-newrelic/commit/964543bae1a7c1fd5af32f02ba40d4bf68737d11))
|
|
11
|
+
|
|
12
|
+
### v13.6.1 (2025-10-28)
|
|
13
|
+
|
|
14
|
+
#### Bug fixes
|
|
15
|
+
|
|
16
|
+
* Updated middleware wrapper to not wrap handler if it is not a function ([#3469](https://github.com/newrelic/node-newrelic/pull/3469)) ([c702689](https://github.com/newrelic/node-newrelic/commit/c702689a6e73756f6b3ad39706a33874fbd6da73))
|
|
17
|
+
|
|
18
|
+
#### Documentation
|
|
19
|
+
|
|
20
|
+
* Updated compatibility report ([#3466](https://github.com/newrelic/node-newrelic/pull/3466)) ([5853657](https://github.com/newrelic/node-newrelic/commit/5853657a63dc75481fc6598c10afd208391f525b))
|
|
21
|
+
|
|
1
22
|
### v13.6.0 (2025-10-27)
|
|
2
23
|
|
|
3
24
|
#### Features
|
|
@@ -8000,3 +8021,4 @@ Special thanks to Ryan Copley (@RyanCopley) for the contribution.
|
|
|
8000
8021
|
|
|
8001
8022
|
|
|
8002
8023
|
|
|
8024
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Subscriber-based Instrumentation
|
|
2
|
+
|
|
3
|
+
As of v13.2.0, we have begun to refactor our traditional instrumentation (`Shim`-based monkey-patching) to instead subscribe to events emitted by Node's [`diagnostic_channel TracingChannel`](https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel). This is done through [`@apm-js-collab/tracing-hooks`](https://github.com/apm-js-collab/tracing-hooks), a helper for [`orchestrion-js`](https://github.com/apm-js-collab/orchestrion-js) which injects the relevant tracing channels into the instrumented package. We then define a `Subscriber` that listens to these channels for specific events (`asyncEnd`, `asyncStart`, `start`, `end`, and/or `error`) and record what we need from the event data and context (which is preserved through `AsyncLocalStorage`).
|
|
4
|
+
|
|
5
|
+
## How to Implement
|
|
6
|
+
|
|
7
|
+
Like `Shim`-based instrumentation, subscriber-based instrumentation largely relies on the specific way the package you're instrumenting is written. However, all packages will follow the below template/guidelines.
|
|
8
|
+
|
|
9
|
+
### Disable Shim-based Instrumentation
|
|
10
|
+
|
|
11
|
+
1. While you are testing your new instrumentation, it's important that you're not also testing the old instrumentation. However, you likely want to keep the old instrumentation around while you're refactoring for reference. The easiest way to do this is to just remove the instrumentation reference in `lib/instrumentations.js`.
|
|
12
|
+
2. When you are done refactoring, make sure to delete all files in `lib/instrumentation/<package_name>` (or `lib/instrumentation/<package_name>.js`), the tests in `test/unit/instrumentation/<package_name> `that rely on `Shim`-based wrapping, and the instrumentation reference in `instrumentations.js` if you haven't already.
|
|
13
|
+
|
|
14
|
+
### Instrumentation Config
|
|
15
|
+
|
|
16
|
+
Now, it is time to look at the internals of the package you're instrumenting. Again, the `Shim`-based instrumentation you're replacing should be helpful here to get the gist of the package internals.
|
|
17
|
+
|
|
18
|
+
1. Create a folder within `lib/subscribers` with the name of the package. If the package is not a new instrumentation, use the same name as the one in `test/versioned`. If it is a new instrumentation and the package name is exceptionally long or complicated or is prefixed with `@`, you may provide a shortened version (e.g. `@modelcontextprotocol/sdk `->`mcp-sdk `). Remember to name the versioned test folder with the same name (`test/versioned/<package_name|shortened_package_name>`).
|
|
19
|
+
2. Create a `config.js` within that folder.
|
|
20
|
+
3. Add a reference to the new config file in [`lib/subscriber-configs.js`](../subscriber-configs.js):
|
|
21
|
+
1. ```javascript
|
|
22
|
+
...require('./subscribers/<package_name>/config')
|
|
23
|
+
```
|
|
24
|
+
4. Identify one function to start with and find where this function lives in the package i.e. the relative file path.
|
|
25
|
+
5. Once you have found where the function you're instrumenting is, you need to determine how it is defined in [AST](https://astexplorer.net/), so that `orchestrion` can properly wrap it. You can then add the proper instrumentation object to your `config.js`.
|
|
26
|
+
|
|
27
|
+
#### Config Template
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// in lib/subscribers/<package_name>/config.js
|
|
31
|
+
|
|
32
|
+
const config = {
|
|
33
|
+
path: './<package_name>/<subscriber_name>.js',
|
|
34
|
+
instrumentations: [
|
|
35
|
+
{
|
|
36
|
+
/**
|
|
37
|
+
* By convention, we prefix channelNames with `nr_` and include at least the expressionName or methodName.
|
|
38
|
+
* It could also contain the moduleName or className to further differentiate between subscribers.
|
|
39
|
+
*/
|
|
40
|
+
channelName: 'nr_functionName',
|
|
41
|
+
/**
|
|
42
|
+
* <version_range> should be the same as the old instrumentation.
|
|
43
|
+
* However, you may need to break apart that range across different configs
|
|
44
|
+
* because code can differ from version to version.
|
|
45
|
+
*
|
|
46
|
+
* <relative_path_to_file> is the relative path from the instrumented package
|
|
47
|
+
* to the file that contains the code that you want to instrument
|
|
48
|
+
*/
|
|
49
|
+
module: { name: '<package_name>', versionRange: '<version_range>', filePath: '<relative_path_to_file>' },
|
|
50
|
+
functionQuery: {
|
|
51
|
+
className: 'ClassName',
|
|
52
|
+
methodName: 'methodName',
|
|
53
|
+
// If the function is `async`, specify `Async` here. Callback functions are typically `Sync`.
|
|
54
|
+
kind: 'Sync' | 'Async'
|
|
55
|
+
},
|
|
56
|
+
// OR
|
|
57
|
+
// if not a Class
|
|
58
|
+
functionQuery: {
|
|
59
|
+
moduleName: 'ModuleName',
|
|
60
|
+
expressionName: 'expressionName',
|
|
61
|
+
kind: 'Sync' | 'Async'
|
|
62
|
+
},
|
|
63
|
+
// OR
|
|
64
|
+
// if the module is not defined
|
|
65
|
+
functionQuery: {
|
|
66
|
+
expressionName: 'expressionName',
|
|
67
|
+
kind: 'Sync' | 'Async'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* If you need to use the same instrumentation/subscriber for differently structured code
|
|
72
|
+
* (e.g. an older version of the package uses moduleName/expressionName, but now the
|
|
73
|
+
* same function is className/methodName), you'd add another instrumentation object
|
|
74
|
+
* to the array of `instrumentations`.
|
|
75
|
+
*/
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
// Note: config(s) must be in an array, even if there's just one
|
|
81
|
+
'<package_name>': [
|
|
82
|
+
config
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Creating the Subscribers
|
|
88
|
+
|
|
89
|
+
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)).
|
|
90
|
+
|
|
91
|
+
#### Datastore Subscribers
|
|
92
|
+
|
|
93
|
+
For datastore queries, inherit from `DbQuerySubscriber`. For datastore operations, inherit from `DbOperationSubscriber`.
|
|
94
|
+
|
|
95
|
+
#### Messaging Subscribers
|
|
96
|
+
|
|
97
|
+
For messaging queues, inherit from `MessageConsumerSubscriber` or `MessageProducerSubscriber.`
|
|
98
|
+
|
|
99
|
+
#### Propagation Subscriber
|
|
100
|
+
|
|
101
|
+
Many packages are written in a way that causes `AsyncLocalStorage` to lose context. A common instance of this is multiple nestled callbacks. To solve this, create `PropagationSubscriber`s for inner functions within the one you are instrumenting. You may have to experiment a few times to know which function is losing context; in most cases, you should only need one `PropagationSubscriber` to support another subscriber.
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const MessageConsumerSubscriber = require('../message-consumer')
|
|
7
7
|
const { getParameters, getParametersFromMessage, TEMP_RE } = require('./utils')
|
|
8
8
|
|
|
9
|
-
class ConsumeSubscriber extends
|
|
9
|
+
class ConsumeSubscriber extends MessageConsumerSubscriber {
|
|
10
10
|
constructor({ agent, logger, channelName = 'nr_consume' }) {
|
|
11
11
|
super({ agent, logger, packageName: 'amqplib', channelName, system: 'RabbitMQ', type: 'Exchange', callback: 1, transport: 'AMQP' })
|
|
12
12
|
this.segmentName = 'amqplib.Channel#consume'
|
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const Subscriber = require('./base')
|
|
7
|
-
const { isApplicationLoggingEnabled,
|
|
7
|
+
const { isApplicationLoggingEnabled,
|
|
8
|
+
isLocalDecoratingEnabled,
|
|
9
|
+
isLogForwardingEnabled,
|
|
10
|
+
isMetricsEnabled, createModuleUsageMetric,
|
|
11
|
+
incrementLoggingLinesMetrics } = require('../util/application-logging')
|
|
8
12
|
|
|
9
13
|
class ApplicationLogsSubscriber extends Subscriber {
|
|
10
|
-
constructor({ agent, logger, packageName, channelName
|
|
11
|
-
super({ agent, logger, packageName, channelName
|
|
14
|
+
constructor({ agent, logger, packageName, channelName }) {
|
|
15
|
+
super({ agent, logger, packageName, channelName })
|
|
12
16
|
this.requireActiveTx = false
|
|
13
17
|
this.libMetricCreated = false
|
|
14
18
|
}
|
package/lib/subscribers/base.js
CHANGED
|
@@ -38,20 +38,31 @@ const ArrayPrototypeSplice = makeCall(Array.prototype.splice)
|
|
|
38
38
|
* register handlers for. For any name in the set, a corresponding method
|
|
39
39
|
* must exist on the subscriber instance. The method will be passed the
|
|
40
40
|
* event object. Possible event names are `start`, `end`, `asyncStart`,
|
|
41
|
-
* `asyncEnd`, and `error`.
|
|
42
|
-
*
|
|
43
|
-
* @
|
|
41
|
+
* `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.
|
|
45
|
+
* @property {boolean} [internal=false] If true, any children segments from the same library
|
|
46
|
+
* will not be created.
|
|
44
47
|
* @property {string} [prefix='orchestrion:'] String to prepend to diagnostics
|
|
45
48
|
* channel event names. This provides a namespace for the events we are
|
|
46
49
|
* injecting into a module.
|
|
47
|
-
* @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events
|
|
48
|
-
*
|
|
49
|
-
* @property {
|
|
50
|
+
* @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events
|
|
51
|
+
* when there is an active transaction.
|
|
52
|
+
* @property {boolean} [propagateContext=false] If true, it will bind `asyncStart` to the store
|
|
53
|
+
* and re-propagate the active context. It will also attach the `transaction` to the event in
|
|
54
|
+
* `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.
|
|
50
57
|
* @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
|
|
51
58
|
* @property {AsyncLocalStorage} store The async local storage instance used for context management.
|
|
52
|
-
* @property {number} callback
|
|
59
|
+
* @property {number} [callback=null] Position of callback if it needs to be wrapped for instrumentation.
|
|
60
|
+
* -1 means last argument.
|
|
53
61
|
*/
|
|
54
62
|
class Subscriber {
|
|
63
|
+
/**
|
|
64
|
+
* @param {SubscriberParams} params the subscriber constructor params
|
|
65
|
+
*/
|
|
55
66
|
constructor({ agent, logger, packageName, channelName }) {
|
|
56
67
|
this.agent = agent
|
|
57
68
|
this.logger = logger.child({ component: `${packageName}-subscriber` })
|
|
@@ -121,15 +132,15 @@ class Subscriber {
|
|
|
121
132
|
|
|
122
133
|
/**
|
|
123
134
|
* Wraps an event emitter and runs the wrap in the new context
|
|
124
|
-
* If the event is `end` or `error
|
|
135
|
+
* If the event is `end` or `error`, it'll touch the active segment.
|
|
125
136
|
*
|
|
126
137
|
* @param {object} params to function
|
|
127
138
|
* @param {Array} params.args arguments to function
|
|
128
139
|
* @param {number} params.index index of argument to wrap
|
|
129
|
-
* @param {string} params.name name of emit function
|
|
140
|
+
* @param {string} [params.name] name of emit function, defaults to 'emit'
|
|
130
141
|
* @param {Context} params.ctx context to bind wrapped emit to
|
|
131
142
|
*/
|
|
132
|
-
wrapEventEmitter({ args, index, name, ctx }) {
|
|
143
|
+
wrapEventEmitter({ args, index, name = 'emit', ctx }) {
|
|
133
144
|
const orig = args[index][name]
|
|
134
145
|
const self = this
|
|
135
146
|
function wrapEmit(...emitArgs) {
|
|
@@ -148,7 +159,7 @@ class Subscriber {
|
|
|
148
159
|
* If the segment is successfully created, it will be started and added to the context.
|
|
149
160
|
* @param {object} params - Parameters for creating the segment
|
|
150
161
|
* @param {string} params.name - The name of the segment
|
|
151
|
-
* @param {object} params.recorder - Optional recorder for the segment
|
|
162
|
+
* @param {object} [params.recorder] - Optional recorder for the segment
|
|
152
163
|
* @param {Context} params.ctx - The context containing the parent segment and transaction
|
|
153
164
|
* @returns {Context} - The updated context with the new segment or existing context if segment creation fails
|
|
154
165
|
*/
|
|
@@ -191,8 +202,8 @@ class Subscriber {
|
|
|
191
202
|
}
|
|
192
203
|
|
|
193
204
|
/**
|
|
194
|
-
* Not all subscribers need change the context on
|
|
195
|
-
* This is defined on base to fulfill those use cases
|
|
205
|
+
* Not all subscribers need to change the context on an event.
|
|
206
|
+
* This is defined on base to fulfill those use cases.
|
|
196
207
|
* @param {object} data event passed to handler
|
|
197
208
|
* @param {Context} ctx context passed to handler
|
|
198
209
|
* @returns {Context} either new context or existing
|
|
@@ -9,6 +9,9 @@ const { DB } = require('../metrics/names')
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Subscriber for database operation events e.g. `connect`.
|
|
12
|
+
*
|
|
13
|
+
* @property {string} operation The name of the database operation.
|
|
14
|
+
* Used to name the segment created for this operation.
|
|
12
15
|
*/
|
|
13
16
|
class DbOperationSubscriber extends DbSubscriber {
|
|
14
17
|
/**
|
|
@@ -9,7 +9,24 @@ const { DB } = require('../metrics/names')
|
|
|
9
9
|
const ParsedStatement = require('../db/parsed-statement')
|
|
10
10
|
const parseSql = require('../db/query-parsers/sql')
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Defines a subscriber for database queries.
|
|
14
|
+
*
|
|
15
|
+
* @property {string} queryString Must be set by any class that extends this
|
|
16
|
+
* one prior to this class's `.handler` method being invoked. It represents
|
|
17
|
+
* the statement being sent to the database.
|
|
18
|
+
* @property {boolean} [isBatch] Set to true if this subscriber is for batch queries.
|
|
19
|
+
*/
|
|
12
20
|
class DbQuerySubscriber extends DbSubscriber {
|
|
21
|
+
/**
|
|
22
|
+
* On an event, this handler will create a segment with the name
|
|
23
|
+
* `{DB.STATEMENT}/{DB_SYSTEM}/{COLLECTION}/{OPERATION}`.
|
|
24
|
+
* Other than `DB.STATEMENT` which is a constant, these values
|
|
25
|
+
* are extracted from `this.queryString`.
|
|
26
|
+
* @param {object} data event data
|
|
27
|
+
* @param {Context} ctx the context
|
|
28
|
+
* @returns {Context} the updated context with the new segment
|
|
29
|
+
*/
|
|
13
30
|
handler(data, ctx) {
|
|
14
31
|
const queryString = this.queryString
|
|
15
32
|
const parsed = this.parseQueryString(queryString)
|
package/lib/subscribers/db.js
CHANGED
|
@@ -7,7 +7,25 @@ const Subscriber = require('./base')
|
|
|
7
7
|
const { ALL, DB } = require('../metrics/names')
|
|
8
8
|
const urltils = require('../util/urltils')
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @property {object} parameters Must be set by subclasses prior to invoking
|
|
12
|
+
* the `addAttributes` method. Should contain the following keys:
|
|
13
|
+
* - `host`: The database host.
|
|
14
|
+
* - `database_name`: The name of the database.
|
|
15
|
+
* - `port_path_or_id`: The database port, path, or ID.
|
|
16
|
+
* @property {string} system The database system being used (e.g., MySQL, MongoDB).
|
|
17
|
+
*/
|
|
10
18
|
class DbSubscriber extends Subscriber {
|
|
19
|
+
/**
|
|
20
|
+
* @param {object} params constructor params object
|
|
21
|
+
* @param {object} params.agent A New Relic Node.js agent instance.
|
|
22
|
+
* @param {object} params.logger An agent logger instance.
|
|
23
|
+
* @param {string} params.packageName The package name being instrumented.
|
|
24
|
+
* This is what a developer would provide to the `require` function.
|
|
25
|
+
* @param {string} params.channelName A unique name for the diagnostics channel
|
|
26
|
+
* that will be created and monitored.
|
|
27
|
+
* @param {string} params.system The database system being used (e.g., MySQL, MongoDB).
|
|
28
|
+
*/
|
|
11
29
|
constructor({ agent, logger, packageName, channelName, system }) {
|
|
12
30
|
super({ agent, logger, packageName, channelName })
|
|
13
31
|
this.system = system
|
|
@@ -29,8 +47,12 @@ class DbSubscriber extends Subscriber {
|
|
|
29
47
|
return this.config.datastore_tracer.database_name_reporting.enabled
|
|
30
48
|
}
|
|
31
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Adds `this.parameters` to the active segment.
|
|
52
|
+
* @param {object} segment the current segment
|
|
53
|
+
*/
|
|
32
54
|
addAttributes(segment) {
|
|
33
|
-
for (let [key, value] of Object.entries(this.parameters)) {
|
|
55
|
+
for (let [key, value] of Object.entries(this.parameters ?? {})) {
|
|
34
56
|
if (this.instanceKeys.includes(key) && !this.instanceReporting) {
|
|
35
57
|
continue
|
|
36
58
|
}
|
|
@@ -33,6 +33,16 @@ class ExpressRouteSubscriber extends MiddlewareSubscriber {
|
|
|
33
33
|
// express could have multiple routers, wrap them all
|
|
34
34
|
for (let i = 0; i < routeArgs.length; i++) {
|
|
35
35
|
const routeHandler = routeArgs[i]
|
|
36
|
+
// express supports an array of middlewares as well when defining route
|
|
37
|
+
// wrap each handler and reassign to args
|
|
38
|
+
if (Array.isArray(routeHandler)) {
|
|
39
|
+
for (let j = 0; j < routeHandler.length; j++) {
|
|
40
|
+
const handler = routeHandler[j]
|
|
41
|
+
routeArgs[i][j] = self.wrapper.wrap({ handler })
|
|
42
|
+
}
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
let segmentName = null
|
|
37
47
|
let route = null
|
|
38
48
|
if (routeHandler.stack) {
|
|
@@ -23,12 +23,14 @@ class ExpressUseSubscriber extends MiddlewareSubscriber {
|
|
|
23
23
|
fnIndex = 0
|
|
24
24
|
}
|
|
25
25
|
let segmentName = null
|
|
26
|
+
// sometimes route is undefined, default to `/` for segmentName only
|
|
27
|
+
const routeName = route || '/'
|
|
26
28
|
// Pre v5 these were marked as `lazyrouter`
|
|
27
29
|
// check for both
|
|
28
30
|
if (fn.lazyrouter || fn.name === 'mounted_app') {
|
|
29
|
-
segmentName = `${this.wrapper.system}/Mounted App: ${
|
|
31
|
+
segmentName = `${this.wrapper.system}/Mounted App: ${routeName}`
|
|
30
32
|
} else if (fn.stack) {
|
|
31
|
-
segmentName = `${this.wrapper.system}/Router: ${
|
|
33
|
+
segmentName = `${this.wrapper.system}/Router: ${routeName}`
|
|
32
34
|
method = 'handle'
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -10,10 +10,6 @@ const messageTransactionRecorder = require('#agentlib/metrics/recorders/message-
|
|
|
10
10
|
const isString = require('#agentlib/util/is-string.js')
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* A message consumer does the following:
|
|
14
|
-
* 1. Calling consume creates a segment if in an active transaction
|
|
15
|
-
* 2. For every consumption, typically registered as a callback, it will create a transaction of type `message`, create a baseSegment, add both segment and trace attributes, and assign the `message-transaction` timeslice metrics
|
|
16
|
-
*
|
|
17
13
|
* @typedef {object} MessageConsumerParams
|
|
18
14
|
* @property {object} agent A New Relic Node.js agent instance.
|
|
19
15
|
* @property {object} logger An agent logger instance.
|
|
@@ -26,7 +22,16 @@ const isString = require('#agentlib/util/is-string.js')
|
|
|
26
22
|
* @property {number} callback if consumer is callback based, indicates index of callback
|
|
27
23
|
* @property {string} transport identifier of the transport(see Transaction.TRANSPORT_TYPES)
|
|
28
24
|
*/
|
|
29
|
-
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A message consumer does the following:
|
|
28
|
+
* 1. Calling consume creates a segment if in an active transaction
|
|
29
|
+
* 2. For every consumption, typically registered as a callback, it will create a transaction of type `message`, create a baseSegment, add both segment and trace attributes, and assign the `message-transaction` timeslice metrics
|
|
30
|
+
*/
|
|
31
|
+
class MessageConsumerSubscriber extends Subscriber {
|
|
32
|
+
/**
|
|
33
|
+
* @param {MessageConsumerParams} params constructor params
|
|
34
|
+
*/
|
|
30
35
|
constructor({ agent, logger, packageName, channelName, system, type, callback, transport }) {
|
|
31
36
|
super({ agent, logger, packageName, channelName })
|
|
32
37
|
this.system = system
|
|
@@ -143,4 +148,4 @@ class MessageConsumer extends Subscriber {
|
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
150
|
|
|
146
|
-
module.exports =
|
|
151
|
+
module.exports = MessageConsumerSubscriber
|
|
@@ -8,11 +8,7 @@ const Subscriber = require('./base')
|
|
|
8
8
|
const genericRecorder = require('#agentlib/metrics/recorders/generic.js')
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* Creates the segment for a message producer call. Injects appropriate DT/CAT
|
|
13
|
-
* headers if enabled.
|
|
14
|
-
*
|
|
15
|
-
* @typedef {object} MessageProducerParams
|
|
11
|
+
* @typedef {object} MessageProducerParams
|
|
16
12
|
* @property {object} agent A New Relic Node.js agent instance.
|
|
17
13
|
* @property {object} logger An agent logger instance.
|
|
18
14
|
* @property {string} packageName The package name being instrumented.
|
|
@@ -22,7 +18,15 @@ const genericRecorder = require('#agentlib/metrics/recorders/generic.js')
|
|
|
22
18
|
* @property {string} system canonical mapping of system(i.e. - Kafka, RabbitMq, SNS, SQS)
|
|
23
19
|
* @property {string} type destinationType: Exchange, Queue, Topic
|
|
24
20
|
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates the segment for a message producer call. Injects appropriate DT/CAT
|
|
24
|
+
* headers if enabled.
|
|
25
|
+
*/
|
|
25
26
|
class MessageProducerSubscriber extends Subscriber {
|
|
27
|
+
/**
|
|
28
|
+
* @param {MessageProducerParams} params constructor params
|
|
29
|
+
*/
|
|
26
30
|
constructor({ agent, logger, packageName, channelName, system, type }) {
|
|
27
31
|
super({ agent, logger, packageName, channelName })
|
|
28
32
|
this.system = system
|
|
@@ -7,9 +7,6 @@
|
|
|
7
7
|
const Subscriber = require('./base')
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Used to subscribe to many different events that have the same behavior around
|
|
11
|
-
* segment creation.
|
|
12
|
-
*
|
|
13
10
|
* @typedef {object} MetaSubscriberParams
|
|
14
11
|
* @property {object} agent A New Relic Node.js agent instance.
|
|
15
12
|
* @property {object} logger An agent logger instance.
|
|
@@ -17,12 +14,22 @@ const Subscriber = require('./base')
|
|
|
17
14
|
* This is what a developer would provide to the `require` function.
|
|
18
15
|
* @property {string} channelName A unique name for the diagnostics channel
|
|
19
16
|
* that will be created and monitored.
|
|
20
|
-
* @property {
|
|
21
|
-
* @property {
|
|
22
|
-
* @property {number} callback if consumer is callback based, indicates index of callback
|
|
17
|
+
* @property {string[]} channels list of channels to construct new subscribers
|
|
18
|
+
* @property {string[]} events list of events to subscribe to new subscribers
|
|
19
|
+
* @property {number} [callback=null] if consumer is callback based, indicates index of callback
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Used to subscribe to many different events that have the same behavior around
|
|
24
|
+
* segment creation.
|
|
25
|
+
*
|
|
26
|
+
* @example - ./amqplib/channel-model.js
|
|
23
27
|
*/
|
|
24
28
|
class MetaSubscriber {
|
|
25
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @param {MetaSubscriberParams} params constructor params
|
|
31
|
+
*/
|
|
32
|
+
constructor({ agent, logger, packageName, channelName, channels, events, callback = null }) {
|
|
26
33
|
this.config = agent.config
|
|
27
34
|
this.packageName = packageName
|
|
28
35
|
this.id = `orchestrion:${packageName}:${channelName}`
|
|
@@ -59,8 +59,17 @@ class MiddlewareWrapper {
|
|
|
59
59
|
*/
|
|
60
60
|
wrap({ handler, prefix, route, segmentName, nextIdx = -1 }) {
|
|
61
61
|
const self = this
|
|
62
|
+
if (typeof handler !== 'function') {
|
|
63
|
+
this.logger.trace('Handler: %s is not a function, not wrapping.', handler)
|
|
64
|
+
return handler
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
function wrappedHandler (...args) {
|
|
63
68
|
const ctx = self.agent.tracer.getContext()
|
|
69
|
+
if (ctx?.transaction?.isActive() !== true) {
|
|
70
|
+
self.logger.trace('No active transaction, calling original function')
|
|
71
|
+
return handler.apply(this, args)
|
|
72
|
+
}
|
|
64
73
|
const transaction = ctx?.transaction
|
|
65
74
|
transaction.nameState.setPrefix(self.system)
|
|
66
75
|
const { txInfo, errorWare, request } = self.extractTxInfo(args, route)
|
|
@@ -8,10 +8,21 @@ const Subscriber = require('./base')
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* This subscriber is purely for propagation of async context within the `asyncStart` event.
|
|
11
|
-
* This will be typically used in a callback based scenario where there is async code getting
|
|
12
|
-
* but not bound to the context.
|
|
11
|
+
* This will be typically used in a callback based scenario where there is async code getting
|
|
12
|
+
* scheduled but not bound to the context.
|
|
13
13
|
*/
|
|
14
14
|
class PropagationSubscriber extends Subscriber {
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @param {object} params constructor params
|
|
18
|
+
* @param {object} params.agent A New Relic Node.js agent instance.
|
|
19
|
+
* @param {object} params.logger An agent logger instance.
|
|
20
|
+
* @param {string} params.packageName The package name being instrumented.
|
|
21
|
+
* This is what a developer would provide to the `require` function.
|
|
22
|
+
* @param {string} params.channelName A unique name for the diagnostics channel
|
|
23
|
+
* that will be created and monitored.
|
|
24
|
+
* @param {number} params.callback position of callback if it needs to be wrapped for instrumentation. -1 means last argument
|
|
25
|
+
*/
|
|
15
26
|
constructor({ agent, logger, packageName, channelName, callback }) {
|
|
16
27
|
super({ agent, logger, packageName, channelName })
|
|
17
28
|
this.callback = callback
|