newrelic 5.10.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/NEWS.md +28 -0
- package/lib/agent.js +47 -4
- package/lib/collector/api.js +6 -0
- 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/harvest.js +18 -8
- package/lib/instrumentation/@hapi/hapi.js +3 -0
- package/lib/instrumentations.js +1 -0
- package/lib/metrics/names.js +1 -0
- package/lib/shimmer.js +4 -4
- package/lib/transaction/index.js +38 -1
- package/lib/util/urltils.js +23 -0
- package/package.json +3 -3
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
|
/**
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
isExpected: function isExpected(type, message, transaction, config, urltils) {
|
|
5
|
+
let isExpectedTransactionCode = false
|
|
6
|
+
if (transaction && urltils.isExpectedError(config, transaction.statusCode)) {
|
|
7
|
+
isExpectedTransactionCode = true
|
|
8
|
+
}
|
|
9
|
+
return this.isExpectedErrorMessage(config, type, message) ||
|
|
10
|
+
this.isExpectedErrorClass(config, type) ||
|
|
11
|
+
isExpectedTransactionCode
|
|
12
|
+
},
|
|
13
|
+
isExpectedErrorMessage: function isExpectedErrorMessage(config, type, message) {
|
|
14
|
+
if (!config.error_collector.expected_messages[type]) {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
if (config.error_collector.expected_messages[type].length > 0) {
|
|
18
|
+
if (-1 !== config.error_collector.expected_messages[type].indexOf(message)) {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return false
|
|
23
|
+
},
|
|
24
|
+
isExpectedErrorClass: function isExpectedErrorClass(config, className) {
|
|
25
|
+
if (config.error_collector.expected_classes.length > 0) {
|
|
26
|
+
if (-1 !== config.error_collector.expected_classes.indexOf(className)) {
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false
|
|
31
|
+
},
|
|
32
|
+
isExpectedException: function isExpectedException(
|
|
33
|
+
transaction,
|
|
34
|
+
exception,
|
|
35
|
+
config,
|
|
36
|
+
urltils
|
|
37
|
+
) {
|
|
38
|
+
let {type, message} = this.extractErrorInformation(
|
|
39
|
+
transaction,
|
|
40
|
+
exception,
|
|
41
|
+
config,
|
|
42
|
+
urltils
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return this.isExpectedErrorClass(config, type) ||
|
|
46
|
+
this.isExpectedErrorMessage(config, type, message)
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
extractErrorInformation: function extractErrorInformation(
|
|
50
|
+
transaction,
|
|
51
|
+
exception,
|
|
52
|
+
config,
|
|
53
|
+
urltils
|
|
54
|
+
) {
|
|
55
|
+
let name = 'Unknown'
|
|
56
|
+
let message = ''
|
|
57
|
+
let type = 'Error'
|
|
58
|
+
|
|
59
|
+
// String errors do not provide us with as much information to provide to the
|
|
60
|
+
// user, but it is a common pattern.
|
|
61
|
+
if (typeof exception === 'string') {
|
|
62
|
+
message = exception
|
|
63
|
+
} else if (
|
|
64
|
+
exception !== null &&
|
|
65
|
+
typeof exception === 'object' &&
|
|
66
|
+
exception.message &&
|
|
67
|
+
!config.high_security &&
|
|
68
|
+
!config.strip_exception_messages.enabled
|
|
69
|
+
) {
|
|
70
|
+
message = exception.message
|
|
71
|
+
|
|
72
|
+
if (exception.name) {
|
|
73
|
+
type = exception.name
|
|
74
|
+
} else if (exception.constructor && exception.constructor.name) {
|
|
75
|
+
type = exception.constructor.name
|
|
76
|
+
}
|
|
77
|
+
} else if (transaction && transaction.statusCode &&
|
|
78
|
+
urltils.isError(config, transaction.statusCode)) {
|
|
79
|
+
message = 'HttpError ' + transaction.statusCode
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (transaction) {
|
|
83
|
+
// transaction.getName is expensive due to running normalizers and ignore
|
|
84
|
+
// rules if a name hasn't been assigned yet.
|
|
85
|
+
const txName = transaction.getFullName()
|
|
86
|
+
if (txName) {
|
|
87
|
+
name = txName
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
name:name,
|
|
93
|
+
message:message,
|
|
94
|
+
type:type,
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
shouldIgnoreError: function shouldIgnoreError(transaction, exception, config) {
|
|
99
|
+
// extract _just_ the error information, not transaction stuff
|
|
100
|
+
let errorInfo = this.extractErrorInformation(null, exception, config, null)
|
|
101
|
+
|
|
102
|
+
return this.shouldIgnoreErrorClass(errorInfo, config) ||
|
|
103
|
+
this.shouldIgnoreErrorMessage(errorInfo, config) ||
|
|
104
|
+
this.shouldIgnoreStatusCode(transaction, config)
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
shouldIgnoreStatusCode: function shouldIgnoreStatusCode(transaction, config) {
|
|
108
|
+
if (!transaction) {
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
return config.error_collector.ignore_status_codes.indexOf(
|
|
112
|
+
transaction.statusCode
|
|
113
|
+
) !== -1
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
shouldIgnoreErrorClass: function shouldIgnoreErrorClass(errorInfo, config) {
|
|
117
|
+
if (config.error_collector.ignore_classes.length < 1) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return -1 !== config.error_collector.ignore_classes.indexOf(errorInfo.type)
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
shouldIgnoreErrorMessage: function shouldIgnoreErrorMessage(errorInfo, config) {
|
|
125
|
+
let configIgnoreMessages = config.error_collector.ignore_messages[errorInfo.type]
|
|
126
|
+
if (!configIgnoreMessages) {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (configIgnoreMessages.length > 0) {
|
|
131
|
+
if (-1 !== configIgnoreMessages.indexOf(errorInfo.message)) {
|
|
132
|
+
return true
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
}
|
package/lib/errors/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var DESTINATIONS = require('../config/attribute-filter').DESTINATIONS
|
|
|
4
4
|
var NAMES = require('../metrics/names')
|
|
5
5
|
var props = require('../util/properties')
|
|
6
6
|
var urltils = require('../util/urltils')
|
|
7
|
+
const errorHelper = require('../errors/helper')
|
|
7
8
|
|
|
8
9
|
module.exports.createError = createError
|
|
9
10
|
module.exports.createEvent = createEvent
|
|
@@ -23,46 +24,20 @@ module.exports.createEvent = createEvent
|
|
|
23
24
|
* the request (optional).
|
|
24
25
|
*/
|
|
25
26
|
function createError(transaction, exception, customAttributes, config) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
let {name, message, type} = errorHelper.extractErrorInformation(
|
|
28
|
+
transaction,
|
|
29
|
+
exception,
|
|
30
|
+
config,
|
|
31
|
+
urltils
|
|
32
|
+
)
|
|
33
|
+
|
|
29
34
|
var params = {
|
|
30
35
|
userAttributes: Object.create(null),
|
|
31
36
|
agentAttributes: Object.create(null),
|
|
32
37
|
intrinsics: Object.create(null)
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
// String errors do not provide us with as much information to provide to the
|
|
36
|
-
// user, but it is a common pattern.
|
|
37
|
-
if (typeof exception === 'string') {
|
|
38
|
-
message = exception
|
|
39
|
-
} else if (
|
|
40
|
-
exception !== null &&
|
|
41
|
-
typeof exception === 'object' &&
|
|
42
|
-
exception.message &&
|
|
43
|
-
!config.high_security &&
|
|
44
|
-
!config.strip_exception_messages.enabled
|
|
45
|
-
) {
|
|
46
|
-
message = exception.message
|
|
47
|
-
|
|
48
|
-
if (exception.name) {
|
|
49
|
-
type = exception.name
|
|
50
|
-
} else if (exception.constructor && exception.constructor.name) {
|
|
51
|
-
type = exception.constructor.name
|
|
52
|
-
}
|
|
53
|
-
} else if (transaction && transaction.statusCode &&
|
|
54
|
-
urltils.isError(config, transaction.statusCode)) {
|
|
55
|
-
message = 'HttpError ' + transaction.statusCode
|
|
56
|
-
}
|
|
57
|
-
|
|
58
40
|
if (transaction) {
|
|
59
|
-
// transaction.getName is expensive due to running normalizers and ignore
|
|
60
|
-
// rules if a name hasn't been assigned yet.
|
|
61
|
-
var txName = transaction.getFullName()
|
|
62
|
-
if (txName) {
|
|
63
|
-
name = txName
|
|
64
|
-
}
|
|
65
|
-
|
|
66
41
|
// Copy all of the parameters off of the transaction.
|
|
67
42
|
params.agentAttributes = transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT)
|
|
68
43
|
params.intrinsics = transaction.getIntrinsicAttributes()
|
|
@@ -97,6 +72,11 @@ function createError(transaction, exception, customAttributes, config) {
|
|
|
97
72
|
}
|
|
98
73
|
}
|
|
99
74
|
|
|
75
|
+
params.intrinsics['error.expected'] = false
|
|
76
|
+
if (errorHelper.isExpected(type, message, transaction, config, urltils)) {
|
|
77
|
+
params.intrinsics['error.expected'] = true
|
|
78
|
+
}
|
|
79
|
+
|
|
100
80
|
var res = [0, name, message, type, params]
|
|
101
81
|
if (transaction) {
|
|
102
82
|
res.transaction = transaction.id
|
|
@@ -117,6 +97,7 @@ function createEvent(transaction, error, timestamp, config) {
|
|
|
117
97
|
transaction,
|
|
118
98
|
errorClass,
|
|
119
99
|
message,
|
|
100
|
+
errorParams.intrinsics['error.expected'],
|
|
120
101
|
timestamp,
|
|
121
102
|
config
|
|
122
103
|
)
|
|
@@ -135,7 +116,15 @@ function createEvent(transaction, error, timestamp, config) {
|
|
|
135
116
|
return errorEvent
|
|
136
117
|
}
|
|
137
118
|
|
|
138
|
-
|
|
119
|
+
// eslint-disable-next-line max-params
|
|
120
|
+
function _getErrorEventIntrinsicAttrs(
|
|
121
|
+
transaction,
|
|
122
|
+
errorClass,
|
|
123
|
+
message,
|
|
124
|
+
expected,
|
|
125
|
+
timestamp,
|
|
126
|
+
conf
|
|
127
|
+
) {
|
|
139
128
|
// the server expects seconds instead of milliseconds
|
|
140
129
|
if (timestamp) timestamp = timestamp / 1000
|
|
141
130
|
|
|
@@ -143,7 +132,8 @@ function _getErrorEventIntrinsicAttrs(transaction, errorClass, message, timestam
|
|
|
143
132
|
type: "TransactionError",
|
|
144
133
|
"error.class": errorClass,
|
|
145
134
|
"error.message": conf.high_security ? '' : message,
|
|
146
|
-
timestamp: timestamp
|
|
135
|
+
timestamp: timestamp,
|
|
136
|
+
'error.expected': expected
|
|
147
137
|
}
|
|
148
138
|
|
|
149
139
|
if (transaction) {
|
package/lib/harvest.js
CHANGED
|
@@ -170,6 +170,9 @@ class HarvestStep {
|
|
|
170
170
|
if (!err) {
|
|
171
171
|
self.success = true
|
|
172
172
|
}
|
|
173
|
+
|
|
174
|
+
logger.trace('Finished sending payloads for ' + self._endpoint)
|
|
175
|
+
|
|
173
176
|
callback(err)
|
|
174
177
|
})
|
|
175
178
|
}
|
|
@@ -196,7 +199,7 @@ class HarvestStep {
|
|
|
196
199
|
// Do we need to retry this endpoint right now?
|
|
197
200
|
if (response.retryAfter) {
|
|
198
201
|
const delay = response.retryAfter
|
|
199
|
-
logger.
|
|
202
|
+
logger.info('Retrying sending to %s in %d ms', self._endpoint, delay)
|
|
200
203
|
setTimeout(() => self._doSend(payload, i, callback), delay)
|
|
201
204
|
return
|
|
202
205
|
}
|
|
@@ -288,18 +291,24 @@ class ErrorTraceHarvest extends HarvestStep {
|
|
|
288
291
|
const errorAggr = harvest.agent.errors
|
|
289
292
|
super(harvest, 'errorData', errorAggr.getErrors())
|
|
290
293
|
|
|
294
|
+
const metrics = harvest.agent.metrics
|
|
295
|
+
|
|
291
296
|
// Generate metrics for collected errors.
|
|
292
|
-
if (errorAggr.
|
|
293
|
-
|
|
294
|
-
let count = errorAggr.getTotalErrorCount()
|
|
297
|
+
if (errorAggr.getTotalUnexpectedErrorCount() > 0) {
|
|
298
|
+
let count = errorAggr.getTotalUnexpectedErrorCount()
|
|
295
299
|
metrics.getOrCreateMetric(NAMES.ERRORS.ALL).incrementCallCount(count)
|
|
296
300
|
|
|
297
|
-
count = errorAggr.
|
|
301
|
+
count = errorAggr.getUnexpectedWebTransactionsErrorCount()
|
|
298
302
|
metrics.getOrCreateMetric(NAMES.ERRORS.WEB).incrementCallCount(count)
|
|
299
303
|
|
|
300
|
-
count = errorAggr.
|
|
304
|
+
count = errorAggr.getUnexpectedOtherTransactionsErrorCount()
|
|
301
305
|
metrics.getOrCreateMetric(NAMES.ERRORS.OTHER).incrementCallCount(count)
|
|
302
306
|
}
|
|
307
|
+
|
|
308
|
+
const expectedCount = errorAggr.getTotalExpectedErrorCount()
|
|
309
|
+
if (expectedCount > 0) {
|
|
310
|
+
metrics.getOrCreateMetric(NAMES.ERRORS.EXPECTED).incrementCallCount(expectedCount)
|
|
311
|
+
}
|
|
303
312
|
}
|
|
304
313
|
|
|
305
314
|
preparePayloadsSync(runId, errors) {
|
|
@@ -594,8 +603,9 @@ class Harvest {
|
|
|
594
603
|
a.map(this._steps, function eachHarvestStep(step, cb) {
|
|
595
604
|
logger.trace('Doing harvest step %s.', step.name)
|
|
596
605
|
if (!self.agent.collector.isConnected()) {
|
|
597
|
-
|
|
598
|
-
|
|
606
|
+
const connectionLostMessage = 'Connection to New Relic lost during harvest.'
|
|
607
|
+
logger.warn(connectionLostMessage)
|
|
608
|
+
return setImmediate(callback, new Error(connectionLostMessage))
|
|
599
609
|
}
|
|
600
610
|
a.series([
|
|
601
611
|
step.prepare.bind(step),
|
package/lib/instrumentations.js
CHANGED
|
@@ -13,6 +13,7 @@ module.exports = function instrumentations() {
|
|
|
13
13
|
'director': {type: MODULE_TYPE.WEB_FRAMEWORK},
|
|
14
14
|
'express': {type: MODULE_TYPE.WEB_FRAMEWORK},
|
|
15
15
|
'generic-pool': {type: MODULE_TYPE.GENERIC},
|
|
16
|
+
'@hapi/hapi': {type: MODULE_TYPE.WEB_FRAMEWORK},
|
|
16
17
|
'hapi': {type: MODULE_TYPE.WEB_FRAMEWORK},
|
|
17
18
|
'ioredis': {type: MODULE_TYPE.DATASTORE},
|
|
18
19
|
'koa': {module: '@newrelic/koa'},
|
package/lib/metrics/names.js
CHANGED
package/lib/shimmer.js
CHANGED
|
@@ -340,14 +340,14 @@ var shimmer = module.exports = {
|
|
|
340
340
|
patchModule: function patchModule(agent) {
|
|
341
341
|
logger.trace("Wrapping module loader.")
|
|
342
342
|
var Module = require('module')
|
|
343
|
-
var
|
|
343
|
+
var filepathMap = {}
|
|
344
344
|
|
|
345
345
|
shimmer.wrapMethod(Module, 'Module', '_resolveFilename', function wrapRes(resolve) {
|
|
346
|
-
return function wrappedResolveFilename() {
|
|
346
|
+
return function wrappedResolveFilename(file) {
|
|
347
347
|
// This is triggered by the load call, so record the path that has been seen so
|
|
348
348
|
// we can examine it after the load call has returned.
|
|
349
349
|
const resolvedFilepath = resolve.apply(this, arguments)
|
|
350
|
-
|
|
350
|
+
filepathMap[file] = resolvedFilepath
|
|
351
351
|
return resolvedFilepath
|
|
352
352
|
}
|
|
353
353
|
})
|
|
@@ -358,7 +358,7 @@ var shimmer = module.exports = {
|
|
|
358
358
|
// load function calls resolve, we must mirror the recursive load calls with a
|
|
359
359
|
// stack.
|
|
360
360
|
const m = load.apply(this, arguments)
|
|
361
|
-
return _postLoad(agent, m, file,
|
|
361
|
+
return _postLoad(agent, m, file, filepathMap[file])
|
|
362
362
|
}
|
|
363
363
|
})
|
|
364
364
|
},
|
package/lib/transaction/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const errorHelper = require('../errors/helper')
|
|
3
4
|
var hashes = require('../util/hashes')
|
|
4
5
|
var logger = require('../logger').child({component: 'transaction'})
|
|
5
6
|
var Metrics = require('../metrics')
|
|
@@ -603,7 +604,20 @@ Transaction.prototype.measure = function measure(name, scope, duration, exclusiv
|
|
|
603
604
|
*/
|
|
604
605
|
Transaction.prototype._setApdex = function _setApdex(name, duration, keyApdexInMillis) {
|
|
605
606
|
var apdexStats = this.metrics.getOrCreateApdexMetric(name, null, keyApdexInMillis)
|
|
606
|
-
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
// if we have an error-like status code, and all the errors are
|
|
610
|
+
// expected, we know the status code was caused by an expected
|
|
611
|
+
// error, so we will not report "frustrating". Otherwise, we
|
|
612
|
+
// don't know which error triggered the error-like status code,
|
|
613
|
+
// and will still incrementing frustrating. If this is an issue,
|
|
614
|
+
// users can either set a status code as expected, or ignore the
|
|
615
|
+
// specific error to avoid incrementing to frustrating
|
|
616
|
+
if (
|
|
617
|
+
urltils.isError(this.agent.config, this.statusCode) &&
|
|
618
|
+
!urltils.isExpectedError(this.agent.config, this.statusCode) &&
|
|
619
|
+
!this.hasOnlyExpectedErrors()
|
|
620
|
+
) {
|
|
607
621
|
apdexStats.incrementFrustrating()
|
|
608
622
|
} else {
|
|
609
623
|
apdexStats.recordValueInMillis(duration, keyApdexInMillis)
|
|
@@ -687,6 +701,29 @@ Transaction.prototype.hasErrors = function _hasErrors() {
|
|
|
687
701
|
return (transactionHasExceptions || transactionHasuserErrors || isErroredTransaction)
|
|
688
702
|
}
|
|
689
703
|
|
|
704
|
+
/**
|
|
705
|
+
* Returns true if all the errors/exceptions collected so far
|
|
706
|
+
* are expected errors.
|
|
707
|
+
*/
|
|
708
|
+
Transaction.prototype.hasOnlyExpectedErrors = function hasOnlyExpectedErrors() {
|
|
709
|
+
if (0 === this.exceptions.length) {
|
|
710
|
+
return false
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
for (let i = 0;i < this.exceptions.length;i++) {
|
|
714
|
+
const exception = this.exceptions[i]
|
|
715
|
+
if (!errorHelper.isExpectedException(
|
|
716
|
+
this,
|
|
717
|
+
exception[0],
|
|
718
|
+
this.agent.config,
|
|
719
|
+
urltils
|
|
720
|
+
)) {
|
|
721
|
+
return false
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return true
|
|
725
|
+
}
|
|
726
|
+
|
|
690
727
|
/**
|
|
691
728
|
* Returns agent intrinsic attribute for this transaction.
|
|
692
729
|
*/
|
package/lib/util/urltils.js
CHANGED
|
@@ -61,6 +61,19 @@ module.exports = {
|
|
|
61
61
|
return code >= 400 && isIgnoredStatusCodeForErrors(config, code)
|
|
62
62
|
},
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Returns true if the status code is configured to be expected
|
|
66
|
+
*
|
|
67
|
+
* @param {Config} config The configuration containing the error list.
|
|
68
|
+
* @param {string} code The HTTP status code to check.
|
|
69
|
+
*
|
|
70
|
+
* @returns {bool} Whether the status code is expected.
|
|
71
|
+
*
|
|
72
|
+
*/
|
|
73
|
+
isExpectedError: function isExpectedError(config, code) {
|
|
74
|
+
return isExpectedStatusCodeForErrors(config, code)
|
|
75
|
+
},
|
|
76
|
+
|
|
64
77
|
/**
|
|
65
78
|
* Get back the pieces of the URL that New Relic cares about. Apply these
|
|
66
79
|
* restrictions, in order:
|
|
@@ -197,3 +210,13 @@ function isIgnoredStatusCodeForErrors(config, code) {
|
|
|
197
210
|
}
|
|
198
211
|
return codes.indexOf(parseInt(code, 10)) >= 0
|
|
199
212
|
}
|
|
213
|
+
|
|
214
|
+
function isExpectedStatusCodeForErrors(config, code) {
|
|
215
|
+
var codes = []
|
|
216
|
+
if (config &&
|
|
217
|
+
config.error_collector &&
|
|
218
|
+
config.error_collector.expected_status_codes) {
|
|
219
|
+
codes = config.error_collector.expected_status_codes
|
|
220
|
+
}
|
|
221
|
+
return codes.indexOf(parseInt(code, 10)) >= 0
|
|
222
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"contributors": [
|
|
@@ -154,8 +154,8 @@
|
|
|
154
154
|
"generic-pool": "^3.6.1",
|
|
155
155
|
"glob": "^7.1.2",
|
|
156
156
|
"got": "^8.0.1",
|
|
157
|
-
"jsdoc": "^3.
|
|
158
|
-
"lodash": "^4.17.
|
|
157
|
+
"jsdoc": "^3.6.3",
|
|
158
|
+
"lodash": "^4.17.14",
|
|
159
159
|
"memcached": ">=0.2.8",
|
|
160
160
|
"minami": "^1.1.1",
|
|
161
161
|
"mocha": "^5.2.0",
|