newrelic 9.14.0 → 9.15.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 +8 -0
- package/index.js +5 -6
- package/lib/config/default.js +7 -0
- package/lib/config/index.js +7 -1
- package/lib/shim/datastore-shim.js +55 -36
- package/lib/symbols.js +0 -1
- package/lib/transaction/index.js +156 -81
- package/package.json +1 -1
package/NEWS.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
### v9.15.0 (2023-04-04)
|
|
2
|
+
|
|
3
|
+
* Added a new configuration option `heroku.use_dyno_names` to specify whether or not to use `process.env.DYNO` for naming the host name and display host. This option defaults to true. If you are on heroku and do not want this functionality set `heroku.use_dyno_names` to `false`. You can also control this configuration options with the environment variable of `NEW_RELIC_HEROKU_USE_DYNO_NAMES`. Thanks @benney-au-le for your contribution 🚀
|
|
4
|
+
|
|
5
|
+
### v9.14.1 (2023-03-23)
|
|
6
|
+
|
|
7
|
+
* Restored assigning loaded version of agent to require.cache as `__NR_cache` instead of a symbol to properly detect attempts at loading agent twice.
|
|
8
|
+
|
|
1
9
|
### v9.14.0 (2023-03-23)
|
|
2
10
|
|
|
3
11
|
* Added new API function called `setErrorGroupCallback`, which provides a way for you to customize the `error.group.name` attribute of errors that are captured by the agent. This attribute controls how the Errors Inbox functionality groups similar errors together. To learn more about this function, please refer to our [example app](https://github.com/newrelic/newrelic-node-examples).
|
package/index.js
CHANGED
|
@@ -14,7 +14,6 @@ require('./lib/util/unwrapped-core')
|
|
|
14
14
|
|
|
15
15
|
const featureFlags = require('./lib/feature_flags').prerelease
|
|
16
16
|
const psemver = require('./lib/util/process-version')
|
|
17
|
-
const symbols = require('./lib/symbols')
|
|
18
17
|
let logger = require('./lib/logger') // Gets re-loaded after initialization.
|
|
19
18
|
|
|
20
19
|
const pkgJSON = require('./package.json')
|
|
@@ -24,15 +23,15 @@ logger.info(
|
|
|
24
23
|
process.version
|
|
25
24
|
)
|
|
26
25
|
|
|
27
|
-
if (require.cache
|
|
26
|
+
if (require.cache.__NR_cache) {
|
|
28
27
|
logger.warn(
|
|
29
28
|
'Attempting to load a second copy of newrelic from %s, using cache instead',
|
|
30
29
|
__dirname
|
|
31
30
|
)
|
|
32
|
-
if (require.cache
|
|
33
|
-
require.cache
|
|
31
|
+
if (require.cache.__NR_cache.agent) {
|
|
32
|
+
require.cache.__NR_cache.agent.recordSupportability('Agent/DoubleLoad')
|
|
34
33
|
}
|
|
35
|
-
module.exports = require.cache
|
|
34
|
+
module.exports = require.cache.__NR_cache
|
|
36
35
|
} else {
|
|
37
36
|
initialize()
|
|
38
37
|
}
|
|
@@ -99,7 +98,7 @@ function initialize() {
|
|
|
99
98
|
API = require('./stub_api')
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
require.cache
|
|
101
|
+
require.cache.__NR_cache = module.exports = new API(agent)
|
|
103
102
|
|
|
104
103
|
// If we loaded an agent, record a startup time for the agent.
|
|
105
104
|
// NOTE: Metrics are recorded in seconds, so divide the value by 1000.
|
package/lib/config/default.js
CHANGED
package/lib/config/index.js
CHANGED
|
@@ -914,11 +914,17 @@ Config.prototype.getIPAddresses = function getIPAddresses() {
|
|
|
914
914
|
|
|
915
915
|
function getHostnameSafe() {
|
|
916
916
|
let _hostname
|
|
917
|
+
const config = this
|
|
917
918
|
this.getHostnameSafe = function getCachedHostname() {
|
|
918
919
|
return _hostname
|
|
919
920
|
}
|
|
920
921
|
try {
|
|
921
|
-
|
|
922
|
+
if (config.heroku.use_dyno_names) {
|
|
923
|
+
const dynoName = process.env.DYNO
|
|
924
|
+
_hostname = dynoName || os.hostname()
|
|
925
|
+
} else {
|
|
926
|
+
_hostname = os.hostname()
|
|
927
|
+
}
|
|
922
928
|
return _hostname
|
|
923
929
|
} catch (e) {
|
|
924
930
|
const addresses = this.getIPAddresses()
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 57] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
|
-
|
|
10
8
|
const dbutil = require('../db/utils')
|
|
11
9
|
const hasOwnProperty = require('../util/properties').hasOwn
|
|
12
10
|
const logger = require('../logger').child({ component: 'DatastoreShim' })
|
|
@@ -357,19 +355,17 @@ function recordOperation(nodule, properties, opSpec) {
|
|
|
357
355
|
return this.record(nodule, properties, function opRecorder(shim, fn, fnName, args) {
|
|
358
356
|
shim.logger.trace('Recording datastore operation "%s"', fnName)
|
|
359
357
|
|
|
360
|
-
// Derive the segment information
|
|
358
|
+
// Derive the segment information, starting from some defaults
|
|
361
359
|
let segDesc = null
|
|
362
360
|
if (shim.isFunction(opSpec)) {
|
|
363
361
|
segDesc = opSpec.call(this, shim, fn, fnName, args)
|
|
364
362
|
} else {
|
|
365
363
|
segDesc = {
|
|
366
|
-
name:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
record: opSpec.record,
|
|
372
|
-
opaque: opSpec.opaque || false
|
|
364
|
+
name: fnName || 'other',
|
|
365
|
+
opaque: false,
|
|
366
|
+
after: null,
|
|
367
|
+
promise: null,
|
|
368
|
+
...opSpec
|
|
373
369
|
}
|
|
374
370
|
}
|
|
375
371
|
if (hasOwnProperty(segDesc, 'parameters')) {
|
|
@@ -648,26 +644,31 @@ function _recordQuery(suffix, nodule, properties, querySpec) {
|
|
|
648
644
|
|
|
649
645
|
let queryDesc = querySpec
|
|
650
646
|
if (shim.isFunction(querySpec)) {
|
|
651
|
-
queryDesc = querySpec.call(this, shim, fn, fnName, args)
|
|
647
|
+
queryDesc = querySpec.call(this, shim, fn, fnName, args) || Object.create(null)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Set some default values, in case they're missing.
|
|
651
|
+
queryDesc = {
|
|
652
|
+
name: fnName,
|
|
653
|
+
callback: null,
|
|
654
|
+
rowCallback: null,
|
|
655
|
+
stream: null,
|
|
656
|
+
after: null,
|
|
657
|
+
promise: null,
|
|
658
|
+
opaque: false,
|
|
659
|
+
inContext: null,
|
|
660
|
+
...queryDesc
|
|
652
661
|
}
|
|
653
662
|
|
|
663
|
+
const parameters = _normalizeParameters.call(shim, queryDesc.parameters || Object.create(null))
|
|
664
|
+
|
|
654
665
|
// If we're not actually recording this, then just return the segment
|
|
655
666
|
// descriptor now.
|
|
656
|
-
if (
|
|
657
|
-
const parameters = _normalizeParameters.call(
|
|
658
|
-
shim,
|
|
659
|
-
queryDesc.parameters || Object.create(null)
|
|
660
|
-
)
|
|
667
|
+
if (queryDesc?.record === false) {
|
|
661
668
|
return {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
rowCallback: 'rowCallback' in queryDesc ? queryDesc.rowCallback : null,
|
|
666
|
-
stream: 'stream' in queryDesc ? queryDesc.stream : null,
|
|
667
|
-
after: 'after' in queryDesc ? queryDesc.after : null,
|
|
668
|
-
promise: 'promise' in queryDesc ? queryDesc.promise : null,
|
|
669
|
-
internal: 'internal' in queryDesc ? queryDesc.internal : false,
|
|
670
|
-
opaque: 'opaque' in queryDesc ? queryDesc.opaque : false
|
|
669
|
+
internal: false,
|
|
670
|
+
...queryDesc,
|
|
671
|
+
parameters
|
|
671
672
|
}
|
|
672
673
|
}
|
|
673
674
|
|
|
@@ -683,18 +684,13 @@ function _recordQuery(suffix, nodule, properties, querySpec) {
|
|
|
683
684
|
shim.logger.trace('Found and parsed query %s -> %s', parsed.type, name)
|
|
684
685
|
|
|
685
686
|
// Return the segment descriptor.
|
|
686
|
-
const parameters = _normalizeParameters.call(shim, queryDesc.parameters || Object.create(null))
|
|
687
687
|
return {
|
|
688
|
+
internal: true,
|
|
689
|
+
...queryDesc,
|
|
690
|
+
// This name and parameters might override those in the original
|
|
691
|
+
// queryDesc.
|
|
688
692
|
name: shim._metrics.STATEMENT + name,
|
|
689
693
|
parameters,
|
|
690
|
-
callback: 'callback' in queryDesc ? queryDesc.callback : null,
|
|
691
|
-
rowCallback: 'rowCallback' in queryDesc ? queryDesc.rowCallback : null,
|
|
692
|
-
stream: 'stream' in queryDesc ? queryDesc.stream : null,
|
|
693
|
-
after: 'after' in queryDesc ? queryDesc.after : null,
|
|
694
|
-
promise: 'promise' in queryDesc ? queryDesc.promise : null,
|
|
695
|
-
internal: 'internal' in queryDesc ? queryDesc.internal : true,
|
|
696
|
-
opaque: 'opaque' in queryDesc ? queryDesc.opaque : false,
|
|
697
|
-
inContext: 'inContext' in queryDesc ? queryDesc.inContext : null,
|
|
698
694
|
recorder: function queryRecorder(segment, scope) {
|
|
699
695
|
if (segment) {
|
|
700
696
|
parsed.recordMetrics(segment, scope)
|
|
@@ -806,6 +802,20 @@ function _normalizeParameters(parameters) {
|
|
|
806
802
|
|
|
807
803
|
parameters.product = parameters.product || this._datastore
|
|
808
804
|
|
|
805
|
+
_normalizeDatabaseName(parameters, dsTracerConf)
|
|
806
|
+
_normalizeInstanceInformation(parameters, dsTracerConf, config)
|
|
807
|
+
|
|
808
|
+
return parameters
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Normalizes the database name from segment parameter values.
|
|
813
|
+
*
|
|
814
|
+
* @private
|
|
815
|
+
* @param {object} parameters - The segment parameters to clean up.
|
|
816
|
+
* @param {object} dsTracerConf - The datastore tracer configuration
|
|
817
|
+
*/
|
|
818
|
+
function _normalizeDatabaseName(parameters, dsTracerConf) {
|
|
809
819
|
// Add database name if provided and enabled.
|
|
810
820
|
if (!dsTracerConf.database_name_reporting.enabled) {
|
|
811
821
|
delete parameters.database_name
|
|
@@ -815,7 +825,18 @@ function _normalizeParameters(parameters) {
|
|
|
815
825
|
? String(parameters.database_name)
|
|
816
826
|
: parameters.database_name || INSTANCE_UNKNOWN
|
|
817
827
|
}
|
|
828
|
+
}
|
|
818
829
|
|
|
830
|
+
/**
|
|
831
|
+
* Normalizes the database instance information from segment parameter
|
|
832
|
+
* values: host and the port/path/id.
|
|
833
|
+
*
|
|
834
|
+
* @private
|
|
835
|
+
* @param {object} parameters - The segment parameters to clean up.
|
|
836
|
+
* @param {object} dsTracerConf - The datastore tracer configuration
|
|
837
|
+
* @param {object} config - The agent configuration
|
|
838
|
+
*/
|
|
839
|
+
function _normalizeInstanceInformation(parameters, dsTracerConf, config) {
|
|
819
840
|
// Add instance information if enabled.
|
|
820
841
|
if (!dsTracerConf.instance_reporting.enabled) {
|
|
821
842
|
delete parameters.host
|
|
@@ -836,6 +857,4 @@ function _normalizeParameters(parameters) {
|
|
|
836
857
|
}
|
|
837
858
|
}
|
|
838
859
|
}
|
|
839
|
-
|
|
840
|
-
return parameters
|
|
841
860
|
}
|
package/lib/symbols.js
CHANGED
package/lib/transaction/index.js
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 29] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
|
-
|
|
10
8
|
const errorHelper = require('../errors/helper')
|
|
11
9
|
const hashes = require('../util/hashes')
|
|
12
10
|
const logger = require('../logger').child({ component: 'transaction' })
|
|
@@ -187,7 +185,7 @@ Transaction.prototype.isWeb = function isWeb() {
|
|
|
187
185
|
}
|
|
188
186
|
|
|
189
187
|
/**
|
|
190
|
-
* @returns {
|
|
188
|
+
* @returns {boolean} Is this transaction still alive?
|
|
191
189
|
*/
|
|
192
190
|
Transaction.prototype.isActive = function isActive() {
|
|
193
191
|
return this.timer.isActive()
|
|
@@ -196,6 +194,8 @@ Transaction.prototype.isActive = function isActive() {
|
|
|
196
194
|
/**
|
|
197
195
|
* Close out the current transaction and its associated trace. Remove any
|
|
198
196
|
* instances of this transaction annotated onto the call stack.
|
|
197
|
+
*
|
|
198
|
+
* @returns {(Transaction|undefined)} this transaction, or undefined
|
|
199
199
|
*/
|
|
200
200
|
Transaction.prototype.end = function end() {
|
|
201
201
|
if (!this.timer.isActive()) {
|
|
@@ -259,6 +259,8 @@ Transaction.prototype._cleanUneededReferences = function _cleanUneededReferences
|
|
|
259
259
|
* For web transactions, this represents the time from when the request was received
|
|
260
260
|
* to when response was sent. For background transactions, it is equal to duration
|
|
261
261
|
* of the transaction trace (until last segment ended).
|
|
262
|
+
*
|
|
263
|
+
* @returns {number} timer or trace duration in milliseconds
|
|
262
264
|
*/
|
|
263
265
|
Transaction.prototype.getResponseTimeInMillis = function getResponseTimeInMillis() {
|
|
264
266
|
if (this.isWeb()) {
|
|
@@ -408,6 +410,7 @@ Transaction.prototype.isIgnored = function getIgnore() {
|
|
|
408
410
|
* @param {number} statusCode - The response status code.
|
|
409
411
|
*/
|
|
410
412
|
Transaction.prototype.finalizeNameFromUri = finalizeNameFromUri
|
|
413
|
+
|
|
411
414
|
function finalizeNameFromUri(requestURL, statusCode) {
|
|
412
415
|
if (logger.traceEnabled()) {
|
|
413
416
|
logger.trace(
|
|
@@ -427,24 +430,7 @@ function finalizeNameFromUri(requestURL, statusCode) {
|
|
|
427
430
|
|
|
428
431
|
// If a namestate stack exists, copy route parameters over to the trace.
|
|
429
432
|
if (!this.nameState.isEmpty() && this.baseSegment) {
|
|
430
|
-
this.nameState.forEachParams(
|
|
431
|
-
for (const key in params) {
|
|
432
|
-
if (props.hasOwn(params, key)) {
|
|
433
|
-
this.trace.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, params[key])
|
|
434
|
-
|
|
435
|
-
const segment = this.agent.tracer.getSegment()
|
|
436
|
-
|
|
437
|
-
if (!segment) {
|
|
438
|
-
logger.trace(
|
|
439
|
-
'Active segment not available, not adding request.parameters attribute for %s',
|
|
440
|
-
key
|
|
441
|
-
)
|
|
442
|
-
} else {
|
|
443
|
-
segment.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, params[key])
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}, this)
|
|
433
|
+
this.nameState.forEachParams(forEachRouteParams, this)
|
|
448
434
|
}
|
|
449
435
|
|
|
450
436
|
// Allow the API to explicitly set the ignored status.
|
|
@@ -468,6 +454,25 @@ function finalizeNameFromUri(requestURL, statusCode) {
|
|
|
468
454
|
}
|
|
469
455
|
}
|
|
470
456
|
|
|
457
|
+
function forEachRouteParams(params) {
|
|
458
|
+
for (const key in params) {
|
|
459
|
+
if (props.hasOwn(params, key)) {
|
|
460
|
+
this.trace.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, params[key])
|
|
461
|
+
|
|
462
|
+
const segment = this.agent.tracer.getSegment()
|
|
463
|
+
|
|
464
|
+
if (!segment) {
|
|
465
|
+
logger.trace(
|
|
466
|
+
'Active segment not available, not adding request.parameters attribute for %s',
|
|
467
|
+
key
|
|
468
|
+
)
|
|
469
|
+
} else {
|
|
470
|
+
segment.attributes.addAttribute(DESTS.NONE, REQUEST_PARAMS_PATH + key, params[key])
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
471
476
|
Transaction.prototype._copyNameToActiveSpan = function _copyNameToActiveSpan(name) {
|
|
472
477
|
const spanContext = this.agent.tracer.getSpanContext()
|
|
473
478
|
if (!spanContext) {
|
|
@@ -512,6 +517,7 @@ Transaction.prototype._markAsWeb = function _markAsWeb(rawURL) {
|
|
|
512
517
|
* @param {string} [name]
|
|
513
518
|
* Optional. The partial name to use for the finalized transaction. If omitted
|
|
514
519
|
* the current partial name is used.
|
|
520
|
+
* @returns {undefined} undefined, finalizing name as a side effect
|
|
515
521
|
*/
|
|
516
522
|
Transaction.prototype.finalizeName = function finalizeName(name) {
|
|
517
523
|
// If no name is given, and this is a web transaction with a url, then
|
|
@@ -564,6 +570,8 @@ Transaction.prototype.finalizeName = function finalizeName(name) {
|
|
|
564
570
|
* cause extra performance overhead. Once this is refactored we can make the
|
|
565
571
|
* caching better and eliminate this extra overhead. Be mindful of if/when this
|
|
566
572
|
* is called.
|
|
573
|
+
*
|
|
574
|
+
* @returns {string} finalized name value or partial name
|
|
567
575
|
*/
|
|
568
576
|
Transaction.prototype.getName = function getName() {
|
|
569
577
|
if (this.isWeb() && this.url) {
|
|
@@ -620,7 +628,7 @@ Transaction.prototype.getFullName = function getFullName() {
|
|
|
620
628
|
* otherwise it will parse .url, store it on .parsedUrl, then scrub the URL and
|
|
621
629
|
* store it in the cache.
|
|
622
630
|
*
|
|
623
|
-
* Returns a string or undefined.
|
|
631
|
+
* @returns {(string|undefined)} Returns a string or undefined.
|
|
624
632
|
*/
|
|
625
633
|
Transaction.prototype.getScrubbedUrl = function getScrubbedUrl() {
|
|
626
634
|
if (!this.isWeb()) {
|
|
@@ -703,18 +711,17 @@ Transaction.prototype.measure = function measure(name, scope, duration, exclusiv
|
|
|
703
711
|
*
|
|
704
712
|
* @param {string} name Metric name.
|
|
705
713
|
* @param {number} duration Duration of the transaction, in milliseconds.
|
|
706
|
-
* @param {number}
|
|
707
|
-
*
|
|
708
|
-
* @param keyApdexInMillis
|
|
714
|
+
* @param {number} keyApdexInMillis Duration sent to the metrics getOrCreateApdexMetric method, to
|
|
715
|
+
* derive apdex from timing in milliseconds
|
|
709
716
|
*/
|
|
710
717
|
Transaction.prototype._setApdex = function _setApdex(name, duration, keyApdexInMillis) {
|
|
711
718
|
const apdexStats = this.metrics.getOrCreateApdexMetric(name, null, keyApdexInMillis)
|
|
712
719
|
|
|
713
720
|
// if we have an error-like status code, and all the errors are
|
|
714
721
|
// expected, we know the status code was caused by an expected
|
|
715
|
-
// error, so we will not report "frustrating"
|
|
722
|
+
// error, so we will not report "frustrating." Otherwise, we
|
|
716
723
|
// don't know which error triggered the error-like status code,
|
|
717
|
-
// and will still
|
|
724
|
+
// and will still increment "frustrating." If this is an issue,
|
|
718
725
|
// users can either set a status code as expected, or ignore the
|
|
719
726
|
// specific error to avoid incrementing to frustrating
|
|
720
727
|
if (
|
|
@@ -732,6 +739,7 @@ Transaction.prototype._setApdex = function _setApdex(name, duration, keyApdexInM
|
|
|
732
739
|
* Store first 10 unique path hashes calculated for a transaction.
|
|
733
740
|
*
|
|
734
741
|
* @param {string} pathHash Path hash
|
|
742
|
+
* @returns {undefined}
|
|
735
743
|
*/
|
|
736
744
|
Transaction.prototype.pushPathHash = function pushPathHash(pathHash) {
|
|
737
745
|
if (this.pathHashes.length >= 10 || this.pathHashes.indexOf(pathHash) !== -1) {
|
|
@@ -742,6 +750,8 @@ Transaction.prototype.pushPathHash = function pushPathHash(pathHash) {
|
|
|
742
750
|
|
|
743
751
|
/**
|
|
744
752
|
* Return whether transaction spawned any outbound requests.
|
|
753
|
+
*
|
|
754
|
+
* @returns {boolean} if there are more than zero pathHashes
|
|
745
755
|
*/
|
|
746
756
|
Transaction.prototype.includesOutboundRequests = function includesOutboundRequests() {
|
|
747
757
|
return this.pathHashes.length > 0
|
|
@@ -750,6 +760,8 @@ Transaction.prototype.includesOutboundRequests = function includesOutboundReques
|
|
|
750
760
|
/**
|
|
751
761
|
* Get unique previous path hashes for a transaction. Does not include
|
|
752
762
|
* current path hash.
|
|
763
|
+
*
|
|
764
|
+
* @returns {(string|null)} Returns sorted altHashes joined by commas, or null.
|
|
753
765
|
*/
|
|
754
766
|
Transaction.prototype.alternatePathHashes = function alternatePathHashes() {
|
|
755
767
|
const curHash = hashes.calculatePathHash(
|
|
@@ -771,7 +783,7 @@ Transaction.prototype.alternatePathHashes = function alternatePathHashes() {
|
|
|
771
783
|
* Add the error information to the current segment and add the segment ID as
|
|
772
784
|
* an attribute onto the exception.
|
|
773
785
|
*
|
|
774
|
-
* @param {Exception}
|
|
786
|
+
* @param {Exception} exception The exception object to be collected.
|
|
775
787
|
*/
|
|
776
788
|
Transaction.prototype._linkExceptionToSegment = _linkExceptionToSegment
|
|
777
789
|
|
|
@@ -817,7 +829,7 @@ function _addException(exception) {
|
|
|
817
829
|
* When the transaction ends, the exception will be collected along with the transaction
|
|
818
830
|
* details.
|
|
819
831
|
*
|
|
820
|
-
* @param {Exception}
|
|
832
|
+
* @param {Exception} exception The exception object to be collected.
|
|
821
833
|
*/
|
|
822
834
|
Transaction.prototype.addUserError = _addUserError
|
|
823
835
|
|
|
@@ -832,7 +844,7 @@ function _addUserError(exception) {
|
|
|
832
844
|
}
|
|
833
845
|
|
|
834
846
|
/**
|
|
835
|
-
*
|
|
847
|
+
* @returns {boolean} true if the transaction's current status code is errored
|
|
836
848
|
* but considered ignored via the config.
|
|
837
849
|
*/
|
|
838
850
|
Transaction.prototype.hasIgnoredErrorStatusCode = function _hasIgnoredErrorStatusCode() {
|
|
@@ -840,7 +852,7 @@ Transaction.prototype.hasIgnoredErrorStatusCode = function _hasIgnoredErrorStatu
|
|
|
840
852
|
}
|
|
841
853
|
|
|
842
854
|
/**
|
|
843
|
-
*
|
|
855
|
+
* @returns {boolean} true if an error happened during the transaction or if the transaction itself is
|
|
844
856
|
* considered to be an error.
|
|
845
857
|
*/
|
|
846
858
|
Transaction.prototype.hasErrors = function _hasErrors() {
|
|
@@ -850,7 +862,9 @@ Transaction.prototype.hasErrors = function _hasErrors() {
|
|
|
850
862
|
return transactionHasExceptions || transactionHasuserErrors || isErroredTransaction
|
|
851
863
|
}
|
|
852
864
|
|
|
853
|
-
|
|
865
|
+
/**
|
|
866
|
+
* @returns {boolean} true if all the errors/exceptions collected so far are expected errors
|
|
867
|
+
*/
|
|
854
868
|
Transaction.prototype.hasOnlyExpectedErrors = function hasOnlyExpectedErrors() {
|
|
855
869
|
if (0 === this.exceptions.length) {
|
|
856
870
|
return false
|
|
@@ -871,7 +885,7 @@ Transaction.prototype.hasOnlyExpectedErrors = function hasOnlyExpectedErrors() {
|
|
|
871
885
|
}
|
|
872
886
|
|
|
873
887
|
/**
|
|
874
|
-
*
|
|
888
|
+
* @returns {object} agent intrinsic attribute for this transaction.
|
|
875
889
|
*/
|
|
876
890
|
Transaction.prototype.getIntrinsicAttributes = function getIntrinsicAttributes() {
|
|
877
891
|
if (!this._intrinsicAttributes.totalTime) {
|
|
@@ -909,7 +923,7 @@ Transaction.prototype.getIntrinsicAttributes = function getIntrinsicAttributes()
|
|
|
909
923
|
* W3C TraceContext format is preferred over the NewRelic DT format.
|
|
910
924
|
* NewRelic DT format will be used if no `traceparent` header is found.
|
|
911
925
|
*
|
|
912
|
-
* @param
|
|
926
|
+
* @param {string} [transport='Unknown'] - The transport type that delivered the trace.
|
|
913
927
|
* @param {object} headers - Headers to search for supported trace formats. Keys must be lowercase.
|
|
914
928
|
*/
|
|
915
929
|
Transaction.prototype.acceptDistributedTraceHeaders = acceptDistributedTraceHeaders
|
|
@@ -1025,32 +1039,33 @@ function acceptTraceContextPayload(traceparent, tracestate, transport) {
|
|
|
1025
1039
|
}
|
|
1026
1040
|
}
|
|
1027
1041
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
* @param {string} [transport='Unknown'] - The transport type that delivered the payload.
|
|
1042
|
+
/*
|
|
1043
|
+
The following underscored functions are used exclusively by the
|
|
1044
|
+
_acceptDistributedTracePayload method. They're broken out to reduce
|
|
1045
|
+
its cognitive complexity.
|
|
1033
1046
|
*/
|
|
1034
|
-
|
|
1035
|
-
function _acceptDistributedTracePayload(payload, transport) {
|
|
1047
|
+
const _dtPayloadTest = function _dtPayloadTest(payload) {
|
|
1036
1048
|
if (!payload) {
|
|
1037
1049
|
this.agent.recordSupportability('DistributedTrace/AcceptPayload/Ignored/Null')
|
|
1038
|
-
return
|
|
1039
1050
|
}
|
|
1040
|
-
|
|
1051
|
+
return !!payload
|
|
1052
|
+
}
|
|
1053
|
+
const _isDtTest = function _isDtTest() {
|
|
1041
1054
|
if (this.isDistributedTrace) {
|
|
1042
1055
|
logger.warn(
|
|
1043
1056
|
'Already accepted or created a distributed trace payload for transaction %s, ignoring call',
|
|
1044
1057
|
this.id
|
|
1045
1058
|
)
|
|
1059
|
+
let supportabilityMetric = 'DistributedTrace/AcceptPayload/Ignored/CreateBeforeAccept'
|
|
1046
1060
|
if (this.parentId) {
|
|
1047
|
-
|
|
1048
|
-
} else {
|
|
1049
|
-
this.agent.recordSupportability('DistributedTrace/AcceptPayload/Ignored/CreateBeforeAccept')
|
|
1061
|
+
supportabilityMetric = 'DistributedTrace/AcceptPayload/Ignored/Multiple'
|
|
1050
1062
|
}
|
|
1051
|
-
|
|
1063
|
+
this.agent.recordSupportability(supportabilityMetric)
|
|
1064
|
+
return true
|
|
1052
1065
|
}
|
|
1053
|
-
|
|
1066
|
+
return false
|
|
1067
|
+
}
|
|
1068
|
+
const _dtConfigTest = function _dtConfigTest() {
|
|
1054
1069
|
const config = this.agent.config
|
|
1055
1070
|
const distTraceEnabled = config.distributed_tracing.enabled
|
|
1056
1071
|
const trustedAccount = config.trusted_account_key || config.account_id
|
|
@@ -1064,60 +1079,56 @@ function _acceptDistributedTracePayload(payload, transport) {
|
|
|
1064
1079
|
)
|
|
1065
1080
|
|
|
1066
1081
|
this.agent.recordSupportability(DT_ACCEPT_PAYLOAD_EXCEPTION_METRIC)
|
|
1067
|
-
return
|
|
1082
|
+
return false
|
|
1068
1083
|
}
|
|
1069
|
-
|
|
1084
|
+
return trustedAccount
|
|
1085
|
+
}
|
|
1086
|
+
const _dtParseTest = function _dtParseTest(payload) {
|
|
1070
1087
|
const parsed = this._getParsedPayload(payload)
|
|
1071
1088
|
|
|
1072
1089
|
if (!parsed) {
|
|
1073
|
-
return
|
|
1090
|
+
return false
|
|
1074
1091
|
}
|
|
1075
1092
|
|
|
1093
|
+
if (!parsed.v) {
|
|
1094
|
+
logger.warn('Received a distributed trace payload with no version field', this.id)
|
|
1095
|
+
}
|
|
1096
|
+
if (!parsed.d) {
|
|
1097
|
+
logger.warn('Received a distributed trace payload with no data field', this.id)
|
|
1098
|
+
}
|
|
1076
1099
|
if (!parsed.v || !parsed.d) {
|
|
1077
|
-
if (!parsed.v) {
|
|
1078
|
-
logger.warn('Received a distributed trace payload with no version field', this.id)
|
|
1079
|
-
}
|
|
1080
|
-
if (!parsed.d) {
|
|
1081
|
-
logger.warn('Received a distributed trace payload with no data field', this.id)
|
|
1082
|
-
}
|
|
1083
1100
|
this.agent.recordSupportability(DT_ACCEPT_PAYLOAD_PARSE_EXCEPTION_METRIC)
|
|
1084
|
-
return
|
|
1101
|
+
return false
|
|
1085
1102
|
}
|
|
1086
|
-
|
|
1103
|
+
return parsed
|
|
1104
|
+
}
|
|
1105
|
+
const _dtVersionTest = function _dtVersionTest(parsed) {
|
|
1087
1106
|
const majorVersion = parsed.v && typeof parsed.v[0] === 'number' && parsed.v[0]
|
|
1088
|
-
|
|
1107
|
+
|
|
1108
|
+
if (majorVersion === null) {
|
|
1089
1109
|
logger.warn('Invalid distributed trace payload, not accepting')
|
|
1090
1110
|
this.agent.recordSupportability(DT_ACCEPT_PAYLOAD_EXCEPTION_METRIC)
|
|
1091
1111
|
}
|
|
1092
1112
|
if (majorVersion > 0) {
|
|
1093
1113
|
// TODO: Add DistributedTracePayload class?
|
|
1094
1114
|
this.agent.recordSupportability('DistributedTrace/AcceptPayload/Ignored/MajorVersion')
|
|
1095
|
-
return
|
|
1096
1115
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
logger.warn('No distributed trace data received, not accepting payload')
|
|
1102
|
-
this.agent.recordSupportability(DT_ACCEPT_PAYLOAD_EXCEPTION_METRIC)
|
|
1103
|
-
return
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
const requiredKeysExist = REQUIRED_DT_KEYS.every(function checkExists(key) {
|
|
1116
|
+
return majorVersion
|
|
1117
|
+
}
|
|
1118
|
+
const _dtRequiredKeyTest = function _dtRequiredKeyTest(data) {
|
|
1119
|
+
return REQUIRED_DT_KEYS.every(function checkExists(key) {
|
|
1107
1120
|
return data[key] != null
|
|
1108
1121
|
})
|
|
1122
|
+
}
|
|
1123
|
+
const _dtSpanParentTest = function _dtSpanParentTest(requiredKeysExist, data) {
|
|
1109
1124
|
// Either parentSpanId or parentId are required.
|
|
1110
1125
|
if (!requiredKeysExist || (data.tx == null && data.id == null)) {
|
|
1111
1126
|
this.agent.recordSupportability(DT_ACCEPT_PAYLOAD_PARSE_EXCEPTION_METRIC)
|
|
1112
|
-
return
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
const trustedAccountKey = data.tk || data.ac
|
|
1116
|
-
if (trustedAccountKey !== trustedAccount) {
|
|
1117
|
-
this.agent.recordSupportability(`DistributedTrace/AcceptPayload/Ignored/UntrustedAccount`)
|
|
1118
|
-
return
|
|
1127
|
+
return false
|
|
1119
1128
|
}
|
|
1120
|
-
|
|
1129
|
+
return true
|
|
1130
|
+
}
|
|
1131
|
+
const _dtDefineAttrsFromTraceData = function _dtDefineAttrsFromTraceData(data, transport) {
|
|
1121
1132
|
this.parentType = data.ty
|
|
1122
1133
|
this.parentApp = data.ap
|
|
1123
1134
|
this.parentAcct = data.ac
|
|
@@ -1147,11 +1158,75 @@ function _acceptDistributedTracePayload(payload, transport) {
|
|
|
1147
1158
|
this.agent.recordSupportability('DistributedTrace/AcceptPayload/Success')
|
|
1148
1159
|
}
|
|
1149
1160
|
|
|
1161
|
+
/**
|
|
1162
|
+
* Parses incoming distributed trace header payload.
|
|
1163
|
+
*
|
|
1164
|
+
* @param {object} payload - The distributed trace payload to accept.
|
|
1165
|
+
* @param {string} [transport='Unknown'] - The transport type that delivered the payload.
|
|
1166
|
+
*/
|
|
1167
|
+
Transaction.prototype._acceptDistributedTracePayload = _acceptDistributedTracePayload
|
|
1168
|
+
function _acceptDistributedTracePayload(payload, transport) {
|
|
1169
|
+
const payloadTest = _dtPayloadTest.bind(this)
|
|
1170
|
+
if (!payloadTest(payload)) {
|
|
1171
|
+
return
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
const isDtTest = _isDtTest.bind(this)
|
|
1175
|
+
if (isDtTest()) {
|
|
1176
|
+
return
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
const configTest = _dtConfigTest.bind(this)
|
|
1180
|
+
const configTestResult = configTest()
|
|
1181
|
+
if (!configTestResult) {
|
|
1182
|
+
return
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const traceParseTest = _dtParseTest.bind(this)
|
|
1186
|
+
const parsed = traceParseTest(payload)
|
|
1187
|
+
if (!parsed) {
|
|
1188
|
+
return
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
const traceVersionTest = _dtVersionTest.bind(this)
|
|
1192
|
+
if (traceVersionTest(parsed) > 0) {
|
|
1193
|
+
return
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const data = parsed.d
|
|
1197
|
+
|
|
1198
|
+
if (!data) {
|
|
1199
|
+
logger.warn('No distributed trace data received, not accepting payload')
|
|
1200
|
+
this.agent.recordSupportability(DT_ACCEPT_PAYLOAD_EXCEPTION_METRIC)
|
|
1201
|
+
return
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
const requiredKeyTest = _dtRequiredKeyTest.bind(this)
|
|
1205
|
+
const requiredKeysExist = requiredKeyTest(data)
|
|
1206
|
+
const spanParentTest = _dtSpanParentTest.bind(this)
|
|
1207
|
+
const spanParentResult = spanParentTest(requiredKeysExist, data)
|
|
1208
|
+
|
|
1209
|
+
if (!spanParentResult) {
|
|
1210
|
+
return
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const trustedAccount = configTestResult
|
|
1214
|
+
const trustedAccountKey = data.tk || data.ac
|
|
1215
|
+
if (trustedAccountKey !== trustedAccount) {
|
|
1216
|
+
this.agent.recordSupportability(`DistributedTrace/AcceptPayload/Ignored/UntrustedAccount`)
|
|
1217
|
+
return
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const defineAttrsFromTraceData = _dtDefineAttrsFromTraceData.bind(this)
|
|
1221
|
+
defineAttrsFromTraceData(data, transport)
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1150
1224
|
/**
|
|
1151
1225
|
* Returns parsed payload object after attempting to decode it from base64,
|
|
1152
1226
|
* and parsing the JSON string.
|
|
1153
1227
|
*
|
|
1154
|
-
* @param payload
|
|
1228
|
+
* @param {string} payload Payload string to be JSON.parsed
|
|
1229
|
+
* @returns {(object|null)} parsed JSON payload or null
|
|
1155
1230
|
*/
|
|
1156
1231
|
Transaction.prototype._getParsedPayload = function _getParsedPayload(payload) {
|
|
1157
1232
|
let parsed = payload
|
|
@@ -1305,7 +1380,7 @@ Transaction.prototype.addRequestParameters = addRequestParameters
|
|
|
1305
1380
|
* 'request.parameters.{key}'. These attributes will only be created
|
|
1306
1381
|
* when 'request.parameters.*' is included in the attribute config.
|
|
1307
1382
|
*
|
|
1308
|
-
* @param {Object<string, string>} requestParameters
|
|
1383
|
+
* @param {Object<string, string>} requestParameters of the request object
|
|
1309
1384
|
*/
|
|
1310
1385
|
function addRequestParameters(requestParameters) {
|
|
1311
1386
|
for (const key in requestParameters) {
|