newrelic 9.7.0 → 9.7.2
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 +42 -18
- package/THIRD_PARTY_NOTICES.md +203 -2
- package/api.js +155 -103
- package/lib/agent.js +29 -82
- package/lib/collector/api.js +234 -201
- package/lib/config/attribute-filter.js +35 -25
- package/lib/config/default.js +45 -19
- package/lib/config/index.js +260 -199
- package/lib/environment.js +16 -12
- package/lib/errors/error-collector.js +124 -55
- package/lib/errors/index.js +59 -49
- package/lib/instrumentation/@hapi/hapi.js +56 -50
- package/lib/instrumentation/@node-redis/client.js +50 -41
- package/lib/instrumentation/amqplib.js +116 -151
- package/lib/instrumentation/core/{async_hooks.js → async-hooks.js} +26 -11
- package/lib/instrumentation/core/globals.js +1 -1
- package/lib/instrumentation/core/http-outbound.js +193 -78
- package/lib/instrumentation/core/timers.js +106 -59
- package/lib/instrumentation/grpc-js/grpc.js +20 -23
- package/lib/instrumentation/mongodb/common.js +87 -85
- package/lib/instrumentation/redis.js +112 -90
- package/lib/instrumentation/undici.js +204 -192
- package/lib/instrumentation/{when.js → when/constants.js} +13 -10
- package/lib/instrumentation/when/contextualizer.js +168 -0
- package/lib/instrumentation/when/index.js +354 -0
- package/lib/instrumentation/when/nr-hooks.js +15 -0
- package/lib/instrumentations.js +1 -1
- package/lib/shim/shim.js +2 -0
- package/lib/shim/webframework-shim.js +19 -0
- package/lib/symbols.js +1 -0
- package/lib/system-info.js +240 -163
- package/lib/util/async-each-limit.js +30 -0
- package/lib/util/attributes.js +159 -0
- package/lib/util/code-level-metrics.js +58 -0
- package/lib/util/deep-equal.js +11 -144
- package/package.json +5 -4
- package/lib/instrumentation/promise.js +0 -572
package/lib/environment.js
CHANGED
|
@@ -10,6 +10,7 @@ const { fsPromises } = require('./util/unwrapped-core')
|
|
|
10
10
|
const os = require('os')
|
|
11
11
|
const logger = require('./logger').child({ component: 'environment' })
|
|
12
12
|
const stringify = require('json-stringify-safe')
|
|
13
|
+
const asyncEachLimit = require('./util/async-each-limit')
|
|
13
14
|
const DISPATCHER_VERSION = 'Dispatcher Version'
|
|
14
15
|
|
|
15
16
|
// As of 1.7.0 you can no longer dynamically link v8
|
|
@@ -72,7 +73,7 @@ async function listPackages(root, packages = []) {
|
|
|
72
73
|
|
|
73
74
|
try {
|
|
74
75
|
const dirs = await fsPromises.readdir(root)
|
|
75
|
-
await
|
|
76
|
+
await asyncEachLimit(dirs, forEachDir, 2)
|
|
76
77
|
_log('Done listing packages in %s', root)
|
|
77
78
|
} catch (err) {
|
|
78
79
|
logger.trace(err, 'Could not list packages in %s (probably not an error)', root)
|
|
@@ -122,7 +123,7 @@ async function listDependencies(root, children = [], visited = Object.create(nul
|
|
|
122
123
|
|
|
123
124
|
try {
|
|
124
125
|
const dirs = await fsPromises.readdir(root)
|
|
125
|
-
await
|
|
126
|
+
await asyncEachLimit(dirs, forEachEntry, 2)
|
|
126
127
|
_log('Done listing dependencies in %s', root)
|
|
127
128
|
} catch (err) {
|
|
128
129
|
logger.trace(err, 'Could not read directories in %s (probably not an error)', root)
|
|
@@ -232,7 +233,7 @@ function getGlobalPackages() {
|
|
|
232
233
|
* package appears at most once, with all the versions joined into a
|
|
233
234
|
* comma-delimited list.
|
|
234
235
|
*
|
|
235
|
-
* @param packages
|
|
236
|
+
* @param {Array} packages list of packages to process
|
|
236
237
|
* @returns {Array.<string[]>} Sorted list of [name, version] pairs.
|
|
237
238
|
*/
|
|
238
239
|
function flattenVersions(packages) {
|
|
@@ -308,15 +309,18 @@ async function getOtherPackages() {
|
|
|
308
309
|
}
|
|
309
310
|
_log('Looking for other packages in %j', paths)
|
|
310
311
|
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
312
|
+
const otherPackages = await asyncEachLimit(
|
|
313
|
+
paths,
|
|
314
|
+
(nodePath) => {
|
|
315
|
+
if (nodePath[0] !== '/') {
|
|
316
|
+
nodePath = path.resolve(process.cwd(), nodePath)
|
|
317
|
+
}
|
|
318
|
+
_log('Getting other packages from %s', nodePath)
|
|
319
|
+
return getPackages(nodePath)
|
|
320
|
+
},
|
|
321
|
+
2
|
|
322
|
+
)
|
|
318
323
|
|
|
319
|
-
const otherPackages = await Promise.all(pathPromises)
|
|
320
324
|
otherPackages.forEach((pkg) => {
|
|
321
325
|
other.packages.push.apply(other.packages, pkg.packages)
|
|
322
326
|
other.dependencies.push.apply(other.dependencies, pkg.dependencies)
|
|
@@ -470,7 +474,7 @@ async function refresh() {
|
|
|
470
474
|
* Refreshes settings and returns the settings object.
|
|
471
475
|
*
|
|
472
476
|
* @private
|
|
473
|
-
* @returns {Promise}
|
|
477
|
+
* @returns {Promise} the updated/refreshed settings
|
|
474
478
|
*/
|
|
475
479
|
async function getJSON() {
|
|
476
480
|
_log('Getting environment JSON')
|
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 25] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
|
-
|
|
10
8
|
const errorsModule = require('./index')
|
|
11
9
|
|
|
12
10
|
const logger = require('../logger').child({ component: 'error_tracer' })
|
|
13
11
|
const urltils = require('../util/urltils')
|
|
14
12
|
const Exception = require('../errors').Exception
|
|
13
|
+
const Transaction = require('../transaction')
|
|
15
14
|
const errorHelper = require('./helper')
|
|
16
15
|
const createError = errorsModule.createError
|
|
17
16
|
const createEvent = errorsModule.createEvent
|
|
@@ -75,6 +74,7 @@ class ErrorCollector {
|
|
|
75
74
|
*
|
|
76
75
|
* @param {?Transaction} transaction -
|
|
77
76
|
* @param {Error} exception - The error to be checked.
|
|
77
|
+
* @returns {boolean} whether or not the exception has already been tracked
|
|
78
78
|
*/
|
|
79
79
|
_haveSeen(transaction, exception) {
|
|
80
80
|
const txId = transaction ? transaction.id : 'Unknown'
|
|
@@ -109,12 +109,78 @@ class ErrorCollector {
|
|
|
109
109
|
return false
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Helper method for processing errors that are created with .noticeError()
|
|
114
|
+
*
|
|
115
|
+
* @param {Transaction} transaction the collected exception's transaction
|
|
116
|
+
* @param {number} collectedErrors the number of errors we've successfully .collect()-ed
|
|
117
|
+
* @returns {number} the new number of errors successfully .collect()-ed
|
|
118
|
+
*/
|
|
119
|
+
_processUserErrors(transaction, collectedErrors) {
|
|
120
|
+
if (transaction.userErrors.length) {
|
|
121
|
+
for (let i = 0; i < transaction.userErrors.length; i++) {
|
|
122
|
+
const exception = transaction.userErrors[i]
|
|
123
|
+
if (this.collect(transaction, exception)) {
|
|
124
|
+
++collectedErrors
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return collectedErrors
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Helper method for processing exceptions on the transaction (transaction.exceptions array)
|
|
134
|
+
*
|
|
135
|
+
* @param {Transaction} transaction the transaction being processed
|
|
136
|
+
* @param {number} collectedErrors the number of errors successfully .collect()-ed
|
|
137
|
+
* @param {number} expectedErrors the number of collected errors that were expected
|
|
138
|
+
* @returns {Array.<number>} the updated [collectedErrors, expectedErrors] numbers post-processing
|
|
139
|
+
*/
|
|
140
|
+
_processTransactionExceptions(transaction, collectedErrors, expectedErrors) {
|
|
141
|
+
for (let i = 0; i < transaction.exceptions.length; i++) {
|
|
142
|
+
const exception = transaction.exceptions[i]
|
|
143
|
+
if (this.collect(transaction, exception)) {
|
|
144
|
+
++collectedErrors
|
|
145
|
+
// if we could collect it, then check if expected
|
|
146
|
+
if (
|
|
147
|
+
urltils.isExpectedError(this.config, transaction.statusCode) ||
|
|
148
|
+
errorHelper.isExpectedException(transaction, exception.error, this.config, urltils)
|
|
149
|
+
) {
|
|
150
|
+
++expectedErrors
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [collectedErrors, expectedErrors]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Helper method for processing an inferred error based on Transaction metadata
|
|
160
|
+
*
|
|
161
|
+
* @param {Transaction} transaction the transaction being processed
|
|
162
|
+
* @param {number} collectedErrors the number of errors successfully .collect()-ed
|
|
163
|
+
* @param {number} expectedErrors the number of collected errors that were expected
|
|
164
|
+
* @returns {Array.<number>} the updated [collectedErrors, expectedErrors] numbers post-processing
|
|
165
|
+
*/
|
|
166
|
+
_processTransactionErrors(transaction, collectedErrors, expectedErrors) {
|
|
167
|
+
if (this.collect(transaction)) {
|
|
168
|
+
++collectedErrors
|
|
169
|
+
if (urltils.isExpectedError(this.config, transaction.statusCode)) {
|
|
170
|
+
++expectedErrors
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return [collectedErrors, expectedErrors]
|
|
175
|
+
}
|
|
176
|
+
|
|
112
177
|
/**
|
|
113
178
|
* Every finished transaction goes through this handler, so do as little as
|
|
114
179
|
* possible.
|
|
115
180
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
181
|
+
* TODO: Prob shouldn't do any work if errors fully disabled.
|
|
182
|
+
*
|
|
183
|
+
* @param {Transaction} transaction the completed transaction
|
|
118
184
|
*/
|
|
119
185
|
onTransactionFinished(transaction) {
|
|
120
186
|
if (!transaction) {
|
|
@@ -124,48 +190,30 @@ class ErrorCollector {
|
|
|
124
190
|
return
|
|
125
191
|
}
|
|
126
192
|
|
|
127
|
-
// TODO: Prob shouldn't do any work if errors fully disabled.
|
|
128
|
-
|
|
129
193
|
// collect user errors even if status code is ignored
|
|
130
194
|
let collectedErrors = 0
|
|
131
195
|
let expectedErrors = 0
|
|
132
196
|
|
|
133
197
|
// errors from noticeError are currently exempt from
|
|
134
198
|
// ignore and exclude rules
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < transaction.userErrors.length; i++) {
|
|
137
|
-
const exception = transaction.userErrors[i]
|
|
138
|
-
if (this.collect(transaction, exception)) {
|
|
139
|
-
++collectedErrors
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
199
|
+
collectedErrors = this._processUserErrors(transaction, collectedErrors)
|
|
143
200
|
|
|
144
201
|
const isErroredTransaction = urltils.isError(this.config, transaction.statusCode)
|
|
145
202
|
const isIgnoredErrorStatusCode = urltils.isIgnoredError(this.config, transaction.statusCode)
|
|
146
203
|
|
|
147
|
-
const isExpectedErrorStatusCode = urltils.isExpectedError(this.config, transaction.statusCode)
|
|
148
|
-
|
|
149
204
|
// collect other exceptions only if status code is not ignored
|
|
150
205
|
if (transaction.exceptions.length && !isIgnoredErrorStatusCode) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
} else if (isErroredTransaction && this.collect(transaction)) {
|
|
165
|
-
++collectedErrors
|
|
166
|
-
if (isExpectedErrorStatusCode) {
|
|
167
|
-
++expectedErrors
|
|
168
|
-
}
|
|
206
|
+
;[collectedErrors, expectedErrors] = this._processTransactionExceptions(
|
|
207
|
+
transaction,
|
|
208
|
+
collectedErrors,
|
|
209
|
+
expectedErrors
|
|
210
|
+
)
|
|
211
|
+
} else if (isErroredTransaction) {
|
|
212
|
+
;[collectedErrors, expectedErrors] = this._processTransactionErrors(
|
|
213
|
+
transaction,
|
|
214
|
+
collectedErrors,
|
|
215
|
+
expectedErrors
|
|
216
|
+
)
|
|
169
217
|
}
|
|
170
218
|
|
|
171
219
|
const unexpectedErrors = collectedErrors - expectedErrors
|
|
@@ -226,9 +274,8 @@ class ErrorCollector {
|
|
|
226
274
|
* NOTE: this interface is unofficial and may change in future.
|
|
227
275
|
*
|
|
228
276
|
* @param {?Transaction} transaction Transaction associated with the error.
|
|
229
|
-
* @param {
|
|
230
|
-
* @param error
|
|
231
|
-
* @param customAttributes
|
|
277
|
+
* @param {*} error The error passed into `API#noticeError()`
|
|
278
|
+
* @param {object} customAttributes custom attributes to add to the error
|
|
232
279
|
*/
|
|
233
280
|
addUserError(transaction, error, customAttributes) {
|
|
234
281
|
if (!error) {
|
|
@@ -263,13 +310,38 @@ class ErrorCollector {
|
|
|
263
310
|
*
|
|
264
311
|
* @param {?Transaction} transaction Transaction associated with the error.
|
|
265
312
|
* @param {?Exception} exception The Exception object to be traced.
|
|
266
|
-
* @returns {
|
|
313
|
+
* @returns {boolean} True if the error was collected.
|
|
267
314
|
*/
|
|
268
|
-
collect(transaction, exception) {
|
|
269
|
-
if (!exception) {
|
|
270
|
-
|
|
315
|
+
collect(transaction, exception = new Exception({})) {
|
|
316
|
+
if (!this._isValidException(exception, transaction)) {
|
|
317
|
+
return false
|
|
271
318
|
}
|
|
272
319
|
|
|
320
|
+
const errorTrace = createError(transaction, exception, this.config)
|
|
321
|
+
this._maybeRecordErrorMetrics(errorTrace, transaction)
|
|
322
|
+
|
|
323
|
+
// defaults true in config/index. can be modified server-side
|
|
324
|
+
if (this.config.collect_errors) {
|
|
325
|
+
this.traceAggregator.add(errorTrace)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (this.config.error_collector.capture_events === true) {
|
|
329
|
+
const priority = (transaction && transaction.priority) || Math.random()
|
|
330
|
+
const event = createEvent(transaction, errorTrace, exception.timestamp, this.config)
|
|
331
|
+
this.eventAggregator.add(event, priority)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return true
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Helper method for ensuring that a collected exception/transaction combination can be collected
|
|
339
|
+
*
|
|
340
|
+
* @param {object} exception the exception to validate
|
|
341
|
+
* @param {Transaction} transaction the Transaction to validate, if exception is malformed we'll try to fallback to transaction data
|
|
342
|
+
* @returns {boolean} whether or not the exception/transaction combo has everything needed for processing
|
|
343
|
+
*/
|
|
344
|
+
_isValidException(exception, transaction) {
|
|
273
345
|
if (exception.error) {
|
|
274
346
|
if (this._haveSeen(transaction, exception.error)) {
|
|
275
347
|
return false
|
|
@@ -288,10 +360,20 @@ class ErrorCollector {
|
|
|
288
360
|
|
|
289
361
|
if (exception.error) {
|
|
290
362
|
logger.trace(exception.error, 'Got exception to trace:')
|
|
363
|
+
} else {
|
|
364
|
+
logger.trace(transaction, 'Got transaction error to trace:')
|
|
291
365
|
}
|
|
292
366
|
|
|
293
|
-
|
|
367
|
+
return true
|
|
368
|
+
}
|
|
294
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Helper method for recording metrics about errors depending on the type of error that happened
|
|
372
|
+
*
|
|
373
|
+
* @param {Array} errorTrace list of error information
|
|
374
|
+
* @param {Transaction} transaction the transaction associated with the trace
|
|
375
|
+
*/
|
|
376
|
+
_maybeRecordErrorMetrics(errorTrace, transaction) {
|
|
295
377
|
const isExpectedError = true === errorTrace[4].intrinsics['error.expected']
|
|
296
378
|
|
|
297
379
|
if (isExpectedError) {
|
|
@@ -307,19 +389,6 @@ class ErrorCollector {
|
|
|
307
389
|
}
|
|
308
390
|
}
|
|
309
391
|
}
|
|
310
|
-
|
|
311
|
-
// defaults true in config/index. can be modified server-side
|
|
312
|
-
if (this.config.collect_errors) {
|
|
313
|
-
this.traceAggregator.add(errorTrace)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (this.config.error_collector.capture_events === true) {
|
|
317
|
-
const priority = (transaction && transaction.priority) || Math.random()
|
|
318
|
-
const event = createEvent(transaction, errorTrace, exception.timestamp, this.config)
|
|
319
|
-
this.eventAggregator.add(event, priority)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return true
|
|
323
392
|
}
|
|
324
393
|
|
|
325
394
|
// TODO: ideally, this becomes unnecessary
|
package/lib/errors/index.js
CHANGED
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 22] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
|
-
|
|
10
8
|
const DESTINATIONS = require('../config/attribute-filter').DESTINATIONS
|
|
11
|
-
const NAMES = require('../metrics/names')
|
|
12
9
|
const props = require('../util/properties')
|
|
13
10
|
const urltils = require('../util/urltils')
|
|
14
11
|
const errorHelper = require('../errors/helper')
|
|
12
|
+
const {
|
|
13
|
+
maybeAddQueueAttributes,
|
|
14
|
+
maybeAddExternalAttributes,
|
|
15
|
+
maybeAddDatabaseAttributes,
|
|
16
|
+
maybeAddSyntheticAttributes
|
|
17
|
+
} = require('../util/attributes')
|
|
18
|
+
const Transaction = require('../transaction')
|
|
15
19
|
const ERROR_EXPECTED_PATH = 'error.expected'
|
|
16
20
|
|
|
17
21
|
class Exception {
|
|
@@ -48,9 +52,19 @@ class Exception {
|
|
|
48
52
|
* handler, which traps actual instances of Error, try to set sensible
|
|
49
53
|
* defaults for everything.
|
|
50
54
|
*
|
|
55
|
+
* NOTE: this function returns an array, but also conditionally mutates the array
|
|
56
|
+
* to add a "transaction" property with the transaction id to the array, which works
|
|
57
|
+
* because everything's an object in JS. I'm not entirely sure why we do this, but
|
|
58
|
+
* weird enough to make note of
|
|
59
|
+
*
|
|
51
60
|
* @param {Transaction} transaction The agent transaction, coming from the instrumentatation
|
|
52
61
|
* @param {Exception} exception An custom Exception object with the error and other information
|
|
53
62
|
* @param {object} config The configuration to use when creating the object
|
|
63
|
+
* @returns {Array} an Array of Error information, [0] -> placeholder,
|
|
64
|
+
* [1] -> name extracted from error info,
|
|
65
|
+
* [2] -> extracted error message,
|
|
66
|
+
* [3] -> extracted error type,
|
|
67
|
+
* [4] -> attributes
|
|
54
68
|
*/
|
|
55
69
|
function createError(transaction, exception, config) {
|
|
56
70
|
const error = exception.error
|
|
@@ -70,60 +84,74 @@ function createError(transaction, exception, config) {
|
|
|
70
84
|
if (transaction) {
|
|
71
85
|
// Copy all of the parameters off of the transaction.
|
|
72
86
|
params.intrinsics = transaction.getIntrinsicAttributes()
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
const transactionAgentAttributes =
|
|
88
|
+
transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT) || {}
|
|
75
89
|
|
|
76
90
|
// Merge the agent attributes specific to this error event with the transaction attributes
|
|
77
|
-
|
|
78
|
-
params.agentAttributes = agentAttributes
|
|
91
|
+
params.agentAttributes = Object.assign(exception.agentAttributes, transactionAgentAttributes)
|
|
79
92
|
|
|
80
93
|
// There should be no attributes to copy in HSM, but check status anyway
|
|
81
94
|
if (!config.high_security) {
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
urltils.overwriteParameters(
|
|
96
|
+
transaction.trace.custom.get(DESTINATIONS.ERROR_EVENT),
|
|
97
|
+
params.userAttributes
|
|
98
|
+
)
|
|
84
99
|
}
|
|
85
100
|
}
|
|
86
101
|
|
|
102
|
+
maybeAddUserAttributes(params.userAttributes, exception, config)
|
|
103
|
+
|
|
104
|
+
params.stack_trace = maybeAddStackTrace(exception, config)
|
|
105
|
+
|
|
106
|
+
params.intrinsics[ERROR_EXPECTED_PATH] = errorHelper.isExpected(
|
|
107
|
+
type,
|
|
108
|
+
message,
|
|
109
|
+
transaction,
|
|
110
|
+
config,
|
|
111
|
+
urltils
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return [0, name, message, type, params]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function maybeAddUserAttributes(userAttributes, exception, config) {
|
|
87
118
|
const customAttributes = exception.customAttributes
|
|
88
119
|
if (!config.high_security && config.api.custom_attributes_enabled && customAttributes) {
|
|
89
120
|
for (const key in customAttributes) {
|
|
90
121
|
if (props.hasOwn(customAttributes, key)) {
|
|
91
122
|
const dest = config.attributeFilter.filterTransaction(DESTINATIONS.ERROR_EVENT, key)
|
|
92
123
|
if (dest & DESTINATIONS.ERROR_EVENT) {
|
|
93
|
-
|
|
124
|
+
userAttributes[key] = customAttributes[key]
|
|
94
125
|
}
|
|
95
126
|
}
|
|
96
127
|
}
|
|
97
128
|
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function maybeAddStackTrace(exception, config) {
|
|
132
|
+
const stack = exception.error?.stack
|
|
133
|
+
let parsedStack
|
|
98
134
|
|
|
99
|
-
const stack = exception.error && exception.error.stack
|
|
100
135
|
if (stack) {
|
|
101
|
-
|
|
136
|
+
parsedStack = ('' + stack).split(/[\n\r]/g)
|
|
137
|
+
|
|
102
138
|
if (config.high_security || config.strip_exception_messages.enabled) {
|
|
103
|
-
|
|
139
|
+
parsedStack[0] = exception.error.name + ': <redacted>'
|
|
104
140
|
}
|
|
105
141
|
}
|
|
106
142
|
|
|
107
|
-
|
|
108
|
-
if (errorHelper.isExpected(type, message, transaction, config, urltils)) {
|
|
109
|
-
params.intrinsics[ERROR_EXPECTED_PATH] = true
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const res = [0, name, message, type, params]
|
|
113
|
-
if (transaction) {
|
|
114
|
-
res.transaction = transaction.id
|
|
115
|
-
}
|
|
116
|
-
return res
|
|
143
|
+
return parsedStack
|
|
117
144
|
}
|
|
118
145
|
|
|
119
146
|
/**
|
|
120
147
|
* Creates a structure for error event that is sent to the collector.
|
|
121
148
|
* The error parameter is an output of the createError() function for a given exception.
|
|
122
149
|
*
|
|
123
|
-
* @param transaction
|
|
124
|
-
* @param error
|
|
125
|
-
* @param timestamp
|
|
126
|
-
* @param config
|
|
150
|
+
* @param {Transaction} transaction the current transaction
|
|
151
|
+
* @param {Array} error createError() output
|
|
152
|
+
* @param {string} timestamp the timestamp of the error event
|
|
153
|
+
* @param {object} config agent configuration object
|
|
154
|
+
* @returns {Array} an Array of different types of attributes [0] -> intrinsic, [1] -> user/custom, [2] -> agent
|
|
127
155
|
*/
|
|
128
156
|
function createEvent(transaction, error, timestamp, config) {
|
|
129
157
|
const message = error[2]
|
|
@@ -166,28 +194,10 @@ function _getErrorEventIntrinsicAttrs(transaction, errorClass, message, expected
|
|
|
166
194
|
attributes.transactionName = transaction.getFullName()
|
|
167
195
|
attributes.duration = transaction.timer.getDurationInMillis() / 1000
|
|
168
196
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
metric = transaction.metrics.getMetric(NAMES.EXTERNAL.ALL)
|
|
175
|
-
if (metric) {
|
|
176
|
-
attributes.externalDuration = metric.total
|
|
177
|
-
attributes.externalCallCount = metric.callCount
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
metric = transaction.metrics.getMetric(NAMES.DB.ALL)
|
|
181
|
-
if (metric) {
|
|
182
|
-
attributes.databaseDuration = metric.total
|
|
183
|
-
attributes.databaseCallCount = metric.callCount
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (transaction.syntheticsData) {
|
|
187
|
-
attributes['nr.syntheticsResourceId'] = transaction.syntheticsData.resourceId
|
|
188
|
-
attributes['nr.syntheticsJobId'] = transaction.syntheticsData.jobId
|
|
189
|
-
attributes['nr.syntheticsMonitorId'] = transaction.syntheticsData.monitorId
|
|
190
|
-
}
|
|
197
|
+
maybeAddQueueAttributes(transaction, attributes)
|
|
198
|
+
maybeAddExternalAttributes(transaction, attributes)
|
|
199
|
+
maybeAddDatabaseAttributes(transaction, attributes)
|
|
200
|
+
maybeAddSyntheticAttributes(transaction, attributes)
|
|
191
201
|
|
|
192
202
|
if (transaction.agent.config.distributed_tracing.enabled) {
|
|
193
203
|
transaction.addDistributedTraceIntrinsics(attributes)
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
/* eslint sonarjs/cognitive-complexity: ["error", 68] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
|
-
|
|
10
8
|
const record = require('../../metrics/recorders/generic')
|
|
11
9
|
// This object defines all the events that we want to wrap extensions
|
|
12
10
|
// for, as they are the only ones associated with requests.
|
|
@@ -66,6 +64,12 @@ function serverPostConstructor(shim) {
|
|
|
66
64
|
return
|
|
67
65
|
}
|
|
68
66
|
|
|
67
|
+
wrapProtoDecorate(shim, proto)
|
|
68
|
+
wrapProtoRoute(shim, proto)
|
|
69
|
+
wrapProtoExt(shim, proto)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function wrapProtoDecorate(shim, proto) {
|
|
69
73
|
shim.wrap(proto, 'decorate', function wrapDecorate(shim, original) {
|
|
70
74
|
return function wrappedDecorate(type) {
|
|
71
75
|
// server.decorate also accepts 'request', 'toolkit', 'server' types,
|
|
@@ -99,7 +103,9 @@ function serverPostConstructor(shim) {
|
|
|
99
103
|
return original.apply(this, args)
|
|
100
104
|
}
|
|
101
105
|
})
|
|
106
|
+
}
|
|
102
107
|
|
|
108
|
+
function wrapProtoRoute(shim, proto) {
|
|
103
109
|
shim.wrap(proto, 'route', function wrapRoute(shim, original) {
|
|
104
110
|
return function wrappedRoute() {
|
|
105
111
|
const args = shim.argsToArray.apply(shim, arguments)
|
|
@@ -116,51 +122,53 @@ function serverPostConstructor(shim) {
|
|
|
116
122
|
this.realm.modifiers.route.prefix) ||
|
|
117
123
|
''
|
|
118
124
|
|
|
119
|
-
_wrapRoute(shim, args[0])
|
|
125
|
+
_wrapRoute(shim, args[0], prefix)
|
|
120
126
|
|
|
121
127
|
return original.apply(this, args)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
return
|
|
130
|
-
} else if (route.options) {
|
|
131
|
-
// v17 now prefers `options` property...
|
|
132
|
-
if (route.options.pre) {
|
|
133
|
-
// config objects can also contain multiple OTHER handlers in a `pre` array
|
|
134
|
-
route.options.pre = wrapPreHandlers(shim, route.options.pre, routePath)
|
|
135
|
-
}
|
|
136
|
-
if (route.options.handler) {
|
|
137
|
-
_wrapRouteHandler(shim, route.options, routePath)
|
|
138
|
-
return
|
|
139
|
-
}
|
|
140
|
-
} else if (route.config) {
|
|
141
|
-
// ... but `config` still works
|
|
142
|
-
if (route.config.pre) {
|
|
143
|
-
route.config.pre = wrapPreHandlers(shim, route.config.pre, routePath)
|
|
144
|
-
}
|
|
145
|
-
if (route.config.handler) {
|
|
146
|
-
_wrapRouteHandler(shim, route.config, routePath)
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
_wrapRouteHandler(shim, route, routePath)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function _wrapRouteHandler(shim, container, path) {
|
|
154
|
-
if (typeof container.handler !== 'function') {
|
|
155
|
-
return
|
|
156
|
-
}
|
|
157
|
-
shim.wrap(container, 'handler', function wrapHandler(shim, handler) {
|
|
158
|
-
return wrapRouteHandler(shim, handler, path)
|
|
159
|
-
})
|
|
160
|
-
}
|
|
132
|
+
function _wrapRoute(shim, route, prefix) {
|
|
133
|
+
const routePath = prefix + route.path
|
|
134
|
+
if (shim.isArray(route)) {
|
|
135
|
+
for (let i = 0; i < route.length; ++i) {
|
|
136
|
+
_wrapRoute(shim, route[i], prefix)
|
|
161
137
|
}
|
|
138
|
+
return
|
|
139
|
+
} else if (route.options) {
|
|
140
|
+
// v17 now prefers `options` property...
|
|
141
|
+
if (route.options.pre) {
|
|
142
|
+
// config objects can also contain multiple OTHER handlers in a `pre` array
|
|
143
|
+
route.options.pre = wrapPreHandlers(shim, route.options.pre, routePath)
|
|
144
|
+
}
|
|
145
|
+
if (route.options.handler) {
|
|
146
|
+
_wrapRouteHandler(shim, route.options, routePath)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
} else if (route.config) {
|
|
150
|
+
// ... but `config` still works
|
|
151
|
+
if (route.config.pre) {
|
|
152
|
+
route.config.pre = wrapPreHandlers(shim, route.config.pre, routePath)
|
|
153
|
+
}
|
|
154
|
+
if (route.config.handler) {
|
|
155
|
+
_wrapRouteHandler(shim, route.config, routePath)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
_wrapRouteHandler(shim, route, routePath)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function _wrapRouteHandler(shim, container, path) {
|
|
163
|
+
if (typeof container.handler !== 'function') {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
shim.wrap(container, 'handler', function wrapHandler(shim, handler) {
|
|
167
|
+
return wrapRouteHandler(shim, handler, path)
|
|
162
168
|
})
|
|
169
|
+
}
|
|
163
170
|
|
|
171
|
+
function wrapProtoExt(shim, proto) {
|
|
164
172
|
shim.wrap(proto, 'ext', function wrapExt(shim, original) {
|
|
165
173
|
return function wrappedExt(event, method) {
|
|
166
174
|
const args = shim.argsToArray.apply(shim, arguments)
|
|
@@ -208,15 +216,13 @@ function wrapRouteHandler(shim, handler, path) {
|
|
|
208
216
|
return shim.recordMiddleware(handler, {
|
|
209
217
|
route: path,
|
|
210
218
|
req: function getReq(shim, fn, fnName, args) {
|
|
211
|
-
const request = args
|
|
212
|
-
|
|
213
|
-
return request.raw.req
|
|
214
|
-
}
|
|
219
|
+
const [request] = args
|
|
220
|
+
return request?.raw?.req
|
|
215
221
|
},
|
|
216
222
|
promise: true,
|
|
217
223
|
params: function getParams(shim, fn, fnName, args) {
|
|
218
|
-
const req = args
|
|
219
|
-
return req
|
|
224
|
+
const [req] = args
|
|
225
|
+
return req?.params
|
|
220
226
|
}
|
|
221
227
|
})
|
|
222
228
|
}
|
|
@@ -230,9 +236,9 @@ function wrapMiddleware(shim, middleware, event) {
|
|
|
230
236
|
route: event,
|
|
231
237
|
type: event === 'onPreResponse' ? shim.ERRORWARE : shim.MIDDLEWARE,
|
|
232
238
|
promise: true,
|
|
233
|
-
req: function getReq(
|
|
234
|
-
const req = args
|
|
235
|
-
return req
|
|
239
|
+
req: function getReq(_shim, _fn, _fnName, args) {
|
|
240
|
+
const [req] = args
|
|
241
|
+
return req?.raw?.req
|
|
236
242
|
}
|
|
237
243
|
})
|
|
238
244
|
}
|