newrelic 6.4.2 → 6.5.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/{.eslintrc → .eslintrc.js} +2 -5
- package/.travis.yml +0 -1
- package/NEWS.md +27 -1
- package/api.js +72 -1
- package/lib/attributes.js +15 -6
- package/lib/config/index.js +4 -0
- package/lib/config/lasp.js +16 -1
- package/lib/errors/error-collector.js +51 -90
- package/lib/errors/helper.js +13 -13
- package/lib/errors/index.js +36 -18
- package/lib/instrumentation/core/child_process.js +12 -0
- package/lib/instrumentation/core/http-outbound.js +4 -0
- package/lib/instrumentation/hapi/hapi-17.js +10 -3
- package/lib/spans/span-event.js +22 -9
- package/lib/transaction/index.js +36 -20
- package/lib/transaction/trace/index.js +2 -2
- package/lib/transaction/trace/segment.js +12 -1
- package/package.json +3 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{
|
|
1
|
+
module.exports = {
|
|
2
2
|
"env": {
|
|
3
3
|
"es6": true,
|
|
4
4
|
"node": true,
|
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
"parserOptions": {
|
|
8
8
|
"ecmaVersion": 6
|
|
9
9
|
},
|
|
10
|
-
"
|
|
11
|
-
"mocha-no-only"
|
|
12
|
-
],
|
|
10
|
+
"ignorePatterns": ["invalid-json/"],
|
|
13
11
|
"rules": {
|
|
14
12
|
"indent": ["warn", 2, {"SwitchCase": 1}],
|
|
15
13
|
"brace-style": "error",
|
|
@@ -27,7 +25,6 @@
|
|
|
27
25
|
"max-len": ["error", 100, { "ignoreUrls": true }],
|
|
28
26
|
"max-nested-callbacks": ["error", 3],
|
|
29
27
|
"max-params": ["error", 5],
|
|
30
|
-
"mocha-no-only/mocha-no-only": ["error"],
|
|
31
28
|
"new-cap": "error",
|
|
32
29
|
"no-console": "warn",
|
|
33
30
|
"no-debugger": "error",
|
package/.travis.yml
CHANGED
package/NEWS.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
### 6.5.0 (2020-03-18):
|
|
2
|
+
|
|
3
|
+
* Added error attributes to spans.
|
|
4
|
+
* The public api method `noticeError()` now attaches exception details to the currently executing
|
|
5
|
+
span. Spans with error details are now highlighted red in the Distributed Tracing UI. Also, the
|
|
6
|
+
attributes `error.class` and `error.message` are added to the span. If multiple errors are
|
|
7
|
+
recorded for a single span, only the final error's attributes will be added to the span.
|
|
8
|
+
|
|
9
|
+
* Added ID of the span in which an error occurred to the corresponding transaction error event.
|
|
10
|
+
|
|
11
|
+
* Added new public API methods `addCustomSpanAttribute` and `addCustomSpanAttributes` to add
|
|
12
|
+
attributes to the currently executing span.
|
|
13
|
+
|
|
14
|
+
* Added new attributes to http outbound spans: `http.statusCode` and `http.statusText`.
|
|
15
|
+
|
|
16
|
+
* Updated W3C Trace Context "Known Issues and Workaround" notes with latest accurate consideration.
|
|
17
|
+
|
|
18
|
+
* Converted unit tests to run via `tap`. Removes `mocha` dependency.
|
|
19
|
+
|
|
20
|
+
* Fixed route naming when hapi's `pre` route handlers share functions.
|
|
21
|
+
|
|
22
|
+
* Fixed `child_process` instrumentation so that handlers can be effectively removed
|
|
23
|
+
when attached via `.once()` or manually removed via `removeListener()`.
|
|
24
|
+
|
|
1
25
|
### 6.4.2 (2020-02-27):
|
|
2
26
|
|
|
3
27
|
* Support new http[s] get/request function signature in Node v10+
|
|
@@ -92,7 +116,9 @@
|
|
|
92
116
|
|
|
93
117
|
Known Issues and Workarounds
|
|
94
118
|
|
|
95
|
-
* If a .NET agent is initiating traces as the root service,
|
|
119
|
+
* If a .NET agent is initiating distributed traces as the root service, you must update
|
|
120
|
+
that .NET agent to version `8.24` or later before upgrading your downstream Node
|
|
121
|
+
New Relic agents to this agent release.
|
|
96
122
|
|
|
97
123
|
* Pins Node 12 version to `v12.15` to avoid breakages with `v12.16.0` until cause(s)
|
|
98
124
|
resolved.
|
package/api.js
CHANGED
|
@@ -340,6 +340,77 @@ API.prototype.addCustomAttributes = function addCustomAttributes(atts) {
|
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Add custom span attributes in an object to the current segment/span.
|
|
345
|
+
*
|
|
346
|
+
* See documentation for newrelic.addCustomSpanAttribute for more information.
|
|
347
|
+
*
|
|
348
|
+
* An example of setting a custom span attribute:
|
|
349
|
+
*
|
|
350
|
+
* newrelic.addCustomSpanAttribute({test: 'value', test2: 'value2'})
|
|
351
|
+
*
|
|
352
|
+
* @param {object} [atts]
|
|
353
|
+
* @param {string} [atts.KEY] The name you want displayed in the RPM UI.API.
|
|
354
|
+
* @param {string} [atts.KEY.VALUE] The value you want displayed. Must be serializable.
|
|
355
|
+
*/
|
|
356
|
+
API.prototype.addCustomSpanAttributes = function addCustomSpanAttributes(atts) {
|
|
357
|
+
const metric = this.agent.metrics.getOrCreateMetric(
|
|
358
|
+
NAMES.SUPPORTABILITY.API + '/addCustomSpanAttributes'
|
|
359
|
+
)
|
|
360
|
+
metric.incrementCallCount()
|
|
361
|
+
|
|
362
|
+
for (let key in atts) {
|
|
363
|
+
if (properties.hasOwn(atts, key)) {
|
|
364
|
+
this.addCustomSpanAttribute(key, atts[key])
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Add a custom span attribute to the current transaction. Some attributes
|
|
371
|
+
* are reserved (see CUSTOM_DENYLIST for the current, very short list), and
|
|
372
|
+
* as with most API methods, this must be called in the context of an
|
|
373
|
+
* active segment/span. Most recently set value wins.
|
|
374
|
+
*
|
|
375
|
+
* @param {string} key The key you want displayed in the RPM UI.
|
|
376
|
+
* @param {string} value The value you want displayed. Must be serializable.
|
|
377
|
+
*/
|
|
378
|
+
API.prototype.addCustomSpanAttribute = function addCustomSpanAttribute(key, value) {
|
|
379
|
+
const metric = this.agent.metrics.getOrCreateMetric(
|
|
380
|
+
NAMES.SUPPORTABILITY.API + '/addCustomSpanAttribute'
|
|
381
|
+
)
|
|
382
|
+
metric.incrementCallCount()
|
|
383
|
+
|
|
384
|
+
// If high security mode is on, custom attributes are disabled.
|
|
385
|
+
if (this.agent.config.high_security) {
|
|
386
|
+
logger.warnOnce(
|
|
387
|
+
'Custom span attributes',
|
|
388
|
+
'Custom span attributes are disabled by high security mode.'
|
|
389
|
+
)
|
|
390
|
+
return false
|
|
391
|
+
} else if (!this.agent.config.api.custom_attributes_enabled) {
|
|
392
|
+
logger.debug(
|
|
393
|
+
'Config.api.custom_attributes_enabled set to false, not collecting value'
|
|
394
|
+
)
|
|
395
|
+
return false
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const segment = this.agent.tracer.getSegment()
|
|
399
|
+
|
|
400
|
+
if (!segment) {
|
|
401
|
+
return logger.debug(
|
|
402
|
+
'Could not add attribute %s. No available span/segment.',
|
|
403
|
+
key
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (CUSTOM_DENYLIST.has(key)) {
|
|
408
|
+
return logger.warn('Not overwriting value of NR-only attribute %s.', key)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
segment.addCustomSpanAttribute(key, value)
|
|
412
|
+
}
|
|
413
|
+
|
|
343
414
|
API.prototype.setIgnoreTransaction = util.deprecate(
|
|
344
415
|
setIgnoreTransaction, [
|
|
345
416
|
'API#setIgnoreTransaction is being deprecated!',
|
|
@@ -415,7 +486,6 @@ API.prototype.noticeError = function noticeError(error, customAttributes) {
|
|
|
415
486
|
if (typeof error === 'string') {
|
|
416
487
|
error = new Error(error)
|
|
417
488
|
}
|
|
418
|
-
const transaction = this.agent.tracer.getTransaction()
|
|
419
489
|
|
|
420
490
|
// Filter all object type valued attributes out
|
|
421
491
|
let filteredAttributes = customAttributes
|
|
@@ -423,6 +493,7 @@ API.prototype.noticeError = function noticeError(error, customAttributes) {
|
|
|
423
493
|
filteredAttributes = _filterAttributes(customAttributes, 'noticeError')
|
|
424
494
|
}
|
|
425
495
|
|
|
496
|
+
const transaction = this.agent.tracer.getTransaction()
|
|
426
497
|
this.agent.errors.addUserError(transaction, error, filteredAttributes)
|
|
427
498
|
}
|
|
428
499
|
|
package/lib/attributes.js
CHANGED
|
@@ -6,6 +6,8 @@ const isValidType = require('./util/attribute-types')
|
|
|
6
6
|
const byteUtils = require('./util/byte-limit')
|
|
7
7
|
const properties = require('./util/properties')
|
|
8
8
|
|
|
9
|
+
const MAXIMUM_CUSTOM_ATTRIBUTES = 64
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* @class
|
|
11
13
|
* @private
|
|
@@ -23,6 +25,7 @@ class Attributes {
|
|
|
23
25
|
this.filter = makeFilter(scope)
|
|
24
26
|
this.limit = limit
|
|
25
27
|
this.attributes = Object.create(null)
|
|
28
|
+
this.attributeCount = 0
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/**
|
|
@@ -57,7 +60,6 @@ class Attributes {
|
|
|
57
60
|
*/
|
|
58
61
|
get(dest) {
|
|
59
62
|
const attrs = Object.create(null)
|
|
60
|
-
let attrCount = 0
|
|
61
63
|
for (let key in this.attributes) { // eslint-disable-line guard-for-in
|
|
62
64
|
const attr = this.attributes[key]
|
|
63
65
|
if (!(attr.destinations & dest)) {
|
|
@@ -67,10 +69,6 @@ class Attributes {
|
|
|
67
69
|
attrs[key] = typeof attr.value === 'string' && !attr.truncateExempt
|
|
68
70
|
? byteUtils.truncate(attr.value, 255)
|
|
69
71
|
: attr.value
|
|
70
|
-
|
|
71
|
-
if (++attrCount >= this.limit) {
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
return attrs
|
|
@@ -103,6 +101,13 @@ class Attributes {
|
|
|
103
101
|
* @param {boolean} [truncateExempt=false] - Flag marking value exempt from truncation
|
|
104
102
|
*/
|
|
105
103
|
addAttribute(destinations, key, value, truncateExempt = false) {
|
|
104
|
+
if (this.attributeCount + 1 > this.limit) {
|
|
105
|
+
return logger.debug(
|
|
106
|
+
`Maximum number of custom attributes have been added.
|
|
107
|
+
Dropping attribute ${key} with ${value} type.`
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
106
111
|
if (!isValidType(value)) {
|
|
107
112
|
return logger.debug(
|
|
108
113
|
'Not adding attribute %s with %s value type. This is expected for undefined' +
|
|
@@ -123,6 +128,7 @@ class Attributes {
|
|
|
123
128
|
// Only set the attribute if at least one destination passed
|
|
124
129
|
const validDestinations = this.filter(destinations, key)
|
|
125
130
|
if (validDestinations) {
|
|
131
|
+
this.attributeCount = this.attributeCount + 1
|
|
126
132
|
this._set(validDestinations, key, value, truncateExempt)
|
|
127
133
|
}
|
|
128
134
|
}
|
|
@@ -159,4 +165,7 @@ function makeFilter(scope) {
|
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
|
|
162
|
-
module.exports =
|
|
168
|
+
module.exports = {
|
|
169
|
+
Attributes: Attributes,
|
|
170
|
+
MAXIMUM_CUSTOM_ATTRIBUTES: MAXIMUM_CUSTOM_ATTRIBUTES
|
|
171
|
+
}
|
package/lib/config/index.js
CHANGED
|
@@ -1458,8 +1458,12 @@ Config.prototype.applyLasp = function applyLasp(agent, policies) {
|
|
|
1458
1458
|
}
|
|
1459
1459
|
var valueName = splitConfigName[splitConfigName.length - 1]
|
|
1460
1460
|
var localVal = settingBlock[valueName]
|
|
1461
|
+
|
|
1462
|
+
// Indexes into "allowed values" based on "enabled" setting
|
|
1463
|
+
// to retreive proper mapping.
|
|
1461
1464
|
var policyValues = localMapping.allowedValues
|
|
1462
1465
|
var policyValue = policyValues[policy.enabled ? 1 : 0]
|
|
1466
|
+
|
|
1463
1467
|
// get the most secure setting between local config and the policy
|
|
1464
1468
|
var finalValue = settingBlock[valueName] = config._getMostSecure(
|
|
1465
1469
|
name,
|
package/lib/config/lasp.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// TODO: would likely be easier to understand if the allowedValues mapping
|
|
4
|
+
// just took the raw enabled/disabled and translated. This is not a hot path.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* path: Full nested path for the local configuration item.
|
|
8
|
+
* allowedValues:
|
|
9
|
+
* Array of valid config values to map for incoming enabled/disabled.
|
|
10
|
+
* policy.enabled: false uses index 0, policy.enabled: true uses index 1.
|
|
11
|
+
* filter: Allows for calculating most secure setting to use
|
|
12
|
+
* applyAdditionalSettings: Applies additional settings that are required
|
|
13
|
+
* when the policy is disabled.
|
|
14
|
+
* clearData: Clears the relevant agent collection.
|
|
15
|
+
*/
|
|
3
16
|
const LASP_MAP = {
|
|
4
17
|
// LASP key
|
|
5
18
|
record_sql: {
|
|
6
19
|
// full path to corresponding config key
|
|
7
20
|
path: 'transaction_tracer.record_sql',
|
|
8
21
|
// Mapping from policy enabled status to usable config value
|
|
9
|
-
//
|
|
22
|
+
// policy.enabled: false === off, policy.enabled: true === 'obfuscated'
|
|
10
23
|
allowedValues: ['off', 'obfuscated'],
|
|
11
24
|
// Tracks the precedent of settings controlled by LASP.
|
|
12
25
|
filter: function mostSecureRecordSQL(first, second) {
|
|
@@ -45,6 +58,8 @@ const LASP_MAP = {
|
|
|
45
58
|
// TODO: rename config key, because the names contradict each other's behavior
|
|
46
59
|
allow_raw_exception_messages: {
|
|
47
60
|
path: 'strip_exception_messages.enabled',
|
|
61
|
+
// if raw messages are allowed, then we should not strip them
|
|
62
|
+
// policy.enabled: false === true, policy.enabled: true === false
|
|
48
63
|
allowedValues: [true, false],
|
|
49
64
|
filter: function mostSecureStripException(first, second) {
|
|
50
65
|
return first || second
|
|
@@ -4,6 +4,7 @@ const errorsModule = require('./index')
|
|
|
4
4
|
|
|
5
5
|
const logger = require('../logger').child({component: 'error_tracer'})
|
|
6
6
|
const urltils = require('../util/urltils')
|
|
7
|
+
const Exception = require('../errors').Exception
|
|
7
8
|
const errorHelper = require('./helper')
|
|
8
9
|
const createError = errorsModule.createError
|
|
9
10
|
const createEvent = errorsModule.createEvent
|
|
@@ -129,7 +130,7 @@ class ErrorCollector {
|
|
|
129
130
|
if (transaction.userErrors.length) {
|
|
130
131
|
for (let i = 0; i < transaction.userErrors.length; i++) {
|
|
131
132
|
const exception = transaction.userErrors[i]
|
|
132
|
-
if (this.
|
|
133
|
+
if (this.collect(transaction, exception)) {
|
|
133
134
|
++collectedErrors
|
|
134
135
|
}
|
|
135
136
|
}
|
|
@@ -145,13 +146,13 @@ class ErrorCollector {
|
|
|
145
146
|
if (transaction.exceptions.length) {
|
|
146
147
|
for (let i = 0; i < transaction.exceptions.length; i++) {
|
|
147
148
|
const exception = transaction.exceptions[i]
|
|
148
|
-
if (this.collect(transaction, exception
|
|
149
|
+
if (this.collect(transaction, exception)) {
|
|
149
150
|
++collectedErrors
|
|
150
151
|
// if we could collect it, then check if expected
|
|
151
152
|
if (isExpectedErrorStatusCode ||
|
|
152
153
|
errorHelper.isExpectedException(
|
|
153
154
|
transaction,
|
|
154
|
-
exception
|
|
155
|
+
exception.error,
|
|
155
156
|
this.config,
|
|
156
157
|
urltils
|
|
157
158
|
)
|
|
@@ -178,31 +179,32 @@ class ErrorCollector {
|
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
/**
|
|
181
|
-
* This function collects the error right away when transaction is not supplied.
|
|
182
|
-
*
|
|
182
|
+
* This function collects the error right away when transaction is not supplied. Otherwise it
|
|
183
|
+
* delays collecting the error until the transaction ends.
|
|
183
184
|
*
|
|
184
185
|
* NOTE: this interface is unofficial and may change in future.
|
|
185
186
|
*
|
|
186
|
-
* @param {?Transaction}
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @param {Error} exception
|
|
190
|
-
* The error to be traced.
|
|
191
|
-
*
|
|
192
|
-
* @param {object} customAttributes
|
|
193
|
-
* Any custom attributes associated with the request (optional).
|
|
187
|
+
* @param {?Transaction} transaction Transaction associated with the error.
|
|
188
|
+
* @param {Error} error The error to be traced.
|
|
189
|
+
* @param {?object} customAttributes Custom attributes associated with the request (optional).
|
|
194
190
|
*/
|
|
195
|
-
add(transaction,
|
|
196
|
-
if (!
|
|
191
|
+
add(transaction, error, customAttributes) {
|
|
192
|
+
if (!error) {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (errorHelper.shouldIgnoreError(transaction, error, this.config)) {
|
|
197
|
+
logger.trace('Ignoring error')
|
|
197
198
|
return
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
const timestamp = Date.now()
|
|
202
|
+
const exception = new Exception({error, timestamp, customAttributes})
|
|
201
203
|
|
|
202
204
|
if (transaction) {
|
|
203
|
-
transaction.addException(exception
|
|
205
|
+
transaction.addException(exception)
|
|
204
206
|
} else {
|
|
205
|
-
this.collect(transaction, exception
|
|
207
|
+
this.collect(transaction, exception)
|
|
206
208
|
}
|
|
207
209
|
}
|
|
208
210
|
|
|
@@ -217,95 +219,54 @@ class ErrorCollector {
|
|
|
217
219
|
*
|
|
218
220
|
* NOTE: this interface is unofficial and may change in future.
|
|
219
221
|
*
|
|
220
|
-
* @param {?Transaction}
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* @param {Error} exception
|
|
224
|
-
* The error to be traced.
|
|
225
|
-
*
|
|
226
|
-
* @param {object} [customAttributes=null]
|
|
227
|
-
* Any custom attributes associated with the request (optional).
|
|
222
|
+
* @param {?Transaction} transaction Transaction associated with the error.
|
|
223
|
+
* @param {Exception} exception The Exception to be traced.
|
|
228
224
|
*/
|
|
229
|
-
addUserError(transaction,
|
|
230
|
-
if (!
|
|
225
|
+
addUserError(transaction, error, customAttributes) {
|
|
226
|
+
if (!error) return
|
|
231
227
|
|
|
232
|
-
|
|
228
|
+
const timestamp = Date.now()
|
|
229
|
+
const exception = new Exception({error, timestamp, customAttributes})
|
|
233
230
|
|
|
234
231
|
if (transaction) {
|
|
235
|
-
transaction.addUserError(exception
|
|
232
|
+
transaction.addUserError(exception)
|
|
236
233
|
} else {
|
|
237
|
-
this.
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Wrapper for _collect, include logic for whether an error should
|
|
243
|
-
* be ignored or not. Exists to allow userErrors to bypass ignore
|
|
244
|
-
* logic.
|
|
245
|
-
*
|
|
246
|
-
* NOTE: this interface is unofficial and may change in future.
|
|
247
|
-
*
|
|
248
|
-
* @param {?Transaction} transaction
|
|
249
|
-
* Transaction associated with the error.
|
|
250
|
-
*
|
|
251
|
-
* @param {?Error} exception
|
|
252
|
-
* The error to be traced.
|
|
253
|
-
*
|
|
254
|
-
* @param {?object} customAttributes
|
|
255
|
-
* Any custom attributes associated with the request.
|
|
256
|
-
*
|
|
257
|
-
* @param {number} timestamp
|
|
258
|
-
*
|
|
259
|
-
* @return {bool} True if the error was collected.
|
|
260
|
-
*/
|
|
261
|
-
collect(transaction, exception, customAttributes, timestamp) {
|
|
262
|
-
if (errorHelper.shouldIgnoreError(transaction, exception, this.config)) {
|
|
263
|
-
logger.trace("Ignoring error")
|
|
264
|
-
return
|
|
234
|
+
this.collect(transaction, exception)
|
|
265
235
|
}
|
|
266
|
-
return this._collect(transaction, exception, customAttributes, timestamp)
|
|
267
236
|
}
|
|
268
237
|
|
|
269
238
|
/**
|
|
270
239
|
* Collects the error and also creates the error event.
|
|
271
240
|
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* This function uses an array of seen exceptions to ensure errors don't get
|
|
275
|
-
* double-counted. It can also be used as an unofficial means of marking that
|
|
276
|
-
* user errors shouldn't be traced.
|
|
241
|
+
* This function uses an array of seen exceptions to ensure errors don't get double-counted. It
|
|
242
|
+
* can also be used as an unofficial means of marking that user errors shouldn't be traced.
|
|
277
243
|
*
|
|
278
|
-
* For an error to be traced, at least one of the transaction or the error
|
|
279
|
-
* must be present.
|
|
244
|
+
* For an error to be traced, at least one of the transaction or the error must be present.
|
|
280
245
|
*
|
|
281
246
|
* NOTE: this interface is unofficial and may change in future.
|
|
282
247
|
*
|
|
283
|
-
* @param
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* @param {?Error} exception
|
|
287
|
-
* The error to be traced.
|
|
288
|
-
*
|
|
289
|
-
* @param {?object} customAttributes
|
|
290
|
-
* Any custom attributes associated with the request.
|
|
291
|
-
*
|
|
292
|
-
* @param {number} timestamp
|
|
293
|
-
*
|
|
294
|
-
* @return {bool} True if the error was collected.
|
|
248
|
+
* @param {?Transaction} transaction Transaction associated with the error.
|
|
249
|
+
* @param {?Exception} exception The Exception object to be traced.
|
|
250
|
+
* @return {bool} True if the error was collected.
|
|
295
251
|
*/
|
|
296
|
-
|
|
297
|
-
if (exception) {
|
|
298
|
-
|
|
252
|
+
collect(transaction, exception) {
|
|
253
|
+
if (!exception) {
|
|
254
|
+
exception = new Exception({})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (exception.error) {
|
|
258
|
+
if (this._haveSeen(transaction, exception.error)) {
|
|
299
259
|
return false
|
|
300
260
|
}
|
|
301
261
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
262
|
+
const error = exception.error
|
|
263
|
+
if (typeof error !== 'string' && !error.message && !error.stack) {
|
|
264
|
+
logger.trace(error, 'Got error that is not an instance of Error or string.')
|
|
265
|
+
exception.error = null
|
|
305
266
|
}
|
|
306
267
|
}
|
|
307
268
|
|
|
308
|
-
if (!exception && (!transaction || !transaction.statusCode || transaction.error)) {
|
|
269
|
+
if (!exception.error && (!transaction || !transaction.statusCode || transaction.error)) {
|
|
309
270
|
return false
|
|
310
271
|
}
|
|
311
272
|
|
|
@@ -321,13 +282,13 @@ class ErrorCollector {
|
|
|
321
282
|
return false
|
|
322
283
|
}
|
|
323
284
|
|
|
324
|
-
if (exception) {
|
|
325
|
-
logger.trace(exception, 'Got exception to trace:')
|
|
285
|
+
if (exception.error) {
|
|
286
|
+
logger.trace(exception.error, 'Got exception to trace:')
|
|
326
287
|
}
|
|
327
288
|
|
|
328
|
-
const
|
|
289
|
+
const errorTrace = createError(transaction, exception, this.config)
|
|
329
290
|
|
|
330
|
-
const isExpectedError = true ===
|
|
291
|
+
const isExpectedError = true === errorTrace[4].intrinsics['error.expected']
|
|
331
292
|
|
|
332
293
|
if (isExpectedError) {
|
|
333
294
|
this.metrics.getOrCreateMetric(NAMES.ERRORS.EXPECTED).incrementCallCount()
|
|
@@ -343,11 +304,11 @@ class ErrorCollector {
|
|
|
343
304
|
}
|
|
344
305
|
}
|
|
345
306
|
|
|
346
|
-
this.traceAggregator.add(
|
|
307
|
+
this.traceAggregator.add(errorTrace)
|
|
347
308
|
|
|
348
309
|
if (this.config.error_collector.capture_events === true) {
|
|
349
310
|
const priority = transaction && transaction.priority || Math.random()
|
|
350
|
-
const event = createEvent(transaction,
|
|
311
|
+
const event = createEvent(transaction, errorTrace, exception.timestamp, this.config)
|
|
351
312
|
this.eventAggregator.add(event, priority)
|
|
352
313
|
}
|
|
353
314
|
|
package/lib/errors/helper.js
CHANGED
|
@@ -48,7 +48,7 @@ module.exports = {
|
|
|
48
48
|
|
|
49
49
|
extractErrorInformation: function extractErrorInformation(
|
|
50
50
|
transaction,
|
|
51
|
-
|
|
51
|
+
error,
|
|
52
52
|
config,
|
|
53
53
|
urltils
|
|
54
54
|
) {
|
|
@@ -58,21 +58,21 @@ module.exports = {
|
|
|
58
58
|
|
|
59
59
|
// String errors do not provide us with as much information to provide to the
|
|
60
60
|
// user, but it is a common pattern.
|
|
61
|
-
if (typeof
|
|
62
|
-
message =
|
|
61
|
+
if (typeof error === 'string') {
|
|
62
|
+
message = error
|
|
63
63
|
} else if (
|
|
64
|
-
|
|
65
|
-
typeof
|
|
66
|
-
|
|
64
|
+
error !== null &&
|
|
65
|
+
typeof error === 'object' &&
|
|
66
|
+
error.message &&
|
|
67
67
|
!config.high_security &&
|
|
68
68
|
!config.strip_exception_messages.enabled
|
|
69
69
|
) {
|
|
70
|
-
message =
|
|
70
|
+
message = error.message
|
|
71
71
|
|
|
72
|
-
if (
|
|
73
|
-
type =
|
|
74
|
-
} else if (
|
|
75
|
-
type =
|
|
72
|
+
if (error.name) {
|
|
73
|
+
type = error.name
|
|
74
|
+
} else if (error.constructor && error.constructor.name) {
|
|
75
|
+
type = error.constructor.name
|
|
76
76
|
}
|
|
77
77
|
} else if (transaction && transaction.statusCode &&
|
|
78
78
|
urltils.isError(config, transaction.statusCode)) {
|
|
@@ -95,9 +95,9 @@ module.exports = {
|
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
|
|
98
|
-
shouldIgnoreError: function shouldIgnoreError(transaction,
|
|
98
|
+
shouldIgnoreError: function shouldIgnoreError(transaction, error, config) {
|
|
99
99
|
// extract _just_ the error information, not transaction stuff
|
|
100
|
-
let errorInfo = this.extractErrorInformation(null,
|
|
100
|
+
let errorInfo = this.extractErrorInformation(null, error, config, null)
|
|
101
101
|
|
|
102
102
|
return this.shouldIgnoreErrorClass(errorInfo, config) ||
|
|
103
103
|
this.shouldIgnoreErrorMessage(errorInfo, config) ||
|
package/lib/errors/index.js
CHANGED
|
@@ -6,8 +6,18 @@ var props = require('../util/properties')
|
|
|
6
6
|
var urltils = require('../util/urltils')
|
|
7
7
|
const errorHelper = require('../errors/helper')
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
class Exception {
|
|
10
|
+
constructor({error, timestamp, customAttributes, agentAttributes}) {
|
|
11
|
+
this.error = error
|
|
12
|
+
this.timestamp = timestamp || 0
|
|
13
|
+
this.customAttributes = customAttributes || {}
|
|
14
|
+
this.agentAttributes = agentAttributes || {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getErrorDetails(config) {
|
|
18
|
+
return errorHelper.extractErrorInformation(null, this.error, config)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
/**
|
|
13
23
|
* Given either or both of a transaction and an exception, generate an error
|
|
@@ -17,21 +27,20 @@ module.exports.createEvent = createEvent
|
|
|
17
27
|
* handler, which traps actual instances of Error, try to set sensible
|
|
18
28
|
* defaults for everything.
|
|
19
29
|
*
|
|
20
|
-
* @param {Transaction} transaction
|
|
21
|
-
*
|
|
22
|
-
* @param {
|
|
23
|
-
* @param {object} customAttributes Any custom attributes associated with
|
|
24
|
-
* the request (optional).
|
|
30
|
+
* @param {Transaction} transaction The agent transaction, coming from the instrumentatation
|
|
31
|
+
* @param {Exception} exception An custom Exception object with the error and other information
|
|
32
|
+
* @param {object} config The configuration to use when creating the object
|
|
25
33
|
*/
|
|
26
|
-
function createError(transaction, exception,
|
|
34
|
+
function createError(transaction, exception, config) {
|
|
35
|
+
const error = exception.error
|
|
27
36
|
let {name, message, type} = errorHelper.extractErrorInformation(
|
|
28
37
|
transaction,
|
|
29
|
-
|
|
38
|
+
error,
|
|
30
39
|
config,
|
|
31
40
|
urltils
|
|
32
41
|
)
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
let params = {
|
|
35
44
|
userAttributes: Object.create(null),
|
|
36
45
|
agentAttributes: Object.create(null),
|
|
37
46
|
intrinsics: Object.create(null)
|
|
@@ -39,20 +48,26 @@ function createError(transaction, exception, customAttributes, config) {
|
|
|
39
48
|
|
|
40
49
|
if (transaction) {
|
|
41
50
|
// Copy all of the parameters off of the transaction.
|
|
42
|
-
params.agentAttributes = transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT)
|
|
43
51
|
params.intrinsics = transaction.getIntrinsicAttributes()
|
|
52
|
+
let transactionAgentAttributes = transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT)
|
|
53
|
+
transactionAgentAttributes = transactionAgentAttributes || {}
|
|
54
|
+
|
|
55
|
+
// Merge the agent attributes specific to this error event with the transaction attributes
|
|
56
|
+
const agentAttributes = Object.assign(exception.agentAttributes, transactionAgentAttributes)
|
|
57
|
+
params.agentAttributes = agentAttributes
|
|
44
58
|
|
|
45
59
|
// There should be no attributes to copy in HSM, but check status anyway
|
|
46
60
|
if (!config.high_security) {
|
|
47
|
-
|
|
61
|
+
const custom = transaction.trace.custom.get(DESTINATIONS.ERROR_EVENT)
|
|
48
62
|
urltils.overwriteParameters(custom, params.userAttributes)
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
|
|
66
|
+
const customAttributes = exception.customAttributes
|
|
52
67
|
if (!config.high_security && config.api.custom_attributes_enabled && customAttributes) {
|
|
53
|
-
for (
|
|
68
|
+
for (let key in customAttributes) {
|
|
54
69
|
if (props.hasOwn(customAttributes, key)) {
|
|
55
|
-
|
|
70
|
+
const dest = config.attributeFilter.filterTransaction(
|
|
56
71
|
DESTINATIONS.ERROR_EVENT,
|
|
57
72
|
key
|
|
58
73
|
)
|
|
@@ -63,12 +78,11 @@ function createError(transaction, exception, customAttributes, config) {
|
|
|
63
78
|
}
|
|
64
79
|
}
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
var stack = exception && exception.stack
|
|
81
|
+
const stack = exception.error && exception.error.stack
|
|
68
82
|
if (stack) {
|
|
69
83
|
params.stack_trace = ('' + stack).split(/[\n\r]/g)
|
|
70
84
|
if (config.high_security || config.strip_exception_messages.enabled) {
|
|
71
|
-
params.stack_trace[0] = exception.name + ': <redacted>'
|
|
85
|
+
params.stack_trace[0] = exception.error.name + ': <redacted>'
|
|
72
86
|
}
|
|
73
87
|
}
|
|
74
88
|
|
|
@@ -77,7 +91,7 @@ function createError(transaction, exception, customAttributes, config) {
|
|
|
77
91
|
params.intrinsics['error.expected'] = true
|
|
78
92
|
}
|
|
79
93
|
|
|
80
|
-
|
|
94
|
+
let res = [0, name, message, type, params]
|
|
81
95
|
if (transaction) {
|
|
82
96
|
res.transaction = transaction.id
|
|
83
97
|
}
|
|
@@ -180,3 +194,7 @@ function _getErrorEventIntrinsicAttrs(
|
|
|
180
194
|
|
|
181
195
|
return attributes
|
|
182
196
|
}
|
|
197
|
+
|
|
198
|
+
module.exports.createError = createError
|
|
199
|
+
module.exports.createEvent = createEvent
|
|
200
|
+
module.exports.Exception = Exception
|
|
@@ -33,8 +33,20 @@ function initialize(agent, childProcess, moduleName, shim) {
|
|
|
33
33
|
const args = shim.argsToArray.apply(shim, arguments)
|
|
34
34
|
const cbIndex = args.length - 1
|
|
35
35
|
|
|
36
|
+
const originalListener = args[cbIndex]
|
|
37
|
+
if (!shim.isFunction(originalListener)) {
|
|
38
|
+
return fn.apply(this, arguments)
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
shim.bindSegment(args, cbIndex)
|
|
37
42
|
|
|
43
|
+
// Leverage events.removeListener() mechanism that checks listener
|
|
44
|
+
// property to allow our wrapped listeners to match and remove appropriately.
|
|
45
|
+
// Avoids having to instrument removeListener() and potentially doubling
|
|
46
|
+
// lookup. Since our wrapping will only be referenced by the events
|
|
47
|
+
// collection, we should not need to unwrap.
|
|
48
|
+
args[cbIndex].listener = originalListener
|
|
49
|
+
|
|
38
50
|
return fn.apply(this, args)
|
|
39
51
|
}
|
|
40
52
|
}
|
|
@@ -195,6 +195,10 @@ function handleError(segment, req, error) {
|
|
|
195
195
|
* @param {http.IncomingMessage} res
|
|
196
196
|
*/
|
|
197
197
|
function handleResponse(segment, hostname, req, res) {
|
|
198
|
+
// Add response attributes for spans
|
|
199
|
+
segment.addAttribute('http.statusCode', res.statusCode)
|
|
200
|
+
segment.addAttribute('http.statusText', res.statusMessage)
|
|
201
|
+
|
|
198
202
|
// If CAT is enabled, grab those headers!
|
|
199
203
|
const agent = segment.transaction.agent
|
|
200
204
|
if (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const shared = require('./shared')
|
|
4
|
+
const record = require('../../metrics/recorders/generic')
|
|
4
5
|
|
|
5
6
|
// TODO: abstract and consolidate mostly-shared hapi functionality
|
|
6
7
|
module.exports = function initialize(agent, hapi, moduleName, shim) {
|
|
@@ -154,14 +155,20 @@ function wrapPreHandlers(shim, container, path) {
|
|
|
154
155
|
}
|
|
155
156
|
return container
|
|
156
157
|
} else if (shim.isFunction(container)) {
|
|
157
|
-
return
|
|
158
|
+
return wrapPreHandler(shim, container, path)
|
|
158
159
|
} else if (container.method && shim.isFunction(container.method)) {
|
|
159
160
|
return shim.wrap(container, 'method', function wrapHandler(shim, handler) {
|
|
160
|
-
return
|
|
161
|
+
return wrapPreHandler(shim, handler, path)
|
|
161
162
|
})
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
function wrapPreHandler(shim, container, path) {
|
|
167
|
+
return shim.record(container, (shim) => {
|
|
168
|
+
return {name: [shim.HAPI, ' pre handler: ','(',path,')'].join(''), recorder: record}
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
165
172
|
function wrapRouteHandler(shim, handler, path) {
|
|
166
173
|
return shim.recordMiddleware(handler, {
|
|
167
174
|
route: path,
|
package/lib/spans/span-event.js
CHANGED
|
@@ -12,6 +12,7 @@ const CATEGORIES = {
|
|
|
12
12
|
DATASTORE: 'datastore',
|
|
13
13
|
GENERIC: 'generic'
|
|
14
14
|
}
|
|
15
|
+
|
|
15
16
|
const EMPTY_USER_ATTRS = Object.freeze(Object.create(null))
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -48,7 +49,8 @@ class SpanIntrinsics {
|
|
|
48
49
|
* @class
|
|
49
50
|
*/
|
|
50
51
|
class SpanEvent {
|
|
51
|
-
constructor(attributes) {
|
|
52
|
+
constructor(attributes, customAttributes) {
|
|
53
|
+
this.customAttributes = customAttributes
|
|
52
54
|
this.attributes = attributes
|
|
53
55
|
this.intrinsics = new SpanIntrinsics()
|
|
54
56
|
}
|
|
@@ -78,14 +80,15 @@ class SpanEvent {
|
|
|
78
80
|
*/
|
|
79
81
|
static fromSegment(segment, parentId = null, isRoot = false) {
|
|
80
82
|
const attributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
|
|
83
|
+
const customAttributes = segment.customAttributes.get(DESTINATIONS.SPAN_EVENT)
|
|
81
84
|
|
|
82
85
|
let span = null
|
|
83
86
|
if (HttpSpanEvent.testSegment(segment)) {
|
|
84
|
-
span = new HttpSpanEvent(attributes)
|
|
87
|
+
span = new HttpSpanEvent(attributes, customAttributes)
|
|
85
88
|
} else if (DatastoreSpanEvent.testSegment(segment)) {
|
|
86
|
-
span = new DatastoreSpanEvent(attributes)
|
|
89
|
+
span = new DatastoreSpanEvent(attributes, customAttributes)
|
|
87
90
|
} else {
|
|
88
|
-
span = new SpanEvent(attributes)
|
|
91
|
+
span = new SpanEvent(attributes, customAttributes)
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
const tx = segment.transaction
|
|
@@ -120,11 +123,21 @@ class SpanEvent {
|
|
|
120
123
|
toJSON() {
|
|
121
124
|
return [
|
|
122
125
|
_filterNulls(this.intrinsics),
|
|
123
|
-
|
|
126
|
+
this.customAttributes ?
|
|
127
|
+
_filterNulls(this.customAttributes) :
|
|
128
|
+
EMPTY_USER_ATTRS,
|
|
124
129
|
_filterNulls(this.attributes)
|
|
125
130
|
]
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
addCustomAttribute(key, value, truncateExempt = false) {
|
|
134
|
+
const {attributeFilter} = Config.getInstance()
|
|
135
|
+
const dest = attributeFilter.filterSegment(DESTINATIONS.SPAN_EVENT, key)
|
|
136
|
+
if (dest & DESTINATIONS.SPAN_EVENT) {
|
|
137
|
+
this.customAttributes[key] = truncateExempt ? value : _truncate(value)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
addAttribute(key, value, truncateExempt = false) {
|
|
129
142
|
const {attributeFilter} = Config.getInstance()
|
|
130
143
|
const dest = attributeFilter.filterSegment(DESTINATIONS.SPAN_EVENT, key)
|
|
@@ -141,8 +154,8 @@ class SpanEvent {
|
|
|
141
154
|
* @class
|
|
142
155
|
*/
|
|
143
156
|
class HttpSpanEvent extends SpanEvent {
|
|
144
|
-
constructor(attributes) {
|
|
145
|
-
super(attributes)
|
|
157
|
+
constructor(attributes, customAttributes) {
|
|
158
|
+
super(attributes, customAttributes)
|
|
146
159
|
|
|
147
160
|
this.intrinsics.category = CATEGORIES.HTTP
|
|
148
161
|
this.intrinsics.component = attributes.library || HTTP_LIBRARY
|
|
@@ -175,8 +188,8 @@ class HttpSpanEvent extends SpanEvent {
|
|
|
175
188
|
* @class.
|
|
176
189
|
*/
|
|
177
190
|
class DatastoreSpanEvent extends SpanEvent {
|
|
178
|
-
constructor(attributes) {
|
|
179
|
-
super(attributes)
|
|
191
|
+
constructor(attributes, customAttributes) {
|
|
192
|
+
super(attributes, customAttributes)
|
|
180
193
|
|
|
181
194
|
this.intrinsics.category = CATEGORIES.DATASTORE
|
|
182
195
|
this.intrinsics['span.kind'] = CLIENT_KIND
|
package/lib/transaction/index.js
CHANGED
|
@@ -678,19 +678,40 @@ Transaction.prototype.alternatePathHashes = function alternatePathHashes() {
|
|
|
678
678
|
return altHashes.length === 0 ? null : altHashes.sort().join(',')
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
+
/**
|
|
682
|
+
* Add the error information to the current segment and add the segment ID as
|
|
683
|
+
* an attribute onto the exception.
|
|
684
|
+
*
|
|
685
|
+
* @param {Exception} exception The exception object to be collected.
|
|
686
|
+
*/
|
|
687
|
+
Transaction.prototype._linkExceptionToSegment = _linkExceptionToSegment
|
|
688
|
+
|
|
689
|
+
function _linkExceptionToSegment(exception) {
|
|
690
|
+
const segment = this.agent.tracer.getSegment()
|
|
691
|
+
if (!segment) {
|
|
692
|
+
return
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Add error attributes to the span
|
|
696
|
+
const details = exception.getErrorDetails(this.agent.config)
|
|
697
|
+
segment.addAttribute('error.message', details.message)
|
|
698
|
+
segment.addAttribute('error.class', details.type)
|
|
699
|
+
|
|
700
|
+
// Add the span/segment ID to the exception as agent attributes
|
|
701
|
+
exception.agentAttributes.spanId = segment.id
|
|
702
|
+
}
|
|
703
|
+
|
|
681
704
|
/**
|
|
682
705
|
* Associate an exception with the transaction. When the transaction ends,
|
|
683
706
|
* the exception will be collected along with the transaction details.
|
|
684
707
|
*
|
|
685
|
-
* @param {
|
|
686
|
-
* @param {object} customAttributes Any custom attributes associated with
|
|
687
|
-
* the request (optional).
|
|
688
|
-
* @param {number} timestamp The timestamp for when the exception occurred.
|
|
708
|
+
* @param {Exception} exception The exception object to be collected.
|
|
689
709
|
*/
|
|
690
710
|
Transaction.prototype.addException = _addException
|
|
691
711
|
|
|
692
|
-
function _addException(exception
|
|
693
|
-
this.
|
|
712
|
+
function _addException(exception) {
|
|
713
|
+
this._linkExceptionToSegment(exception)
|
|
714
|
+
this.exceptions.push(exception)
|
|
694
715
|
}
|
|
695
716
|
|
|
696
717
|
/**
|
|
@@ -698,20 +719,18 @@ function _addException(exception, customAttributes, timestamp) {
|
|
|
698
719
|
* When the transaction ends, the exception will be collected along with the transaction
|
|
699
720
|
* details.
|
|
700
721
|
*
|
|
701
|
-
* @param {
|
|
702
|
-
* @param {object} customAttributes Any custom attributes associated with
|
|
703
|
-
* the request (optional).
|
|
704
|
-
* @param {number} timestamp The timestamp for when the exception occurred.
|
|
722
|
+
* @param {Exception} exception The exception object to be collected.
|
|
705
723
|
*/
|
|
706
724
|
Transaction.prototype.addUserError = _addUserError
|
|
707
725
|
|
|
708
|
-
function _addUserError(exception
|
|
709
|
-
this.
|
|
726
|
+
function _addUserError(exception) {
|
|
727
|
+
this._linkExceptionToSegment(exception)
|
|
728
|
+
this.userErrors.push(exception)
|
|
710
729
|
}
|
|
711
730
|
|
|
712
731
|
/**
|
|
713
|
-
* Returns true if an error happened during the transaction or if the transaction itself
|
|
714
|
-
*
|
|
732
|
+
* Returns true if an error happened during the transaction or if the transaction itself is
|
|
733
|
+
* considered to be an error.
|
|
715
734
|
*/
|
|
716
735
|
Transaction.prototype.hasErrors = function _hasErrors() {
|
|
717
736
|
var isErroredTransaction = urltils.isError(this.agent.config, this.statusCode)
|
|
@@ -720,10 +739,7 @@ Transaction.prototype.hasErrors = function _hasErrors() {
|
|
|
720
739
|
return (transactionHasExceptions || transactionHasuserErrors || isErroredTransaction)
|
|
721
740
|
}
|
|
722
741
|
|
|
723
|
-
|
|
724
|
-
* Returns true if all the errors/exceptions collected so far
|
|
725
|
-
* are expected errors.
|
|
726
|
-
*/
|
|
742
|
+
// Returns true if all the errors/exceptions collected so far are expected errors
|
|
727
743
|
Transaction.prototype.hasOnlyExpectedErrors = function hasOnlyExpectedErrors() {
|
|
728
744
|
if (0 === this.exceptions.length) {
|
|
729
745
|
return false
|
|
@@ -735,13 +751,13 @@ Transaction.prototype.hasOnlyExpectedErrors = function hasOnlyExpectedErrors() {
|
|
|
735
751
|
const isUnexpected = !(
|
|
736
752
|
errorHelper.isExpectedException(
|
|
737
753
|
this,
|
|
738
|
-
exception
|
|
754
|
+
exception.error,
|
|
739
755
|
this.agent.config,
|
|
740
756
|
urltils
|
|
741
757
|
) ||
|
|
742
758
|
errorHelper.shouldIgnoreError(
|
|
743
759
|
this,
|
|
744
|
-
exception
|
|
760
|
+
exception.error,
|
|
745
761
|
this.agent.config
|
|
746
762
|
)
|
|
747
763
|
)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var codec = require('../../util/codec')
|
|
4
4
|
var Segment = require('./segment')
|
|
5
|
-
var Attributes = require('../../attributes')
|
|
5
|
+
var {Attributes, MAXIMUM_CUSTOM_ATTRIBUTES} = require('../../attributes')
|
|
6
6
|
var logger = require('../../logger').child({component: 'trace'})
|
|
7
7
|
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@ function Trace(transaction) {
|
|
|
29
29
|
this.segmentsSeen = 0
|
|
30
30
|
this.totalTimeCache = null
|
|
31
31
|
|
|
32
|
-
this.custom = new Attributes(ATTRIBUTE_SCOPE,
|
|
32
|
+
this.custom = new Attributes(ATTRIBUTE_SCOPE, MAXIMUM_CUSTOM_ATTRIBUTES)
|
|
33
33
|
this.attributes = new Attributes(ATTRIBUTE_SCOPE)
|
|
34
34
|
|
|
35
35
|
// sending displayName if set by user
|
|
@@ -6,7 +6,7 @@ const Timer = require('../../timer')
|
|
|
6
6
|
const urltils = require('../../util/urltils')
|
|
7
7
|
const hashes = require('../../util/hashes')
|
|
8
8
|
|
|
9
|
-
const Attributes = require('../../attributes')
|
|
9
|
+
const {Attributes, MAXIMUM_CUSTOM_ATTRIBUTES} = require('../../attributes')
|
|
10
10
|
const ExclusiveCalculator = require('./exclusive-time-calculator')
|
|
11
11
|
|
|
12
12
|
const NAMES = require('../../metrics/names')
|
|
@@ -52,6 +52,7 @@ function TraceSegment(transaction, name, recorder) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
this.attributes = new Attributes(ATTRIBUTE_SCOPE)
|
|
55
|
+
this.customAttributes = new Attributes(ATTRIBUTE_SCOPE, MAXIMUM_CUSTOM_ATTRIBUTES)
|
|
55
56
|
|
|
56
57
|
this.children = []
|
|
57
58
|
|
|
@@ -90,6 +91,16 @@ TraceSegment.prototype.getAttributes = function getAttributes() {
|
|
|
90
91
|
return this.attributes.get(DESTINATIONS.TRANS_SEGMENT)
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
TraceSegment.prototype.addCustomSpanAttribute =
|
|
95
|
+
function addCustomSpanAttribute(key, value, truncateExempt = false) {
|
|
96
|
+
this.customAttributes.addAttribute(
|
|
97
|
+
DESTINATIONS.SPAN_EVENT,
|
|
98
|
+
key,
|
|
99
|
+
value,
|
|
100
|
+
truncateExempt
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
93
104
|
TraceSegment.prototype.getSpanId = function getSpanId() {
|
|
94
105
|
const conf = this.transaction.agent.config
|
|
95
106
|
const enabled = conf.span_events.enabled && conf.distributed_tracing.enabled
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"contributors": [
|
|
@@ -113,13 +113,12 @@
|
|
|
113
113
|
"lint": "eslint ./*.js lib test",
|
|
114
114
|
"public-docs": "npm ci && jsdoc -c ./jsdoc-conf.json --tutorials examples/shim api.js lib/shim/ lib/transaction/handle.js && cp examples/shim/*.png out/",
|
|
115
115
|
"publish-docs": "./bin/publish-docs.sh",
|
|
116
|
-
"security": "npm audit",
|
|
117
116
|
"services": "./bin/docker-services.sh",
|
|
118
117
|
"smoke": "npm run clean && ./bin/smoke.sh",
|
|
119
118
|
"ssl": "./bin/ssl.sh",
|
|
120
119
|
"sub-install": "node test/bin/install_sub_deps",
|
|
121
120
|
"test": "npm run integration && npm run unit",
|
|
122
|
-
"unit": "rm -f newrelic_agent.log &&
|
|
121
|
+
"unit": "rm -f newrelic_agent.log && time tap --test-regex='(\\/|^test\\/unit\\/.*\\.test\\.js)$' --timeout=120 --no-coverage",
|
|
123
122
|
"update-cross-agent-tests": "./bin/update-cats.sh",
|
|
124
123
|
"versioned-tests": "./bin/run-versioned-tests.sh",
|
|
125
124
|
"versioned": "npm run prepare-test && time ./bin/run-versioned-tests.sh"
|
|
@@ -152,8 +151,7 @@
|
|
|
152
151
|
"benchmark": "^2.1.4",
|
|
153
152
|
"bluebird": "^3.4.7",
|
|
154
153
|
"chai": "^4.1.2",
|
|
155
|
-
"eslint": "^
|
|
156
|
-
"eslint-plugin-mocha-no-only": "^1.1.0",
|
|
154
|
+
"eslint": "^6.8.0",
|
|
157
155
|
"express": "*",
|
|
158
156
|
"generic-pool": "^3.6.1",
|
|
159
157
|
"glob": "^7.1.2",
|
|
@@ -162,7 +160,6 @@
|
|
|
162
160
|
"lodash": "^4.17.14",
|
|
163
161
|
"memcached": ">=0.2.8",
|
|
164
162
|
"minami": "^1.1.1",
|
|
165
|
-
"mocha": "^6.2.1",
|
|
166
163
|
"mongodb": "^3.3.3",
|
|
167
164
|
"mysql": "*",
|
|
168
165
|
"nock": "11.8.0",
|