newrelic 5.8.0 → 5.11.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/LICENSE +1 -12
- package/NEWS.md +103 -0
- package/THIRD_PARTY_NOTICES.md +239 -0
- package/api.js +52 -0
- package/lib/agent.js +47 -4
- package/lib/collector/api.js +12 -3
- package/lib/collector/serverless.js +2 -0
- package/lib/config/default.js +7 -1
- package/lib/config/env.js +7 -1
- package/lib/config/index.js +218 -25
- package/lib/config/merge-server-config.js +56 -0
- package/lib/errors/aggregator.js +99 -34
- package/lib/errors/helper.js +137 -0
- package/lib/errors/index.js +25 -35
- package/lib/feature_flags.js +1 -1
- package/lib/harvest.js +18 -8
- package/lib/instrumentation/@hapi/hapi.js +3 -0
- package/lib/instrumentation/core/http.js +10 -2
- package/lib/instrumentation/express.js +10 -0
- package/lib/instrumentations.js +1 -0
- package/lib/metrics/names.js +1 -0
- package/lib/serverless/aws-lambda.js +12 -1
- package/lib/shim/index.js +22 -0
- package/lib/shim/shim.js +72 -29
- package/lib/shim/webframework-shim.js +2 -1
- package/lib/shimmer.js +44 -35
- package/lib/transaction/index.js +38 -1
- package/lib/util/urltils.js +23 -0
- package/package.json +4 -4
package/lib/config/env.js
CHANGED
|
@@ -36,7 +36,10 @@ const ENV_MAPPING = {
|
|
|
36
36
|
enabled: 'NEW_RELIC_ERROR_COLLECTOR_ATTRIBUTES_ENABLED'
|
|
37
37
|
},
|
|
38
38
|
enabled: 'NEW_RELIC_ERROR_COLLECTOR_ENABLED',
|
|
39
|
-
ignore_status_codes: 'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERROR_CODES'
|
|
39
|
+
ignore_status_codes: 'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERROR_CODES',
|
|
40
|
+
ignore_classes: 'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERRORS',
|
|
41
|
+
expected_status_codes: 'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERROR_CODES',
|
|
42
|
+
expected_classes: 'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERRORS',
|
|
40
43
|
},
|
|
41
44
|
strip_exception_messages: {
|
|
42
45
|
enabled: 'NEW_RELIC_STRIP_EXCEPTION_MESSAGES_ENABLED'
|
|
@@ -147,6 +150,9 @@ const LIST_VARS = new Set([
|
|
|
147
150
|
'NEW_RELIC_ATTRIBUTES_EXCLUDE',
|
|
148
151
|
'NEW_RELIC_ATTRIBUTES_INCLUDE',
|
|
149
152
|
'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERROR_CODES',
|
|
153
|
+
'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERRORS',
|
|
154
|
+
'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERROR_CODES',
|
|
155
|
+
'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERRORS',
|
|
150
156
|
'NEW_RELIC_ERROR_COLLECTOR_ATTRIBUTES_EXCLUDE',
|
|
151
157
|
'NEW_RELIC_ERROR_COLLECTOR_ATTRIBUTES_INCLUDE',
|
|
152
158
|
'NEW_RELIC_TRANSACTION_TRACER_ATTRIBUTES_EXCLUDE',
|
package/lib/config/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const AttributeFilter = require('./attribute-filter')
|
|
4
4
|
const CollectorResponse = require('../collector/response')
|
|
5
|
+
const MergeServerConfig = require('./merge-server-config')
|
|
5
6
|
const copy = require('../util/copy')
|
|
6
7
|
const defaultConfig = require('./default').config
|
|
7
8
|
const EventEmitter = require('events').EventEmitter
|
|
@@ -31,9 +32,11 @@ const CONFIG_FILE_LOCATIONS = [
|
|
|
31
32
|
path.join(__dirname, '../../../..') // above node_modules
|
|
32
33
|
]
|
|
33
34
|
|
|
34
|
-
const HAS_ARBITRARY_KEYS = [
|
|
35
|
+
const HAS_ARBITRARY_KEYS = new Set([
|
|
36
|
+
'ignore_messages',
|
|
37
|
+
'expected_messages',
|
|
35
38
|
'labels'
|
|
36
|
-
]
|
|
39
|
+
])
|
|
37
40
|
|
|
38
41
|
const LASP_MAP = require('./lasp').LASP_MAP
|
|
39
42
|
const ENV = require('./env')
|
|
@@ -72,7 +75,7 @@ function fromObjectList(setting) {
|
|
|
72
75
|
try {
|
|
73
76
|
return JSON.parse('[' + setting + ']')
|
|
74
77
|
} catch (error) {
|
|
75
|
-
logger.error(
|
|
78
|
+
logger.error('New Relic configurator could not deserialize object list:')
|
|
76
79
|
logger.error(error.stack)
|
|
77
80
|
}
|
|
78
81
|
}
|
|
@@ -96,6 +99,8 @@ function _findConfigFile() {
|
|
|
96
99
|
function Config(config) {
|
|
97
100
|
EventEmitter.call(this)
|
|
98
101
|
|
|
102
|
+
// helper object for merging server side values
|
|
103
|
+
this.mergeServerConfig = new MergeServerConfig(this)
|
|
99
104
|
// 1. start by cloning the defaults
|
|
100
105
|
try {
|
|
101
106
|
Object.assign(this, defaultConfig())
|
|
@@ -151,7 +156,7 @@ function Config(config) {
|
|
|
151
156
|
this.browser_monitoring.loader_version = ''
|
|
152
157
|
|
|
153
158
|
// Settings to play nice with DLPs (see NODE-1044).
|
|
154
|
-
this.compressed_content_encoding =
|
|
159
|
+
this.compressed_content_encoding = 'deflate' // Deflate or gzip
|
|
155
160
|
this.simple_compression = false // Disables subcomponent compression
|
|
156
161
|
this.put_for_data_send = false // Changes http verb for harvest
|
|
157
162
|
|
|
@@ -178,10 +183,10 @@ function Config(config) {
|
|
|
178
183
|
if (this.high_security) {
|
|
179
184
|
if (this.security_policies_token) {
|
|
180
185
|
throw new Error(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
'Security Policies and High Security Mode cannot both be present ' +
|
|
187
|
+
'in the agent configuration. If Security Policies have been set ' +
|
|
188
|
+
'for your account, please ensure the security_policies_token is ' +
|
|
189
|
+
'set but high_security is disabled (default).'
|
|
185
190
|
)
|
|
186
191
|
}
|
|
187
192
|
this._applyHighSecurity()
|
|
@@ -398,7 +403,7 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
398
403
|
)
|
|
399
404
|
break
|
|
400
405
|
case 'error_collector.ignore_status_codes':
|
|
401
|
-
this.
|
|
406
|
+
this._validateThenUpdateStatusCodes(
|
|
402
407
|
params,
|
|
403
408
|
this.error_collector,
|
|
404
409
|
'error_collector.ignore_status_codes',
|
|
@@ -406,6 +411,47 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
406
411
|
)
|
|
407
412
|
this._canonicalize()
|
|
408
413
|
break
|
|
414
|
+
case 'error_collector.expected_status_codes':
|
|
415
|
+
this._validateThenUpdateStatusCodes(
|
|
416
|
+
params,
|
|
417
|
+
this.error_collector,
|
|
418
|
+
'error_collector.expected_status_codes',
|
|
419
|
+
'expected_status_codes'
|
|
420
|
+
)
|
|
421
|
+
this._canonicalize()
|
|
422
|
+
break
|
|
423
|
+
case 'error_collector.ignore_classes':
|
|
424
|
+
this._validateThenUpdateErrorClasses(
|
|
425
|
+
params,
|
|
426
|
+
this.error_collector,
|
|
427
|
+
'error_collector.ignore_classes',
|
|
428
|
+
'ignore_classes'
|
|
429
|
+
)
|
|
430
|
+
break
|
|
431
|
+
case 'error_collector.expected_classes':
|
|
432
|
+
this._validateThenUpdateErrorClasses(
|
|
433
|
+
params,
|
|
434
|
+
this.error_collector,
|
|
435
|
+
'error_collector.expected_classes',
|
|
436
|
+
'expected_classes'
|
|
437
|
+
)
|
|
438
|
+
break
|
|
439
|
+
case 'error_collector.ignore_messages':
|
|
440
|
+
this._validateThenUpdateErrorMessages(
|
|
441
|
+
params,
|
|
442
|
+
this.error_collector,
|
|
443
|
+
'error_collector.ignore_messages',
|
|
444
|
+
'ignore_messages'
|
|
445
|
+
)
|
|
446
|
+
break
|
|
447
|
+
case 'error_collector.expected_messages':
|
|
448
|
+
this._validateThenUpdateErrorMessages(
|
|
449
|
+
params,
|
|
450
|
+
this.error_collector,
|
|
451
|
+
'error_collector.expected_messages',
|
|
452
|
+
'expected_messages'
|
|
453
|
+
)
|
|
454
|
+
break
|
|
409
455
|
case 'error_collector.capture_events':
|
|
410
456
|
this._updateNestedIfChanged(
|
|
411
457
|
params,
|
|
@@ -550,6 +596,153 @@ Config.prototype._updateIfChanged = function _updateIfChanged(json, key) {
|
|
|
550
596
|
this._updateNestedIfChanged(json, this, key, key)
|
|
551
597
|
}
|
|
552
598
|
|
|
599
|
+
/**
|
|
600
|
+
* Expected and Ignored status code configuration values should look like this
|
|
601
|
+
*
|
|
602
|
+
* [500,'501','503-507']
|
|
603
|
+
*
|
|
604
|
+
* If the server side config is not in this format, it might put the agent
|
|
605
|
+
* in a world of hurt. So, before we pass everything on to
|
|
606
|
+
* _updateNestedIfChanged, we'll do some validation.
|
|
607
|
+
*
|
|
608
|
+
* @param {object} remote JSON sent from New Relic.
|
|
609
|
+
* @param {object} local A portion of this configuration object.
|
|
610
|
+
* @param {string} remoteKey The name sent by New Relic.
|
|
611
|
+
* @param {string} localKey The local name.
|
|
612
|
+
*/
|
|
613
|
+
Config.prototype._validateThenUpdateStatusCodes = _validateThenUpdateStatusCodes
|
|
614
|
+
function _validateThenUpdateStatusCodes(remote, local, remoteKey, localKey) {
|
|
615
|
+
let valueToTest = remote[remoteKey]
|
|
616
|
+
if (!Array.isArray(valueToTest)) {
|
|
617
|
+
logger.warn(
|
|
618
|
+
'Saw SSC (ignore|expect)_status_codes that is not an array, will not merge: %s',
|
|
619
|
+
valueToTest
|
|
620
|
+
)
|
|
621
|
+
return
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
let valid = true
|
|
625
|
+
valueToTest.forEach(function validateArray(thingToTest) {
|
|
626
|
+
if (!('string' === (typeof thingToTest) || 'number' === (typeof thingToTest))) {
|
|
627
|
+
logger.warn(
|
|
628
|
+
'Saw SSC (ignore|expect)_status_code that is not a number or string,' +
|
|
629
|
+
'will not merge: %s', thingToTest
|
|
630
|
+
)
|
|
631
|
+
valid = false
|
|
632
|
+
}
|
|
633
|
+
})
|
|
634
|
+
if (!valid) {
|
|
635
|
+
return
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return this._updateNestedIfChanged(remote, local, remoteKey, localKey)
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Expected and Ignored classes configuration values should look like this
|
|
643
|
+
*
|
|
644
|
+
* ['Error','Again']
|
|
645
|
+
*
|
|
646
|
+
* If the server side config is not in this format, it might put the agent
|
|
647
|
+
* in a world of hurt. So, before we pass everything on to
|
|
648
|
+
* _updateNestedIfChanged, we'll do some validation.
|
|
649
|
+
*
|
|
650
|
+
* @param {object} remote JSON sent from New Relic.
|
|
651
|
+
* @param {object} local A portion of this configuration object.
|
|
652
|
+
* @param {string} remoteKey The name sent by New Relic.
|
|
653
|
+
* @param {string} localKey The local name.
|
|
654
|
+
*/
|
|
655
|
+
Config.prototype._validateThenUpdateErrorClasses = _validateThenUpdateErrorClasses
|
|
656
|
+
|
|
657
|
+
function _validateThenUpdateErrorClasses(remote, local, remoteKey, localKey) {
|
|
658
|
+
let valueToTest = remote[remoteKey]
|
|
659
|
+
if (!Array.isArray(valueToTest)) {
|
|
660
|
+
logger.warn(
|
|
661
|
+
'Saw SSC (ignore|expect)_classes that is not an array, will not merge: %s',
|
|
662
|
+
valueToTest
|
|
663
|
+
)
|
|
664
|
+
return
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
let valid = true
|
|
668
|
+
Object.keys(valueToTest).forEach(function validateArray(key) {
|
|
669
|
+
let thingToTest = valueToTest[key]
|
|
670
|
+
if ('string' !== (typeof thingToTest)) {
|
|
671
|
+
logger.warn(
|
|
672
|
+
'Saw SSC (ignore|expect)_class that is not a string, will not merge: %s',
|
|
673
|
+
thingToTest
|
|
674
|
+
)
|
|
675
|
+
valid = false
|
|
676
|
+
}
|
|
677
|
+
})
|
|
678
|
+
if (!valid) {
|
|
679
|
+
return
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return this._updateNestedIfChanged(remote, local, remoteKey, localKey)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Expected and Ignore messages configuration values should look like this
|
|
687
|
+
*
|
|
688
|
+
* {'ErrorType':['Error Message']}
|
|
689
|
+
*
|
|
690
|
+
* If the server side config is not in this format, it might put the agent
|
|
691
|
+
* in a world of hurt. So, before we pass everything on to
|
|
692
|
+
* _updateNestedIfChanged, we'll do some validation.
|
|
693
|
+
*
|
|
694
|
+
* @param {object} remote JSON sent from New Relic.
|
|
695
|
+
* @param {object} local A portion of this configuration object.
|
|
696
|
+
* @param {string} remoteKey The name sent by New Relic.
|
|
697
|
+
* @param {string} localKey The local name.
|
|
698
|
+
*/
|
|
699
|
+
Config.prototype._validateThenUpdateErrorMessages = _validateThenUpdateErrorMessages
|
|
700
|
+
|
|
701
|
+
function _validateThenUpdateErrorMessages(remote, local, remoteKey, localKey) {
|
|
702
|
+
let valueToTest = remote[remoteKey]
|
|
703
|
+
if (Array.isArray(valueToTest)) {
|
|
704
|
+
logger.warn(
|
|
705
|
+
'Saw SSC (ignore|expect)_message that is an Array, will not merge: %s',
|
|
706
|
+
valueToTest
|
|
707
|
+
)
|
|
708
|
+
return
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (!valueToTest) {
|
|
712
|
+
logger.warn('SSC ignore|expect_message is null or undefined, will not merge')
|
|
713
|
+
return
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if ('object' !== typeof valueToTest) {
|
|
717
|
+
logger.warn(
|
|
718
|
+
'Saw SSC (ignore|expect)_message that is primitive/scaler, will not merge: %s',
|
|
719
|
+
valueToTest
|
|
720
|
+
)
|
|
721
|
+
return
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (!valueToTest) {
|
|
725
|
+
logger.warn('SSC ignore|expect_message is null or undefined, will not merge')
|
|
726
|
+
return
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
let valid = true
|
|
730
|
+
Object.keys(valueToTest).forEach(function validateArray(key) {
|
|
731
|
+
let arrayToTest = valueToTest[key]
|
|
732
|
+
if (!Array.isArray(arrayToTest)) {
|
|
733
|
+
logger.warn(
|
|
734
|
+
'Saw SSC message array that is not an array, will not merge: %s',
|
|
735
|
+
arrayToTest
|
|
736
|
+
)
|
|
737
|
+
valid = false
|
|
738
|
+
}
|
|
739
|
+
})
|
|
740
|
+
if (!valid) {
|
|
741
|
+
return
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return this._updateNestedIfChanged(remote, local, remoteKey, localKey)
|
|
745
|
+
}
|
|
553
746
|
/**
|
|
554
747
|
* Some parameter values are nested, need a simple way to change them as well.
|
|
555
748
|
* Will merge local and remote if and only if both are arrays.
|
|
@@ -572,18 +765,13 @@ function _updateNestedIfChanged(remote, local, remoteKey, localKey) {
|
|
|
572
765
|
Config.prototype._updateNestedIfChangedRaw = _updateNestedIfChangedRaw
|
|
573
766
|
|
|
574
767
|
function _updateNestedIfChangedRaw(remote, local, remoteKey, localKey) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
local[localKey] = value
|
|
583
|
-
}
|
|
584
|
-
this.emit(remoteKey, value)
|
|
585
|
-
logger.debug('Configuration of %s was changed to %s by New Relic.', remoteKey, value)
|
|
586
|
-
}
|
|
768
|
+
return this.mergeServerConfig.updateNestedIfChanged(
|
|
769
|
+
remote,
|
|
770
|
+
local,
|
|
771
|
+
remoteKey,
|
|
772
|
+
localKey,
|
|
773
|
+
logger
|
|
774
|
+
)
|
|
587
775
|
}
|
|
588
776
|
|
|
589
777
|
/**
|
|
@@ -645,7 +833,7 @@ Config.prototype.logUnsupported = function logUnsupported(json, key) {
|
|
|
645
833
|
Config.prototype.logUnknown = function logUnknown(json, key) {
|
|
646
834
|
var value = json[key]
|
|
647
835
|
logger.debug(
|
|
648
|
-
|
|
836
|
+
'New Relic sent unknown configuration parameter %s with value %s.',
|
|
649
837
|
key,
|
|
650
838
|
value
|
|
651
839
|
)
|
|
@@ -813,8 +1001,8 @@ Config.prototype._fromPassed = function _fromPassed(external, internal, arbitrar
|
|
|
813
1001
|
|
|
814
1002
|
if (typeof node === 'object' && !Array.isArray(node)) {
|
|
815
1003
|
// is top level and can have arbitrary keys
|
|
816
|
-
var
|
|
817
|
-
this._fromPassed(node, internal[key],
|
|
1004
|
+
var allowArbitrary = internal === this || HAS_ARBITRARY_KEYS.has(key)
|
|
1005
|
+
this._fromPassed(node, internal[key], allowArbitrary)
|
|
818
1006
|
} else {
|
|
819
1007
|
internal[key] = node
|
|
820
1008
|
}
|
|
@@ -994,6 +1182,11 @@ Config.prototype._canonicalize = function _canonicalize() {
|
|
|
994
1182
|
this.error_collector.ignore_status_codes = _parseCodes(statusCodes)
|
|
995
1183
|
}
|
|
996
1184
|
|
|
1185
|
+
const expectedCodes = this.error_collector && this.error_collector.expected_status_codes
|
|
1186
|
+
if (statusCodes) {
|
|
1187
|
+
this.error_collector.expected_status_codes = _parseCodes(expectedCodes)
|
|
1188
|
+
}
|
|
1189
|
+
|
|
997
1190
|
var logAliases = {
|
|
998
1191
|
verbose: 'trace',
|
|
999
1192
|
debugging: 'debug',
|
|
@@ -1327,7 +1520,7 @@ function initialize(config) {
|
|
|
1327
1520
|
|
|
1328
1521
|
config = new Config(userConf)
|
|
1329
1522
|
config.config_file_path = filepath
|
|
1330
|
-
logger.debug(
|
|
1523
|
+
logger.debug('Using configuration file %s.', filepath)
|
|
1331
1524
|
|
|
1332
1525
|
config.validateFlags()
|
|
1333
1526
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
class MergeServerConfig {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
updateNestedIfChanged(remote, local, remoteKey, localKey, logger) {
|
|
8
|
+
var value = remote[remoteKey]
|
|
9
|
+
|
|
10
|
+
// if the value hasn't changed, skip this work
|
|
11
|
+
if (value === null || local[localKey] === value) {
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// we need different update/merge logic if the server
|
|
16
|
+
// value is an array, a simple object, or anything else
|
|
17
|
+
if (Array.isArray(value) && Array.isArray(local[localKey])) {
|
|
18
|
+
this.updateArray(value, local, localKey)
|
|
19
|
+
} else if (this.isSimpleObject(value) && this.isSimpleObject(local[localKey])) {
|
|
20
|
+
this.updateObject(value, local, localKey)
|
|
21
|
+
} else {
|
|
22
|
+
local[localKey] = value
|
|
23
|
+
}
|
|
24
|
+
this.config.emit(remoteKey, value)
|
|
25
|
+
logger.debug('Configuration of %s was changed to %s by New Relic.', remoteKey, value)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
updateArray(value, local, localKey) {
|
|
29
|
+
value.forEach( (element) => {
|
|
30
|
+
if (local[localKey].indexOf(element) === -1) local[localKey].push(element)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
updateObject(value, local, localKey) {
|
|
35
|
+
// go through each key of the object and update it
|
|
36
|
+
Object.keys(value).forEach( (element) => {
|
|
37
|
+
if (Array.isArray(local[localKey][element]) && Array.isArray(value[element])) {
|
|
38
|
+
// if both key-values are arrays, push the remote value onto the local array
|
|
39
|
+
value[element].forEach( (elementValue) => {
|
|
40
|
+
if (-1 === local[localKey][element].indexOf(elementValue)) {
|
|
41
|
+
local[localKey][element].push(elementValue)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
} else {
|
|
45
|
+
// otherwise, replace the local value with the server value
|
|
46
|
+
local[localKey][element] = value[element]
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
isSimpleObject(thing) {
|
|
52
|
+
return 'object' === (typeof thing) && this !== null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = MergeServerConfig
|
package/lib/errors/aggregator.js
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
const errorsModule = require('./index')
|
|
4
4
|
const EventAggregator = require('../event-aggregator')
|
|
5
5
|
const logger = require('../logger').child({component: 'error_tracer'})
|
|
6
|
-
const NAMES = require('../metrics/names')
|
|
7
6
|
const urltils = require('../util/urltils')
|
|
8
|
-
|
|
7
|
+
const errorHelper = require('../errors/helper')
|
|
9
8
|
const createError = errorsModule.createError
|
|
10
9
|
const createEvent = errorsModule.createEvent
|
|
11
10
|
|
|
@@ -37,6 +36,11 @@ class ErrorAggregator extends EventAggregator {
|
|
|
37
36
|
this.errorCount = 0
|
|
38
37
|
this.webTransactionErrorCount = 0
|
|
39
38
|
this.otherTransactionErrorCount = 0
|
|
39
|
+
|
|
40
|
+
this.expectedErrorCount = 0
|
|
41
|
+
this.expectedWebTransactionErrorCount = 0
|
|
42
|
+
this.expectedOtherTransactionErrorCount = 0
|
|
43
|
+
|
|
40
44
|
this.errors = []
|
|
41
45
|
this.seenObjectsByTransaction = Object.create(null)
|
|
42
46
|
this.seenStringsByTransaction = Object.create(null)
|
|
@@ -47,17 +51,21 @@ class ErrorAggregator extends EventAggregator {
|
|
|
47
51
|
* possible.
|
|
48
52
|
*
|
|
49
53
|
* @param {Transaction} transaction
|
|
50
|
-
*
|
|
54
|
+
*
|
|
55
|
+
* @return {number} The number of unexpected errors
|
|
51
56
|
*/
|
|
52
|
-
onTransactionFinished(transaction
|
|
57
|
+
onTransactionFinished(transaction) {
|
|
53
58
|
if (!transaction) throw new Error('Error collector got a blank transaction.')
|
|
54
|
-
if (!metrics) throw new Error('Error collector requires metrics to count errors.')
|
|
55
59
|
if (transaction.ignore) {
|
|
56
60
|
return
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
// collect user errors even if status code is ignored
|
|
60
64
|
let collectedErrors = 0
|
|
65
|
+
let expectedErrors = 0
|
|
66
|
+
|
|
67
|
+
// errors from noticeError are currently exempt from
|
|
68
|
+
// ignore and exclude rules
|
|
61
69
|
if (transaction.userErrors.length) {
|
|
62
70
|
for (let i = 0; i < transaction.userErrors.length; i++) {
|
|
63
71
|
const exception = transaction.userErrors[i]
|
|
@@ -68,28 +76,38 @@ class ErrorAggregator extends EventAggregator {
|
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
const isErroredTransaction = urltils.isError(this.config, transaction.statusCode)
|
|
71
|
-
const
|
|
79
|
+
const isExpectedErrorStatusCode = urltils.isExpectedError(
|
|
72
80
|
this.config,
|
|
73
81
|
transaction.statusCode
|
|
74
82
|
)
|
|
75
83
|
|
|
76
84
|
// collect other exceptions only if status code is not ignored
|
|
77
|
-
if (transaction.exceptions.length
|
|
85
|
+
if (transaction.exceptions.length) {
|
|
78
86
|
for (let i = 0; i < transaction.exceptions.length; i++) {
|
|
79
87
|
const exception = transaction.exceptions[i]
|
|
80
|
-
if (this.
|
|
88
|
+
if (this.collect(transaction, exception[0], exception[1], exception[2])) {
|
|
81
89
|
++collectedErrors
|
|
90
|
+
// if we could collect it, then check if expected
|
|
91
|
+
if (isExpectedErrorStatusCode ||
|
|
92
|
+
errorHelper.isExpectedException(
|
|
93
|
+
transaction,
|
|
94
|
+
exception[0],
|
|
95
|
+
this.config,
|
|
96
|
+
urltils
|
|
97
|
+
)
|
|
98
|
+
) {
|
|
99
|
+
++expectedErrors
|
|
100
|
+
}
|
|
82
101
|
}
|
|
83
102
|
}
|
|
84
|
-
} else if (isErroredTransaction && this.
|
|
103
|
+
} else if (isErroredTransaction && this.collect(transaction)) {
|
|
85
104
|
++collectedErrors
|
|
105
|
+
if (isExpectedErrorStatusCode) {
|
|
106
|
+
++expectedErrors
|
|
107
|
+
}
|
|
86
108
|
}
|
|
87
109
|
|
|
88
|
-
|
|
89
|
-
if (collectedErrors > 0) {
|
|
90
|
-
metrics.getOrCreateMetric(NAMES.ERRORS.PREFIX + transaction.getFullName())
|
|
91
|
-
.incrementCallCount(collectedErrors)
|
|
92
|
-
}
|
|
110
|
+
return collectedErrors - expectedErrors
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
/**
|
|
@@ -115,7 +133,7 @@ class ErrorAggregator extends EventAggregator {
|
|
|
115
133
|
if (transaction) {
|
|
116
134
|
transaction.addException(exception, customAttributes, timestamp)
|
|
117
135
|
} else {
|
|
118
|
-
this.
|
|
136
|
+
this.collect(transaction, exception, customAttributes, timestamp)
|
|
119
137
|
}
|
|
120
138
|
}
|
|
121
139
|
|
|
@@ -192,6 +210,34 @@ class ErrorAggregator extends EventAggregator {
|
|
|
192
210
|
return false
|
|
193
211
|
}
|
|
194
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Wrapper for _collect, include logic for whether an error should
|
|
215
|
+
* be ignored or not. Exists to allow userErrors to bypass ignore
|
|
216
|
+
* logic.
|
|
217
|
+
*
|
|
218
|
+
* NOTE: this interface is unofficial and may change in future.
|
|
219
|
+
*
|
|
220
|
+
* @param {?Transaction} transaction
|
|
221
|
+
* Transaction associated with the error.
|
|
222
|
+
*
|
|
223
|
+
* @param {?Error} exception
|
|
224
|
+
* The error to be traced.
|
|
225
|
+
*
|
|
226
|
+
* @param {?object} customAttributes
|
|
227
|
+
* Any custom attributes associated with the request.
|
|
228
|
+
*
|
|
229
|
+
* @param {number} timestamp
|
|
230
|
+
*
|
|
231
|
+
* @return {bool} True if the error was collected.
|
|
232
|
+
*/
|
|
233
|
+
collect(transaction, exception, customAttributes, timestamp) {
|
|
234
|
+
if (errorHelper.shouldIgnoreError(transaction, exception, this.config)) {
|
|
235
|
+
logger.trace("Ignoring error")
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
return this._collect(transaction, exception, customAttributes, timestamp)
|
|
239
|
+
}
|
|
240
|
+
|
|
195
241
|
/**
|
|
196
242
|
* Collects the error and also creates the error event.
|
|
197
243
|
*
|
|
@@ -235,16 +281,6 @@ class ErrorAggregator extends EventAggregator {
|
|
|
235
281
|
return
|
|
236
282
|
}
|
|
237
283
|
|
|
238
|
-
this.errorCount++
|
|
239
|
-
|
|
240
|
-
if (transaction) {
|
|
241
|
-
if (transaction.isWeb()) {
|
|
242
|
-
this.webTransactionErrorCount++
|
|
243
|
-
} else {
|
|
244
|
-
this.otherTransactionErrorCount++
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
284
|
// allow enabling & disabling the error tracer at runtime
|
|
249
285
|
// TODO: it would be better to check config in the public add() to prevents collecting
|
|
250
286
|
// errors on the transaction unnecessarily
|
|
@@ -260,8 +296,29 @@ class ErrorAggregator extends EventAggregator {
|
|
|
260
296
|
logger.trace(exception, 'Got exception to trace:')
|
|
261
297
|
}
|
|
262
298
|
|
|
299
|
+
this.errorCount++
|
|
300
|
+
|
|
301
|
+
if (transaction) {
|
|
302
|
+
if (transaction.isWeb()) {
|
|
303
|
+
this.webTransactionErrorCount++
|
|
304
|
+
} else {
|
|
305
|
+
this.otherTransactionErrorCount++
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
263
309
|
var error = createError(transaction, exception, customAttributes, this.config)
|
|
264
310
|
|
|
311
|
+
if (true === error[4].intrinsics['error.expected'] ) {
|
|
312
|
+
this.expectedErrorCount++
|
|
313
|
+
if (transaction) {
|
|
314
|
+
if (transaction.isWeb()) {
|
|
315
|
+
this.expectedWebTransactionErrorCount++
|
|
316
|
+
} else {
|
|
317
|
+
this.expectedOtherTransactionErrorCount++
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
265
322
|
if (this.errors.length < MAX_ERRORS) {
|
|
266
323
|
logger.debug(error, 'Error to be sent to collector.')
|
|
267
324
|
this.errors.push(error)
|
|
@@ -288,24 +345,32 @@ class ErrorAggregator extends EventAggregator {
|
|
|
288
345
|
}
|
|
289
346
|
|
|
290
347
|
/**
|
|
291
|
-
* Returns total number of
|
|
348
|
+
* Returns total number of errors that matched `expected_*` configuration.
|
|
349
|
+
* Expected errors do not impact standard error counts.
|
|
350
|
+
*/
|
|
351
|
+
getTotalExpectedErrorCount() {
|
|
352
|
+
return this.expectedErrorCount
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Returns total number of unexpected errors.
|
|
292
357
|
*/
|
|
293
|
-
|
|
294
|
-
return this.errorCount
|
|
358
|
+
getTotalUnexpectedErrorCount() {
|
|
359
|
+
return this.errorCount - this.expectedErrorCount
|
|
295
360
|
}
|
|
296
361
|
|
|
297
362
|
/**
|
|
298
|
-
* Returns total number of errors collected during web transactions.
|
|
363
|
+
* Returns total number of unexpected errors collected during web transactions.
|
|
299
364
|
*/
|
|
300
|
-
|
|
301
|
-
return this.webTransactionErrorCount
|
|
365
|
+
getUnexpectedWebTransactionsErrorCount() {
|
|
366
|
+
return this.webTransactionErrorCount - this.expectedWebTransactionErrorCount
|
|
302
367
|
}
|
|
303
368
|
|
|
304
369
|
/**
|
|
305
|
-
* Returns total number of errors collected during background transactions.
|
|
370
|
+
* Returns total number of unexpected errors collected during background transactions.
|
|
306
371
|
*/
|
|
307
|
-
|
|
308
|
-
return this.otherTransactionErrorCount
|
|
372
|
+
getUnexpectedOtherTransactionsErrorCount() {
|
|
373
|
+
return this.otherTransactionErrorCount - this.expectedOtherTransactionErrorCount
|
|
309
374
|
}
|
|
310
375
|
|
|
311
376
|
/**
|