newrelic 9.7.0 → 9.7.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 +42 -18
- package/THIRD_PARTY_NOTICES.md +203 -2
- package/api.js +155 -103
- package/lib/agent.js +29 -82
- package/lib/collector/api.js +234 -201
- package/lib/config/attribute-filter.js +35 -25
- package/lib/config/default.js +45 -19
- package/lib/config/index.js +260 -199
- package/lib/environment.js +16 -12
- package/lib/errors/error-collector.js +124 -55
- package/lib/errors/index.js +59 -49
- package/lib/instrumentation/@hapi/hapi.js +56 -50
- package/lib/instrumentation/@node-redis/client.js +50 -41
- package/lib/instrumentation/amqplib.js +116 -151
- package/lib/instrumentation/core/{async_hooks.js → async-hooks.js} +26 -11
- package/lib/instrumentation/core/globals.js +1 -1
- package/lib/instrumentation/core/http-outbound.js +193 -78
- package/lib/instrumentation/core/timers.js +106 -59
- package/lib/instrumentation/grpc-js/grpc.js +20 -23
- package/lib/instrumentation/mongodb/common.js +87 -85
- package/lib/instrumentation/redis.js +112 -90
- package/lib/instrumentation/undici.js +204 -192
- package/lib/instrumentation/{when.js → when/constants.js} +13 -10
- package/lib/instrumentation/when/contextualizer.js +168 -0
- package/lib/instrumentation/when/index.js +354 -0
- package/lib/instrumentation/when/nr-hooks.js +15 -0
- package/lib/instrumentations.js +1 -1
- package/lib/shim/shim.js +2 -0
- package/lib/shim/webframework-shim.js +19 -0
- package/lib/symbols.js +1 -0
- package/lib/system-info.js +240 -163
- package/lib/util/async-each-limit.js +30 -0
- package/lib/util/attributes.js +159 -0
- package/lib/util/code-level-metrics.js +58 -0
- package/lib/util/deep-equal.js +11 -144
- package/package.json +5 -4
- package/lib/instrumentation/promise.js +0 -572
|
@@ -3,61 +3,70 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 20] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
7
|
-
|
|
8
6
|
'use strict'
|
|
9
7
|
const CLIENT_COMMANDS = ['select', 'quit', 'SELECT', 'QUIT']
|
|
8
|
+
const opts = Symbol('clientOptions')
|
|
10
9
|
|
|
11
|
-
module.exports = function initialize(
|
|
10
|
+
module.exports = function initialize(_agent, redis, _moduleName, shim) {
|
|
12
11
|
shim.setDatastore(shim.REDIS)
|
|
13
12
|
const COMMANDS = Object.keys(shim.require('dist/lib/client/commands.js').default)
|
|
14
13
|
const CMDS_TO_INSTRUMENT = [...COMMANDS, ...CLIENT_COMMANDS]
|
|
15
14
|
shim.wrap(redis, 'createClient', function wrapCreateClient(shim, original) {
|
|
16
15
|
return function wrappedCreateClient() {
|
|
17
16
|
const client = original.apply(this, arguments)
|
|
18
|
-
|
|
19
|
-
CMDS_TO_INSTRUMENT.forEach((
|
|
20
|
-
shim.recordOperation(client, cmd, function wrapCommand(shim, fn, fnName, args) {
|
|
21
|
-
const [key, value] = args
|
|
22
|
-
const parameters = Object.assign({}, clientOptions)
|
|
23
|
-
// If selecting a database, subsequent commands
|
|
24
|
-
// will be using said database, update the clientOptions
|
|
25
|
-
// but not the current parameters(feature parity with v3)
|
|
26
|
-
if (cmd.toLowerCase() === 'select') {
|
|
27
|
-
clientOptions.database_name = key
|
|
28
|
-
}
|
|
29
|
-
if (agent.config.attributes.enabled) {
|
|
30
|
-
if (key) {
|
|
31
|
-
parameters.key = JSON.stringify(key)
|
|
32
|
-
}
|
|
33
|
-
if (value) {
|
|
34
|
-
parameters.value = JSON.stringify(value)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
name: (cmd && cmd.toLowerCase()) || 'other',
|
|
40
|
-
parameters,
|
|
41
|
-
promise: true
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
17
|
+
client[opts] = getRedisParams(client.options)
|
|
18
|
+
CMDS_TO_INSTRUMENT.forEach(instrumentClientCommand.bind(null, shim, client))
|
|
46
19
|
return client
|
|
47
20
|
}
|
|
48
21
|
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Instruments a given command on the client by calling `shim.recordOperation`
|
|
26
|
+
*
|
|
27
|
+
* @param {Shim} shim shim instance
|
|
28
|
+
* @param {object} client redis client instance
|
|
29
|
+
* @param {string} cmd command to instrument
|
|
30
|
+
*/
|
|
31
|
+
function instrumentClientCommand(shim, client, cmd) {
|
|
32
|
+
const { agent } = shim
|
|
33
|
+
|
|
34
|
+
shim.recordOperation(client, cmd, function wrapCommand(_shim, _fn, _fnName, args) {
|
|
35
|
+
const [key, value] = args
|
|
36
|
+
const parameters = Object.assign({}, client[opts])
|
|
37
|
+
// If selecting a database, subsequent commands
|
|
38
|
+
// will be using said database, update the clientOptions
|
|
39
|
+
// but not the current parameters(feature parity with v3)
|
|
40
|
+
if (cmd.toLowerCase() === 'select') {
|
|
41
|
+
client[opts].database_name = key
|
|
42
|
+
}
|
|
43
|
+
if (agent.config.attributes.enabled) {
|
|
44
|
+
if (key) {
|
|
45
|
+
parameters.key = JSON.stringify(key)
|
|
46
|
+
}
|
|
47
|
+
if (value) {
|
|
48
|
+
parameters.value = JSON.stringify(value)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
49
51
|
|
|
50
|
-
/**
|
|
51
|
-
* Extracts the datastore parameters from the client options
|
|
52
|
-
*
|
|
53
|
-
* @param {object} opts client.options
|
|
54
|
-
* @returns {object} params
|
|
55
|
-
*/
|
|
56
|
-
function getRedisParams(opts) {
|
|
57
52
|
return {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
name: (cmd && cmd.toLowerCase()) || 'other',
|
|
54
|
+
parameters,
|
|
55
|
+
promise: true
|
|
61
56
|
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extracts the datastore parameters from the client options
|
|
62
|
+
*
|
|
63
|
+
* @param {object} clientOpts client.options
|
|
64
|
+
* @returns {object} params
|
|
65
|
+
*/
|
|
66
|
+
function getRedisParams(clientOpts) {
|
|
67
|
+
return {
|
|
68
|
+
host: clientOpts?.socket?.host || 'localhost',
|
|
69
|
+
port_path_or_id: clientOpts?.socket?.path || clientOpts?.socket?.port || '6379',
|
|
70
|
+
database_name: clientOpts.database || 0
|
|
62
71
|
}
|
|
63
72
|
}
|
|
@@ -5,17 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 16] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252*/
|
|
9
|
-
|
|
10
8
|
const url = require('url')
|
|
11
9
|
|
|
12
|
-
// TODO: Make this an external module.
|
|
13
|
-
// var newrelic = require('newrelic')
|
|
14
|
-
// newrelic.instrumentMessages('amqplib', instrumentChannelAPI)
|
|
15
|
-
// newrelic.instrumentMessages('amqplib/channel_api', instrumentChannelAPI)
|
|
16
|
-
// newrelic.instrumentMessages('amqplib/channel_api.js', instrumentChannelAPI)
|
|
17
|
-
// newrelic.instrumentMessages('amqplib/callback_api', instrumentCallbackAPI)
|
|
18
|
-
// newrelic.instrumentMessages('amqplib/callback_api.js', instrumentCallbackAPI)
|
|
19
10
|
module.exports.selfRegister = function selfRegister(shimmer) {
|
|
20
11
|
shimmer.registerInstrumentation({
|
|
21
12
|
moduleName: 'amqplib',
|
|
@@ -67,20 +58,47 @@ const CHANNEL_METHODS = [
|
|
|
67
58
|
|
|
68
59
|
const TEMP_RE = /^amq\./
|
|
69
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Register all the necessary instrumentation when using
|
|
63
|
+
* promise based methods
|
|
64
|
+
*
|
|
65
|
+
* @param {Shim} shim instance of shim
|
|
66
|
+
* @param {object} amqp amqplib object
|
|
67
|
+
*/
|
|
70
68
|
function instrumentChannelAPI(shim, amqp) {
|
|
71
69
|
instrumentAMQP(shim, amqp, true)
|
|
72
|
-
|
|
70
|
+
// 👀 take note the model is channel not callback 👀
|
|
71
|
+
const model = shim.require('./lib/channel_model')
|
|
72
|
+
wrapModel(shim, model, true)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Register all the necessary instrumentation when using
|
|
77
|
+
* callback based methods
|
|
78
|
+
*
|
|
79
|
+
* @param {Shim} shim instance of shim
|
|
80
|
+
* @param {object} amqp amqplib object
|
|
81
|
+
*/
|
|
75
82
|
function instrumentCallbackAPI(shim, amqp) {
|
|
76
83
|
instrumentAMQP(shim, amqp, false)
|
|
77
|
-
|
|
84
|
+
// 👀 take note the model is callback not channel 👀
|
|
85
|
+
const model = shim.require('./lib/callback_model')
|
|
86
|
+
wrapModel(shim, model, false)
|
|
78
87
|
}
|
|
79
88
|
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
* Instruments the connect method and channel prototype of amqplib
|
|
92
|
+
*
|
|
93
|
+
* @param {Shim} shim instance of shim
|
|
94
|
+
* @param {object} amqp amqplib object
|
|
95
|
+
* @param {boolean} promiseMode is this promise based?
|
|
96
|
+
* @returns {void}
|
|
97
|
+
*/
|
|
80
98
|
function instrumentAMQP(shim, amqp, promiseMode) {
|
|
81
99
|
if (!amqp || !amqp.connect) {
|
|
82
100
|
shim.logger.debug("This module is not the amqplib we're looking for.")
|
|
83
|
-
return
|
|
101
|
+
return
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
if (shim.isWrapped(amqp.connect)) {
|
|
@@ -89,8 +107,33 @@ function instrumentAMQP(shim, amqp, promiseMode) {
|
|
|
89
107
|
}
|
|
90
108
|
shim.setLibrary(shim.RABBITMQ)
|
|
91
109
|
|
|
110
|
+
wrapConnect(shim, amqp, promiseMode)
|
|
111
|
+
wrapChannel(shim)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Helper to set the appropriate value of the callback property
|
|
116
|
+
* in the spec. If it's a promise set to null otherwise set it to `shim.LAST`
|
|
117
|
+
*
|
|
118
|
+
* @param {Shim} shim instance of shim
|
|
119
|
+
* @param {boolean} promiseMode is this promise based?
|
|
120
|
+
* @returns {string|null} appropriate value
|
|
121
|
+
*/
|
|
122
|
+
function setCallback(shim, promiseMode) {
|
|
123
|
+
return promiseMode ? null : shim.LAST
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
*
|
|
128
|
+
* Instruments the connect method
|
|
129
|
+
*
|
|
130
|
+
* @param {Shim} shim instance of shim
|
|
131
|
+
* @param {object} amqp amqplib object
|
|
132
|
+
* @param {boolean} promiseMode is this promise based?
|
|
133
|
+
*/
|
|
134
|
+
function wrapConnect(shim, amqp, promiseMode) {
|
|
92
135
|
shim.record(amqp, 'connect', function recordConnect(shim, connect, name, args) {
|
|
93
|
-
let connArgs = args
|
|
136
|
+
let [connArgs] = args
|
|
94
137
|
let params = null
|
|
95
138
|
|
|
96
139
|
if (shim.isString(connArgs)) {
|
|
@@ -103,21 +146,24 @@ function instrumentAMQP(shim, amqp, promiseMode) {
|
|
|
103
146
|
|
|
104
147
|
return {
|
|
105
148
|
name: 'amqplib.connect',
|
|
106
|
-
callback:
|
|
149
|
+
callback: setCallback(shim, promiseMode),
|
|
107
150
|
promise: promiseMode,
|
|
108
151
|
parameters: params,
|
|
109
|
-
|
|
110
152
|
stream: null,
|
|
111
153
|
recorder: null
|
|
112
154
|
}
|
|
113
155
|
})
|
|
114
|
-
|
|
115
|
-
wrapChannel(shim)
|
|
116
156
|
}
|
|
117
157
|
|
|
158
|
+
/**
|
|
159
|
+
*
|
|
160
|
+
* Instruments the sendOrEnqueue and sendMessage methods of the ampqlib channel.
|
|
161
|
+
*
|
|
162
|
+
* @param {Shim} shim instance of shim
|
|
163
|
+
*/
|
|
118
164
|
function wrapChannel(shim) {
|
|
119
165
|
const libChannel = shim.require('./lib/channel')
|
|
120
|
-
if (!libChannel
|
|
166
|
+
if (!libChannel?.Channel?.prototype) {
|
|
121
167
|
shim.logger.debug('Could not get Channel class to instrument.')
|
|
122
168
|
return
|
|
123
169
|
}
|
|
@@ -149,29 +195,7 @@ function wrapChannel(shim) {
|
|
|
149
195
|
}
|
|
150
196
|
})
|
|
151
197
|
|
|
152
|
-
|
|
153
|
-
// { exchange: 'test-exchange-topic',
|
|
154
|
-
// routingKey: 'routing.key',
|
|
155
|
-
// mandatory: false,
|
|
156
|
-
// immediate: false,
|
|
157
|
-
// ticket: undefined,
|
|
158
|
-
// contentType: undefined,
|
|
159
|
-
// contentEncoding: undefined,
|
|
160
|
-
// headers: {},
|
|
161
|
-
// deliveryMode: undefined,
|
|
162
|
-
// priority: undefined,
|
|
163
|
-
// correlationId: undefined,
|
|
164
|
-
// replyTo: undefined,
|
|
165
|
-
// expiration: undefined,
|
|
166
|
-
// messageId: undefined,
|
|
167
|
-
// timestamp: undefined,
|
|
168
|
-
// type: undefined,
|
|
169
|
-
// userId: undefined,
|
|
170
|
-
// appId: undefined,
|
|
171
|
-
// clusterId: undefined }
|
|
172
|
-
|
|
173
|
-
shim.recordProduce(proto, 'sendMessage', recordSendMessage)
|
|
174
|
-
function recordSendMessage(shim, fn, n, args) {
|
|
198
|
+
shim.recordProduce(proto, 'sendMessage', function recordSendMessage(shim, fn, n, args) {
|
|
175
199
|
const fields = args[0]
|
|
176
200
|
if (!fields) {
|
|
177
201
|
return null
|
|
@@ -189,9 +213,16 @@ function wrapChannel(shim) {
|
|
|
189
213
|
headers: fields.headers,
|
|
190
214
|
parameters: getParameters(Object.create(null), fields)
|
|
191
215
|
}
|
|
192
|
-
}
|
|
216
|
+
})
|
|
193
217
|
}
|
|
194
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Sets the relevant message parameters
|
|
221
|
+
*
|
|
222
|
+
* @param {object} parameters object used to store the message parameters
|
|
223
|
+
* @param {object} fields fields from the sendMessage method
|
|
224
|
+
* @returns {object} parameters updated parameters
|
|
225
|
+
*/
|
|
195
226
|
function getParameters(parameters, fields) {
|
|
196
227
|
if (fields.routingKey) {
|
|
197
228
|
parameters.routing_key = fields.routingKey
|
|
@@ -206,29 +237,42 @@ function getParameters(parameters, fields) {
|
|
|
206
237
|
return parameters
|
|
207
238
|
}
|
|
208
239
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
240
|
+
/**
|
|
241
|
+
*
|
|
242
|
+
* Instruments the relevant channel callback_model or channel_model.
|
|
243
|
+
*
|
|
244
|
+
* @param {Shim} shim instance of shim
|
|
245
|
+
* @param {object} Model either channel or callback model
|
|
246
|
+
* @param {boolean} promiseMode is this promise based?
|
|
247
|
+
*/
|
|
248
|
+
function wrapModel(shim, Model, promiseMode) {
|
|
249
|
+
if (!Model.Channel?.prototype) {
|
|
250
|
+
shim.logger.debug(
|
|
251
|
+
`Could not get ${promiseMode ? 'promise' : 'callback'} model Channel to instrument.`
|
|
252
|
+
)
|
|
213
253
|
}
|
|
214
254
|
|
|
215
|
-
const proto =
|
|
255
|
+
const proto = Model.Channel.prototype
|
|
216
256
|
if (shim.isWrapped(proto.consume)) {
|
|
217
|
-
shim.logger.trace('
|
|
257
|
+
shim.logger.trace(`${promiseMode ? 'promise' : 'callback'} model already instrumented.`)
|
|
218
258
|
return
|
|
219
259
|
}
|
|
220
260
|
|
|
221
261
|
shim.record(proto, CHANNEL_METHODS, function recordChannelMethod(shim, fn, name) {
|
|
222
262
|
return {
|
|
223
263
|
name: 'Channel#' + name,
|
|
224
|
-
|
|
264
|
+
callback: setCallback(shim, promiseMode),
|
|
265
|
+
promise: promiseMode
|
|
225
266
|
}
|
|
226
267
|
})
|
|
227
268
|
|
|
228
269
|
shim.recordConsume(proto, 'get', {
|
|
229
270
|
destinationName: shim.FIRST,
|
|
230
|
-
|
|
271
|
+
callback: setCallback(shim, promiseMode),
|
|
272
|
+
promise: promiseMode,
|
|
231
273
|
messageHandler: function handleConsumedMessage(shim, fn, name, message) {
|
|
274
|
+
// the message is the param when using the promised based model
|
|
275
|
+
message = promiseMode ? message : message[1]
|
|
232
276
|
if (!message) {
|
|
233
277
|
shim.logger.trace('No results from consume.')
|
|
234
278
|
return null
|
|
@@ -237,95 +281,9 @@ function wrapPromiseChannel(shim) {
|
|
|
237
281
|
getParameters(parameters, message.fields)
|
|
238
282
|
getParameters(parameters, message.properties)
|
|
239
283
|
|
|
240
|
-
|
|
241
|
-
if (message.properties && message.properties.headers) {
|
|
242
|
-
headers = message.properties.headers
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return { parameters: parameters, headers: headers }
|
|
246
|
-
}
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
shim.recordPurgeQueue(proto, 'purgeQueue', function recordPurge(shim, fn, name, args) {
|
|
250
|
-
let queue = args[0] || null
|
|
251
|
-
if (TEMP_RE.test(queue)) {
|
|
252
|
-
queue = null
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return { queue: queue, promise: true }
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
shim.recordSubscribedConsume(proto, 'consume', {
|
|
259
|
-
name: 'amqplib.Channel#consume',
|
|
260
|
-
queue: shim.FIRST,
|
|
261
|
-
consumer: shim.SECOND,
|
|
262
|
-
promise: true,
|
|
263
|
-
messageHandler: describeMessage
|
|
264
|
-
})
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function wrapCallbackChannel(shim) {
|
|
268
|
-
const libCbModel = shim.require('./lib/callback_model')
|
|
269
|
-
if (!libCbModel || !libCbModel.Channel || !libCbModel.Channel.prototype) {
|
|
270
|
-
shim.logger.debug('Could not get callback model Channel to instrument')
|
|
271
|
-
return
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const proto = libCbModel.Channel.prototype
|
|
275
|
-
if (shim.isWrapped(proto.consume)) {
|
|
276
|
-
return
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Example message:
|
|
280
|
-
// { fields:
|
|
281
|
-
// { consumerTag: 'amq.ctag-8oZE10ovvyAP8e-vgbOnSA',
|
|
282
|
-
// deliveryTag: 1,
|
|
283
|
-
// redelivered: false,
|
|
284
|
-
// exchange: 'test-exchange-topic',
|
|
285
|
-
// routingKey: 'routing.key' },
|
|
286
|
-
// properties:
|
|
287
|
-
// { contentType: undefined,
|
|
288
|
-
// contentEncoding: undefined,
|
|
289
|
-
// headers: {},
|
|
290
|
-
// deliveryMode: undefined,
|
|
291
|
-
// priority: undefined,
|
|
292
|
-
// correlationId: undefined,
|
|
293
|
-
// replyTo: undefined,
|
|
294
|
-
// expiration: undefined,
|
|
295
|
-
// messageId: undefined,
|
|
296
|
-
// timestamp: undefined,
|
|
297
|
-
// type: undefined,
|
|
298
|
-
// userId: undefined,
|
|
299
|
-
// appId: undefined,
|
|
300
|
-
// clusterId: undefined },
|
|
301
|
-
// content: Buffer [ 97 ] }
|
|
302
|
-
|
|
303
|
-
shim.record(proto, CHANNEL_METHODS, function recordChannelMethod(shim, fn, name) {
|
|
304
|
-
return {
|
|
305
|
-
name: 'Channel#' + name,
|
|
306
|
-
callback: shim.LAST
|
|
307
|
-
}
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
shim.recordConsume(proto, 'get', {
|
|
311
|
-
destinationName: shim.FIRST,
|
|
312
|
-
callback: shim.LAST,
|
|
313
|
-
messageHandler: function handleConsumedMessage(shim, fn, name, args) {
|
|
314
|
-
const message = args[1]
|
|
315
|
-
if (!message) {
|
|
316
|
-
shim.logger.trace('No results from consume.')
|
|
317
|
-
return null
|
|
318
|
-
}
|
|
319
|
-
const parameters = Object.create(null)
|
|
320
|
-
getParameters(parameters, message.fields)
|
|
321
|
-
getParameters(parameters, message.properties)
|
|
322
|
-
|
|
323
|
-
let headers = null
|
|
324
|
-
if (message.properties && message.properties.headers) {
|
|
325
|
-
headers = message.properties.headers
|
|
326
|
-
}
|
|
284
|
+
const headers = message?.properties?.headers
|
|
327
285
|
|
|
328
|
-
return { parameters
|
|
286
|
+
return { parameters, headers }
|
|
329
287
|
}
|
|
330
288
|
})
|
|
331
289
|
|
|
@@ -334,42 +292,49 @@ function wrapCallbackChannel(shim) {
|
|
|
334
292
|
if (TEMP_RE.test(queue)) {
|
|
335
293
|
queue = null
|
|
336
294
|
}
|
|
337
|
-
|
|
338
|
-
return { queue: queue, callback: shim.LAST }
|
|
295
|
+
return { queue, promise: promiseMode, callback: setCallback(shim, promiseMode) }
|
|
339
296
|
})
|
|
340
297
|
|
|
341
298
|
shim.recordSubscribedConsume(proto, 'consume', {
|
|
342
299
|
name: 'amqplib.Channel#consume',
|
|
343
300
|
queue: shim.FIRST,
|
|
344
301
|
consumer: shim.SECOND,
|
|
345
|
-
|
|
346
|
-
|
|
302
|
+
promise: promiseMode,
|
|
303
|
+
callback: promiseMode ? null : shim.FOURTH,
|
|
347
304
|
messageHandler: describeMessage
|
|
348
305
|
})
|
|
349
306
|
}
|
|
350
307
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Extracts the appropriate messageHandler parameters for the consume method.
|
|
310
|
+
*
|
|
311
|
+
* @param {Shim} shim instance of shim
|
|
312
|
+
* @param {object} _consumer not used
|
|
313
|
+
* @param {string} _name not used
|
|
314
|
+
* @param {Array} args arguments passed to the consume method
|
|
315
|
+
* @returns {object} message params
|
|
316
|
+
*/
|
|
317
|
+
function describeMessage(shim, _consumer, _name, args) {
|
|
318
|
+
const [message] = args
|
|
319
|
+
|
|
320
|
+
if (!message?.properties) {
|
|
354
321
|
shim.logger.debug({ message: message }, 'Failed to find message in consume arguments.')
|
|
355
322
|
return null
|
|
356
323
|
}
|
|
357
324
|
|
|
358
|
-
let exchangeName = message.fields.exchange
|
|
359
325
|
const parameters = getParameters(Object.create(null), message.fields)
|
|
360
326
|
getParameters(parameters, message.properties)
|
|
327
|
+
let exchangeName = message?.fields?.exchange || 'Default'
|
|
361
328
|
|
|
362
|
-
if (
|
|
363
|
-
exchangeName = 'Default'
|
|
364
|
-
} else if (TEMP_RE.test(exchangeName)) {
|
|
329
|
+
if (TEMP_RE.test(exchangeName)) {
|
|
365
330
|
exchangeName = null
|
|
366
331
|
}
|
|
367
332
|
|
|
368
333
|
return {
|
|
369
334
|
destinationName: exchangeName,
|
|
370
335
|
destinationType: shim.EXCHANGE,
|
|
371
|
-
routingKey: message
|
|
336
|
+
routingKey: message?.fields?.routingKey,
|
|
372
337
|
headers: message.properties.headers,
|
|
373
|
-
parameters
|
|
338
|
+
parameters
|
|
374
339
|
}
|
|
375
340
|
}
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 18] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
|
-
|
|
10
8
|
const logger = require('../../logger').child({ component: 'async_hooks' })
|
|
11
9
|
const asyncHooks = require('async_hooks')
|
|
12
10
|
|
|
@@ -26,7 +24,8 @@ function initialize(agent, shim) {
|
|
|
26
24
|
const segmentMap = new Map()
|
|
27
25
|
module.exports.segmentMap = segmentMap
|
|
28
26
|
|
|
29
|
-
const hookHandlers =
|
|
27
|
+
const hookHandlers = getHookHandlers(segmentMap, agent, shim)
|
|
28
|
+
maybeRegisterDestroyHook(segmentMap, agent, hookHandlers)
|
|
30
29
|
|
|
31
30
|
const hook = asyncHooks.createHook(hookHandlers)
|
|
32
31
|
hook.enable()
|
|
@@ -38,8 +37,18 @@ function initialize(agent, shim) {
|
|
|
38
37
|
return true
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Registers the async hooks events
|
|
42
|
+
*
|
|
43
|
+
* Note: The init only fires when the type is PROMISE.
|
|
44
|
+
*
|
|
45
|
+
* @param {Map} segmentMap map of async ids and segments
|
|
46
|
+
* @param {Agent} agent New Relic APM agent
|
|
47
|
+
* @param {Shim} shim instance of shim
|
|
48
|
+
* @returns {object} event handlers for async hooks
|
|
49
|
+
*/
|
|
50
|
+
function getHookHandlers(segmentMap, agent, shim) {
|
|
51
|
+
return {
|
|
43
52
|
init: function initHook(id, type, triggerId) {
|
|
44
53
|
if (type !== 'PROMISE') {
|
|
45
54
|
return
|
|
@@ -101,17 +110,23 @@ function getPromiseResolveStyleHooks(segmentMap, agent, shim) {
|
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
}
|
|
113
|
+
}
|
|
104
114
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Adds the destroy async hook event that will lean up any unresolved promises that have been destroyed.
|
|
117
|
+
* This defaults to true but does have a significant performance impact
|
|
118
|
+
* when customers have a lot of promises.
|
|
119
|
+
* See: https://github.com/newrelic/node-newrelic/issues/760
|
|
120
|
+
*
|
|
121
|
+
* @param {Map} segmentMap map of async ids and segments
|
|
122
|
+
* @param {Agent} agent New Relic APM agent
|
|
123
|
+
* @param {object} hooks async-hook events
|
|
124
|
+
*/
|
|
125
|
+
function maybeRegisterDestroyHook(segmentMap, agent, hooks) {
|
|
109
126
|
if (agent.config.feature_flag.unresolved_promise_cleanup) {
|
|
110
127
|
logger.info('Adding destroy hook to clean up unresolved promises.')
|
|
111
128
|
hooks.destroy = function destroyHandler(id) {
|
|
112
129
|
segmentMap.delete(id)
|
|
113
130
|
}
|
|
114
131
|
}
|
|
115
|
-
|
|
116
|
-
return hooks
|
|
117
132
|
}
|