newrelic 8.5.0 → 8.7.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 +79 -0
- package/README.md +7 -6
- package/THIRD_PARTY_NOTICES.md +33 -2
- package/api.js +103 -136
- package/lib/agent.js +11 -2
- package/lib/config/index.js +11 -8
- package/lib/context-manager/create-context-manager.js +32 -0
- package/lib/context-manager/diagnostics/legacy-diagnostic-context-manager.js +36 -0
- package/lib/context-manager/legacy-context-manager.js +66 -0
- package/lib/instrumentation/mongodb/common.js +12 -4
- package/lib/instrumentation/promise.js +28 -41
- package/lib/metrics/names.js +5 -1
- package/lib/parse-proc-cpuinfo.js +8 -2
- package/lib/parse-proc-meminfo.js +6 -0
- package/lib/serverless/aws-lambda.js +8 -2
- package/lib/shim/message-shim.js +45 -90
- package/lib/shim/shim.js +139 -324
- package/lib/shim/transaction-shim.js +9 -39
- package/lib/shimmer.js +171 -11
- package/lib/transaction/tracer/index.js +22 -29
- package/package.json +7 -4
|
@@ -16,15 +16,13 @@ const TRANSACTION_TYPES_SET = Transaction.TYPES_SET
|
|
|
16
16
|
/**
|
|
17
17
|
* Constructs a transaction managing shim.
|
|
18
18
|
*
|
|
19
|
-
* @
|
|
20
|
-
* @
|
|
19
|
+
* @class
|
|
20
|
+
* @augments Shim
|
|
21
21
|
* @classdesc
|
|
22
22
|
* A helper class for working with transactions.
|
|
23
|
-
*
|
|
24
23
|
* @param {Agent} agent - The agent the shim will use.
|
|
25
24
|
* @param {string} moduleName - The name of the module being instrumented.
|
|
26
25
|
* @param {string} resolvedName - The full path to the loaded module.
|
|
27
|
-
*
|
|
28
26
|
* @see Shim
|
|
29
27
|
* @see WebFrameworkShim
|
|
30
28
|
*/
|
|
@@ -75,22 +73,18 @@ TransactionShim.prototype.insertCATRequestHeaders = insertCATRequestHeaders
|
|
|
75
73
|
|
|
76
74
|
/**
|
|
77
75
|
* @interface TransactionSpec
|
|
78
|
-
*
|
|
79
76
|
* @description
|
|
80
77
|
* Describes the type of transaction to be created by the function being
|
|
81
78
|
* wrapped by {@link Shim#bindCreateTransaction}.
|
|
82
|
-
*
|
|
83
79
|
* @property {string} type
|
|
84
80
|
* The type of transaction to create. Must be one of the values from
|
|
85
81
|
* {@link Shim#TRANSACTION_TYPES}.
|
|
86
|
-
*
|
|
87
82
|
* @property {bool} [nest=false]
|
|
88
83
|
* Indicates if the transaction being created is allowed to be nested within
|
|
89
84
|
* another transaction of the same type. If `false`, the default, the transaction
|
|
90
85
|
* will only be created if there is no existing transaction, or the current
|
|
91
86
|
* transaction is of a different type. If `true`, the transaction will be
|
|
92
87
|
* created regardless of the current transaction's type.
|
|
93
|
-
*
|
|
94
88
|
* @see Shim#bindCreateTransaction
|
|
95
89
|
* @see Shim#TRANSACTION_TYPES
|
|
96
90
|
*/
|
|
@@ -105,18 +99,14 @@ TransactionShim.prototype.insertCATRequestHeaders = insertCATRequestHeaders
|
|
|
105
99
|
* - `bindCreateTransaction(func, spec)`
|
|
106
100
|
*
|
|
107
101
|
* @memberof TransactionShim.prototype
|
|
108
|
-
*
|
|
109
|
-
* @param {Object|Function} nodule
|
|
102
|
+
* @param {object | Function} nodule
|
|
110
103
|
* The source for the property to wrap, or a single function to wrap.
|
|
111
|
-
*
|
|
112
104
|
* @param {string} [property]
|
|
113
105
|
* The property to wrap. If omitted, the `nodule` parameter is assumed to be
|
|
114
106
|
* the function to wrap.
|
|
115
|
-
*
|
|
116
107
|
* @param {TransactionSpec} spec
|
|
117
108
|
* The spec for creating the transaction.
|
|
118
|
-
*
|
|
119
|
-
* @return {Object|Function} The first parameter to this function, after
|
|
109
|
+
* @returns {object | Function} The first parameter to this function, after
|
|
120
110
|
* wrapping it or its property.
|
|
121
111
|
*/
|
|
122
112
|
function bindCreateTransaction(nodule, property, spec) {
|
|
@@ -164,7 +154,6 @@ function bindCreateTransaction(nodule, property, spec) {
|
|
|
164
154
|
* back off when resolution exits the item.
|
|
165
155
|
*
|
|
166
156
|
* @memberof TransactionShim.prototype
|
|
167
|
-
*
|
|
168
157
|
* @param {string} pathSegment - The path segment to add to the naming stack.
|
|
169
158
|
*/
|
|
170
159
|
function pushTransactionName(pathSegment) {
|
|
@@ -183,7 +172,6 @@ function pushTransactionName(pathSegment) {
|
|
|
183
172
|
* this function, but we do not live in an ideal world.
|
|
184
173
|
*
|
|
185
174
|
* @memberof TransactionShim.prototype
|
|
186
|
-
*
|
|
187
175
|
* @param {string} [pathSegment]
|
|
188
176
|
* Optional. Path segment to pop the stack repeatedly until a segment matching
|
|
189
177
|
* `pathSegment` is removed.
|
|
@@ -203,7 +191,6 @@ function popTransactionName(pathSegment) {
|
|
|
203
191
|
* Either this _or_ the naming stack should be used. Do not use them together.
|
|
204
192
|
*
|
|
205
193
|
* @memberof TransactionShim.prototype
|
|
206
|
-
*
|
|
207
194
|
* @param {string} name - The name to use for the transaction.
|
|
208
195
|
*/
|
|
209
196
|
function setTransactionName(name) {
|
|
@@ -222,14 +209,11 @@ function setTransactionName(name) {
|
|
|
222
209
|
*
|
|
223
210
|
* This will check for either header naming style, and both request and reply
|
|
224
211
|
* CAT headers.
|
|
225
|
-
*
|
|
226
212
|
* @param {object} headers
|
|
227
213
|
* The request/response headers object to look in.
|
|
228
|
-
*
|
|
229
214
|
* @param {TraceSegment} [segment=null]
|
|
230
215
|
* The trace segment to associate the header data with. If no segment is
|
|
231
216
|
* provided then the currently active segment is used.
|
|
232
|
-
*
|
|
233
217
|
* @param {string} [transportType='Unknown']
|
|
234
218
|
* The transport type that brought the headers. Usually `HTTP` or `HTTPS`.
|
|
235
219
|
*/
|
|
@@ -282,10 +266,8 @@ function handleCATHeaders(headers, segment, transportType) {
|
|
|
282
266
|
* - `insertCATRequestHeaders(headers [, useAlternateHeaderNames])`
|
|
283
267
|
*
|
|
284
268
|
* @memberof TransactionShim.prototype
|
|
285
|
-
*
|
|
286
269
|
* @param {object} headers
|
|
287
270
|
* The outbound request headers object to inject our CAT headers into.
|
|
288
|
-
*
|
|
289
271
|
* @param {bool} [useAlternateHeaderNames=false]
|
|
290
272
|
* Indicates if HTTP-style headers should be used or alternate style. Some
|
|
291
273
|
* transport protocols are more strict on the characters allowed in headers
|
|
@@ -327,10 +309,8 @@ function insertCATRequestHeaders(headers, useAlternateHeaderNames) {
|
|
|
327
309
|
* - `insertCATReplyHeaders(headers [, useAlternateHeaderNames])`
|
|
328
310
|
*
|
|
329
311
|
* @memberof TransactionShim.prototype
|
|
330
|
-
*
|
|
331
312
|
* @param {object} headers
|
|
332
313
|
* The outbound response headers object to inject our CAT headers into.
|
|
333
|
-
*
|
|
334
314
|
* @param {bool} [useAlternateHeaderNames=false]
|
|
335
315
|
* Indicates if HTTP-style headers should be used or alternate style. Some
|
|
336
316
|
* transport protocols are more strict on the characters allowed in headers
|
|
@@ -388,20 +368,15 @@ function insertCATReplyHeader(headers, useAlternateHeaderNames) {
|
|
|
388
368
|
* `spec.type` is not the same as the current transaction's type.
|
|
389
369
|
*
|
|
390
370
|
* @private
|
|
391
|
-
*
|
|
392
371
|
* @param {Shim} shim
|
|
393
372
|
* The shim used for the binding.
|
|
394
|
-
*
|
|
395
|
-
* @param {function} fn
|
|
373
|
+
* @param {Function} fn
|
|
396
374
|
* The function link with the transaction.
|
|
397
|
-
*
|
|
398
375
|
* @param {string} name
|
|
399
376
|
* The name of the wrapped function.
|
|
400
|
-
*
|
|
401
377
|
* @param {TransactionSpec} spec
|
|
402
378
|
* The spec for the transaction to create.
|
|
403
|
-
*
|
|
404
|
-
* @return {function} A function which wraps `fn` and creates potentially nested
|
|
379
|
+
* @returns {Function} A function which wraps `fn` and creates potentially nested
|
|
405
380
|
* transactions linked to its execution.
|
|
406
381
|
*/
|
|
407
382
|
function _makeNestedTransWrapper(shim, fn, name, spec) {
|
|
@@ -412,7 +387,7 @@ function _makeNestedTransWrapper(shim, fn, name, spec) {
|
|
|
412
387
|
|
|
413
388
|
// Reuse existing transactions only if the type matches.
|
|
414
389
|
let transaction = shim.tracer.getTransaction()
|
|
415
|
-
let segment = shim.
|
|
390
|
+
let segment = shim.getSegment()
|
|
416
391
|
|
|
417
392
|
// Only create a new transaction if we either do not have a current
|
|
418
393
|
// transaction _or_ the current transaction is not of the type we want.
|
|
@@ -433,20 +408,15 @@ function _makeNestedTransWrapper(shim, fn, name, spec) {
|
|
|
433
408
|
* A transaction will only be created if there is not a currently active one.
|
|
434
409
|
*
|
|
435
410
|
* @private
|
|
436
|
-
*
|
|
437
411
|
* @param {Shim} shim
|
|
438
412
|
* The shim used for the binding.
|
|
439
|
-
*
|
|
440
|
-
* @param {function} fn
|
|
413
|
+
* @param {Function} fn
|
|
441
414
|
* The function link with the transaction.
|
|
442
|
-
*
|
|
443
415
|
* @param {string} name
|
|
444
416
|
* The name of the wrapped function.
|
|
445
|
-
*
|
|
446
417
|
* @param {TransactionSpec} spec
|
|
447
418
|
* The spec for the transaction to create.
|
|
448
|
-
*
|
|
449
|
-
* @return {function} A function which wraps `fn` and potentially creates a new
|
|
419
|
+
* @returns {Function} A function which wraps `fn` and potentially creates a new
|
|
450
420
|
* transaction linked to the function's execution.
|
|
451
421
|
*/
|
|
452
422
|
function _makeTransWrapper(shim, fn, name, spec) {
|
package/lib/shimmer.js
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const path = require('path')
|
|
9
|
+
const semver = require('semver')
|
|
9
10
|
const fs = require('./util/unwrapped-core').fs
|
|
10
11
|
const logger = require('./logger').child({ component: 'shimmer' })
|
|
11
12
|
const INSTRUMENTATIONS = require('./instrumentations')()
|
|
12
13
|
const properties = require('./util/properties')
|
|
13
14
|
const shims = require('./shim')
|
|
14
15
|
|
|
16
|
+
const NAMES = require('./metrics/names')
|
|
17
|
+
|
|
15
18
|
const MODULE_TYPE = shims.constants.MODULE_TYPE
|
|
16
19
|
|
|
17
20
|
const CORE_INSTRUMENTATION = {
|
|
@@ -90,9 +93,8 @@ const shimmer = (module.exports = {
|
|
|
90
93
|
/**
|
|
91
94
|
* Detects if the given function has already been wrapped.
|
|
92
95
|
*
|
|
93
|
-
* @param {
|
|
94
|
-
*
|
|
95
|
-
* @return {bool} True if `fn` exists and has an attached original, else false.
|
|
96
|
+
* @param {Function} fn - The function to look for a wrapper on.
|
|
97
|
+
* @returns {bool} True if `fn` exists and has an attached original, else false.
|
|
96
98
|
*/
|
|
97
99
|
isWrapped: function isWrapped(fn) {
|
|
98
100
|
return !!(fn && fn.__NR_original)
|
|
@@ -111,7 +113,7 @@ const shimmer = (module.exports = {
|
|
|
111
113
|
* helpful than you'd think.
|
|
112
114
|
* @param {string} methods One or more names of methods or functions to extract
|
|
113
115
|
* and wrap.
|
|
114
|
-
* @param {
|
|
116
|
+
* @param {Function} wrapper A generator that, when called, returns a
|
|
115
117
|
* wrapped version of the original function.
|
|
116
118
|
*/
|
|
117
119
|
wrapMethod: function wrapMethod(nodule, noduleName, methods, wrapper) {
|
|
@@ -177,8 +179,7 @@ const shimmer = (module.exports = {
|
|
|
177
179
|
* @param {object} noduleName Human-readable module / Class name. More
|
|
178
180
|
* helpful than you'd think.
|
|
179
181
|
* @param {string} property The property to replace with the accessor.
|
|
180
|
-
* @param {
|
|
181
|
-
*
|
|
182
|
+
* @param {Function} options Optional getter and setter to use for the accessor.
|
|
182
183
|
* @returns {object} The original value of the property.
|
|
183
184
|
*/
|
|
184
185
|
wrapDeprecated: function wrapDeprecated(nodule, noduleName, property, options) {
|
|
@@ -275,6 +276,8 @@ const shimmer = (module.exports = {
|
|
|
275
276
|
/**
|
|
276
277
|
* Patch the module.load function so that we see modules loading and
|
|
277
278
|
* have an opportunity to patch them with instrumentation.
|
|
279
|
+
*
|
|
280
|
+
* @param agent
|
|
278
281
|
*/
|
|
279
282
|
patchModule: function patchModule(agent) {
|
|
280
283
|
logger.trace('Wrapping module loader.')
|
|
@@ -286,7 +289,14 @@ const shimmer = (module.exports = {
|
|
|
286
289
|
// This is triggered by the load call, so record the path that has been seen so
|
|
287
290
|
// we can examine it after the load call has returned.
|
|
288
291
|
const resolvedFilepath = resolve.apply(this, arguments)
|
|
289
|
-
|
|
292
|
+
|
|
293
|
+
// Only fire the first time we see a specific module resolved
|
|
294
|
+
if (filepathMap[file] !== resolvedFilepath) {
|
|
295
|
+
filepathMap[file] = resolvedFilepath
|
|
296
|
+
|
|
297
|
+
_onResolveFileName(agent, file, resolvedFilepath)
|
|
298
|
+
}
|
|
299
|
+
|
|
290
300
|
return resolvedFilepath
|
|
291
301
|
}
|
|
292
302
|
})
|
|
@@ -306,6 +316,7 @@ const shimmer = (module.exports = {
|
|
|
306
316
|
* their parent has already been loaded/cached by Node.
|
|
307
317
|
* Provides a fall-back for unexpected cases that may occur.
|
|
308
318
|
* Also provides flexibility for testing now that node 11+ caches these.
|
|
319
|
+
*
|
|
309
320
|
* @param {*} request
|
|
310
321
|
* @param {*} parent
|
|
311
322
|
* @param {*} isMain
|
|
@@ -398,6 +409,10 @@ const shimmer = (module.exports = {
|
|
|
398
409
|
},
|
|
399
410
|
|
|
400
411
|
registerInstrumentation: function registerInstrumentation(opts) {
|
|
412
|
+
if (!hasValidRegisterOptions(opts)) {
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
|
|
401
416
|
shimmer.registeredInstrumentations[opts.moduleName] = opts
|
|
402
417
|
},
|
|
403
418
|
|
|
@@ -419,6 +434,9 @@ const shimmer = (module.exports = {
|
|
|
419
434
|
* re-wrap them.
|
|
420
435
|
*
|
|
421
436
|
* Use this to re-apply any applicable instrumentation.
|
|
437
|
+
*
|
|
438
|
+
* @param agent
|
|
439
|
+
* @param modulePath
|
|
422
440
|
*/
|
|
423
441
|
reinstrument: function reinstrument(agent, modulePath) {
|
|
424
442
|
return _postLoad(agent, require(modulePath), modulePath)
|
|
@@ -428,6 +446,8 @@ const shimmer = (module.exports = {
|
|
|
428
446
|
* Given a NodeJS module name, return the name/identifier of our
|
|
429
447
|
* instrumentation. These two things are usually, but not always,
|
|
430
448
|
* the same.
|
|
449
|
+
*
|
|
450
|
+
* @param moduleName
|
|
431
451
|
*/
|
|
432
452
|
getInstrumentationNameFromModuleName(moduleName) {
|
|
433
453
|
let instrumentation
|
|
@@ -445,6 +465,14 @@ const shimmer = (module.exports = {
|
|
|
445
465
|
instrumentation = moduleName
|
|
446
466
|
}
|
|
447
467
|
return instrumentation
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
instrumentPostLoad(agent, module, moduleName, resolvedName) {
|
|
471
|
+
const result = _postLoad(agent, module, moduleName, resolvedName)
|
|
472
|
+
// This is to not break the public API
|
|
473
|
+
// previously it would just call instrumentation
|
|
474
|
+
// and not check the result
|
|
475
|
+
return !!result.__NR_instrumented
|
|
448
476
|
}
|
|
449
477
|
})
|
|
450
478
|
|
|
@@ -467,8 +495,13 @@ function applyDebugState(shim, nodule) {
|
|
|
467
495
|
* All instrumentation files must export the same interface: a single
|
|
468
496
|
* initialization function that takes the agent and the module to be
|
|
469
497
|
* instrumented.
|
|
498
|
+
*
|
|
499
|
+
* @param agent
|
|
500
|
+
* @param nodule
|
|
501
|
+
* @param moduleName
|
|
502
|
+
* @param resolvedName
|
|
470
503
|
*/
|
|
471
|
-
function
|
|
504
|
+
function instrumentPostLoad(agent, nodule, moduleName, resolvedName) {
|
|
472
505
|
const instrumentation = shimmer.registeredInstrumentations[moduleName]
|
|
473
506
|
if (
|
|
474
507
|
properties.hasOwn(nodule, '__NR_instrumented') ||
|
|
@@ -484,6 +517,7 @@ function instrument(agent, nodule, moduleName, resolvedName) {
|
|
|
484
517
|
const shim = shims.createShimFromType(instrumentation.type, agent, moduleName, resolvedName)
|
|
485
518
|
|
|
486
519
|
applyDebugState(shim, nodule)
|
|
520
|
+
trackInstrumentationUsage(agent, shim, moduleName, NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE)
|
|
487
521
|
|
|
488
522
|
try {
|
|
489
523
|
if (instrumentation.onRequire(shim, nodule, moduleName) !== false) {
|
|
@@ -536,11 +570,137 @@ function _firstPartyInstrumentation(agent, fileName, shim, nodule, moduleName) {
|
|
|
536
570
|
function _postLoad(agent, nodule, name, resolvedName) {
|
|
537
571
|
const instrumentation = shimmer.getInstrumentationNameFromModuleName(name)
|
|
538
572
|
|
|
573
|
+
const registeredInstrumentation = shimmer.registeredInstrumentations[instrumentation]
|
|
574
|
+
const hasPostLoadInstrumentation =
|
|
575
|
+
registeredInstrumentation && registeredInstrumentation.onRequire
|
|
576
|
+
|
|
539
577
|
// Check if this is a known instrumentation and then run it.
|
|
540
|
-
if (
|
|
541
|
-
logger.trace('Instrumenting %s.', name)
|
|
542
|
-
return
|
|
578
|
+
if (hasPostLoadInstrumentation) {
|
|
579
|
+
logger.trace('Instrumenting %s with onRequire (module loaded) hook.', name)
|
|
580
|
+
return instrumentPostLoad(agent, nodule, instrumentation, resolvedName)
|
|
543
581
|
}
|
|
544
582
|
|
|
545
583
|
return nodule
|
|
546
584
|
}
|
|
585
|
+
|
|
586
|
+
function _onResolveFileName(agent, requiredNameOrPath, resolvedFilepath) {
|
|
587
|
+
const instrumentation = shimmer.getInstrumentationNameFromModuleName(requiredNameOrPath)
|
|
588
|
+
|
|
589
|
+
const registeredInstrumentation = shimmer.registeredInstrumentations[instrumentation]
|
|
590
|
+
const hasResolvedFileInstrumentation =
|
|
591
|
+
registeredInstrumentation && registeredInstrumentation.onResolved
|
|
592
|
+
|
|
593
|
+
// Check if this is a known instrumentation and then run it.
|
|
594
|
+
if (hasResolvedFileInstrumentation) {
|
|
595
|
+
logger.trace('Instrumenting %s with onResolved hook.', requiredNameOrPath)
|
|
596
|
+
_instrumentOnResolved(agent, instrumentation, resolvedFilepath)
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Invokes the onResolved handler with a shim instance.
|
|
602
|
+
*
|
|
603
|
+
* Given Node.js caches resolvedFilePaths in versions we support and we cache as well
|
|
604
|
+
* for the cases we force resolution, we should not run into the case of multiple
|
|
605
|
+
* invocations for the same module. As such, this function does not defend against multiple runs.
|
|
606
|
+
*
|
|
607
|
+
* @param agent
|
|
608
|
+
* @param moduleName
|
|
609
|
+
* @param resolvedFilepath
|
|
610
|
+
*/
|
|
611
|
+
function _instrumentOnResolved(agent, moduleName, resolvedFilepath) {
|
|
612
|
+
const instrumentation = shimmer.registeredInstrumentations[moduleName]
|
|
613
|
+
|
|
614
|
+
const shim = shims.createShimFromType(instrumentation.type, agent, moduleName, resolvedFilepath)
|
|
615
|
+
|
|
616
|
+
trackInstrumentationUsage(agent, shim, moduleName, NAMES.FEATURES.INSTRUMENTATION.ON_RESOLVED)
|
|
617
|
+
|
|
618
|
+
try {
|
|
619
|
+
instrumentation.onResolved(shim, moduleName, resolvedFilepath)
|
|
620
|
+
} catch (instrumentationError) {
|
|
621
|
+
if (instrumentation.onError) {
|
|
622
|
+
try {
|
|
623
|
+
instrumentation.onError(instrumentationError)
|
|
624
|
+
} catch (error) {
|
|
625
|
+
logger.warn(
|
|
626
|
+
error,
|
|
627
|
+
instrumentationError,
|
|
628
|
+
'OnResolved instrumentation for %s failed, then the onError handler threw an error',
|
|
629
|
+
moduleName
|
|
630
|
+
)
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
logger.warn(
|
|
634
|
+
instrumentationError,
|
|
635
|
+
'OnResolved instrumentation for %s failed. Please report this to the ' +
|
|
636
|
+
'maintainers of the custom instrumentation.',
|
|
637
|
+
moduleName
|
|
638
|
+
)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function hasValidRegisterOptions(opts) {
|
|
644
|
+
if (!opts) {
|
|
645
|
+
logger.warn('Instrumentation registration failed, no options provided')
|
|
646
|
+
return false
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (!opts.moduleName) {
|
|
650
|
+
logger.warn(`Instrumentation registration failed, 'moduleName' not provided`)
|
|
651
|
+
return false
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (!opts.onRequire && !opts.onResolved) {
|
|
655
|
+
logger.warn(
|
|
656
|
+
'Instrumentation registration for %s failed, no require hooks provided.',
|
|
657
|
+
opts.moduleName
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
return false
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return true
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Adds metrics to indicate instrumentation was used for a particular module and
|
|
668
|
+
* what major version the module was at, if possible.
|
|
669
|
+
*
|
|
670
|
+
* @param {*} agent The agent instance.
|
|
671
|
+
* @param {*} shim The instance of the shim used to instrument the module.
|
|
672
|
+
* @param {string} moduleName The name of the required module.
|
|
673
|
+
* @param {string} metricPrefix Support metric prefix to prepend to the metrics. Will indicate onRequire or onResolved
|
|
674
|
+
* from NAMES.FEATURES.INSTRUMENTATION.
|
|
675
|
+
*/
|
|
676
|
+
function trackInstrumentationUsage(agent, shim, moduleName, metricPrefix) {
|
|
677
|
+
try {
|
|
678
|
+
const version = tryGetVersion(shim)
|
|
679
|
+
const instrumentationMetricName = `${metricPrefix}/${moduleName}`
|
|
680
|
+
|
|
681
|
+
agent.metrics.getOrCreateMetric(instrumentationMetricName).incrementCallCount()
|
|
682
|
+
|
|
683
|
+
if (version) {
|
|
684
|
+
const majorVersion = semver.major(version)
|
|
685
|
+
const versionMetricName = `${instrumentationMetricName}/Version/${majorVersion}`
|
|
686
|
+
|
|
687
|
+
agent.metrics.getOrCreateMetric(versionMetricName).incrementCallCount()
|
|
688
|
+
}
|
|
689
|
+
} catch (error) {
|
|
690
|
+
logger.debug('Unable to track instrumentation usage for: ', moduleName, error)
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function tryGetVersion(shim) {
|
|
695
|
+
// Global module (i.e. domain) or finding root failed
|
|
696
|
+
if (shim._moduleRoot === '.') {
|
|
697
|
+
return
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const packageInfo = shim.require('./package.json')
|
|
701
|
+
if (!packageInfo) {
|
|
702
|
+
return
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return packageInfo.version
|
|
706
|
+
}
|
|
@@ -16,13 +16,13 @@ const SEGMENT = '__NR_segment'
|
|
|
16
16
|
|
|
17
17
|
module.exports = Tracer
|
|
18
18
|
|
|
19
|
-
function Tracer(agent) {
|
|
19
|
+
function Tracer(agent, contextManager) {
|
|
20
20
|
if (!agent) {
|
|
21
21
|
throw new Error('Must be initialized with an agent.')
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
this.agent = agent
|
|
25
|
-
this.
|
|
25
|
+
this._contextManager = contextManager
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
Tracer.prototype.getTransaction = getTransaction
|
|
@@ -45,35 +45,28 @@ Tracer.prototype.wrapFunctionFirst = wrapFunctionFirst
|
|
|
45
45
|
Tracer.prototype.wrapSyncFunction = wrapSyncFunction
|
|
46
46
|
Tracer.prototype.wrapCallback = wrapCallback
|
|
47
47
|
|
|
48
|
-
Object.defineProperty(Tracer.prototype, 'segment', {
|
|
49
|
-
get: function segmentGetter() {
|
|
50
|
-
return this._segment
|
|
51
|
-
},
|
|
52
|
-
set: function segmentSetter(segment) {
|
|
53
|
-
this._segment && this._segment.probe('Segment removed from tracer')
|
|
54
|
-
segment && segment.probe('Set tracer.segment')
|
|
55
|
-
return (this._segment = segment)
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
|
|
59
48
|
function getTransaction() {
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
const currentSegment = this._contextManager.getContext()
|
|
50
|
+
if (currentSegment && currentSegment.transaction && currentSegment.transaction.isActive()) {
|
|
51
|
+
return currentSegment.transaction
|
|
62
52
|
}
|
|
63
53
|
|
|
64
54
|
return null
|
|
65
55
|
}
|
|
66
56
|
|
|
57
|
+
// TODO: Remove/replace external uses to tracer.getSegment()
|
|
67
58
|
function getSegment() {
|
|
68
|
-
return this.
|
|
59
|
+
return this._contextManager.getContext()
|
|
69
60
|
}
|
|
70
61
|
|
|
62
|
+
// TODO: Remove/replace external uses to tracer.getSpanContext()
|
|
71
63
|
function getSpanContext() {
|
|
72
|
-
|
|
64
|
+
const currentSegment = this.getSegment()
|
|
65
|
+
return currentSegment && currentSegment.getSpanContext()
|
|
73
66
|
}
|
|
74
67
|
|
|
75
68
|
function createSegment(name, recorder, _parent) {
|
|
76
|
-
const parent = _parent || this.
|
|
69
|
+
const parent = _parent || this.getSegment()
|
|
77
70
|
if (!parent || !parent.transaction.isActive()) {
|
|
78
71
|
logger.trace(
|
|
79
72
|
{
|
|
@@ -111,7 +104,7 @@ function transactionProxy(handler) {
|
|
|
111
104
|
}
|
|
112
105
|
|
|
113
106
|
// don't nest transactions, reuse existing ones
|
|
114
|
-
const segment = tracer.
|
|
107
|
+
const segment = tracer.getSegment()
|
|
115
108
|
if (segment) {
|
|
116
109
|
if (segment.transaction.traceStacks) {
|
|
117
110
|
segment.probe('!!! Nested transaction creation !!!')
|
|
@@ -143,9 +136,9 @@ function transactionProxy(handler) {
|
|
|
143
136
|
* if there is an in play segment and uses that as the root instead of
|
|
144
137
|
* transaction.trace.root.
|
|
145
138
|
*
|
|
146
|
-
* @param {
|
|
147
|
-
*
|
|
148
|
-
* @
|
|
139
|
+
* @param {string} type - Type of transaction to create. 'web' or 'bg'.
|
|
140
|
+
* @param {Function} handler - Generator to proxy.
|
|
141
|
+
* @returns {Function} Proxy.
|
|
149
142
|
*/
|
|
150
143
|
function transactionNestProxy(type, handler) {
|
|
151
144
|
if (handler === undefined && typeof type === 'function') {
|
|
@@ -165,7 +158,7 @@ function transactionNestProxy(type, handler) {
|
|
|
165
158
|
|
|
166
159
|
// don't nest transactions, reuse existing ones
|
|
167
160
|
let transaction = tracer.getTransaction()
|
|
168
|
-
let segment = tracer.
|
|
161
|
+
let segment = tracer.getSegment()
|
|
169
162
|
|
|
170
163
|
let createNew = false
|
|
171
164
|
|
|
@@ -192,7 +185,7 @@ function bindFunction(handler, segment, full) {
|
|
|
192
185
|
return handler
|
|
193
186
|
}
|
|
194
187
|
|
|
195
|
-
return _makeWrapped(this, handler, segment || this.
|
|
188
|
+
return _makeWrapped(this, handler, segment || this.getSegment(), !!full)
|
|
196
189
|
}
|
|
197
190
|
function _makeWrapped(tracer, handler, active, full) {
|
|
198
191
|
wrapped[ORIGINAL] = getOriginal(handler)
|
|
@@ -201,18 +194,19 @@ function _makeWrapped(tracer, handler, active, full) {
|
|
|
201
194
|
return wrapped
|
|
202
195
|
|
|
203
196
|
function wrapped() {
|
|
204
|
-
const prev = tracer.
|
|
205
|
-
|
|
197
|
+
const prev = tracer.getSegment()
|
|
198
|
+
|
|
206
199
|
if (active && full) {
|
|
207
200
|
active.start()
|
|
208
201
|
}
|
|
202
|
+
|
|
209
203
|
try {
|
|
210
|
-
return
|
|
204
|
+
return tracer._contextManager.runInContext(active, handler, this, arguments)
|
|
211
205
|
} catch (err) {
|
|
212
206
|
logger.trace(err, 'Error from wrapped function:')
|
|
213
207
|
|
|
214
208
|
if (prev === null && process.domain != null) {
|
|
215
|
-
process.domain.__NR_transactionSegment = tracer.
|
|
209
|
+
process.domain.__NR_transactionSegment = tracer.getSegment()
|
|
216
210
|
}
|
|
217
211
|
|
|
218
212
|
throw err // Re-throwing application error, this is not an agent error.
|
|
@@ -220,7 +214,6 @@ function _makeWrapped(tracer, handler, active, full) {
|
|
|
220
214
|
if (active && full) {
|
|
221
215
|
active.touch()
|
|
222
216
|
}
|
|
223
|
-
tracer.segment = prev
|
|
224
217
|
}
|
|
225
218
|
}
|
|
226
219
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.7.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -148,9 +148,11 @@
|
|
|
148
148
|
"update-cross-agent-tests": "./bin/update-cats.sh",
|
|
149
149
|
"versioned-tests": "./bin/run-versioned-tests.sh",
|
|
150
150
|
"update-changelog-version": "node ./bin/update-changelog-version",
|
|
151
|
+
"checkout-external-versioned": "node ./test/versioned-external/checkout-external-tests.js",
|
|
151
152
|
"versioned": "npm run versioned:npm7",
|
|
152
|
-
"versioned:
|
|
153
|
-
"versioned:
|
|
153
|
+
"versioned:major": "npm run checkout-external-versioned && npm run prepare-test && VERSIONED_MODE=--major NPM7=1 time ./bin/run-versioned-tests.sh",
|
|
154
|
+
"versioned:npm6": "npm run checkout-external-versioned && npm run prepare-test && time ./bin/run-versioned-tests.sh",
|
|
155
|
+
"versioned:npm7": "npm run checkout-external-versioned && npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
|
|
154
156
|
"prepare": "husky install"
|
|
155
157
|
},
|
|
156
158
|
"bin": {
|
|
@@ -177,8 +179,9 @@
|
|
|
177
179
|
"@newrelic/eslint-config": "^0.0.3",
|
|
178
180
|
"@newrelic/newrelic-oss-cli": "^0.1.2",
|
|
179
181
|
"@newrelic/proxy": "^2.0.0",
|
|
180
|
-
"@newrelic/test-utilities": "^6.
|
|
182
|
+
"@newrelic/test-utilities": "^6.1.1",
|
|
181
183
|
"@octokit/rest": "^18.0.15",
|
|
184
|
+
"@slack/bolt": "^3.7.0",
|
|
182
185
|
"ajv": "^6.12.6",
|
|
183
186
|
"architect": "*",
|
|
184
187
|
"benchmark": "^2.1.4",
|