newrelic 9.6.0 → 9.7.1
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 +40 -16
- package/THIRD_PARTY_NOTICES.md +203 -2
- package/api.js +155 -101
- package/bin/test-naming-rules.js +1 -1
- package/esm-loader.mjs +1 -3
- package/lib/adaptive-sampler.js +1 -2
- package/lib/agent.js +29 -80
- package/lib/aggregators/event-aggregator.js +2 -2
- package/lib/attributes.js +2 -4
- package/lib/collector/api.js +252 -211
- package/lib/collector/facts.js +1 -3
- package/lib/collector/parse-response.js +3 -1
- package/lib/collector/remote-method.js +8 -9
- package/lib/config/attribute-filter.js +42 -34
- package/lib/config/default.js +60 -17
- package/lib/config/harvest-config-validator.js +3 -5
- package/lib/config/index.js +263 -199
- package/lib/environment.js +22 -17
- package/lib/errors/error-collector.js +123 -51
- package/lib/errors/helper.js +10 -18
- package/lib/errors/index.js +63 -47
- package/lib/grpc/connection.js +12 -5
- package/lib/header-attributes.js +4 -2
- package/lib/header-processing.js +2 -2
- package/lib/instrumentation/@hapi/hapi.js +56 -48
- package/lib/instrumentation/@node-redis/client.js +50 -39
- package/lib/instrumentation/amqplib.js +116 -149
- package/lib/instrumentation/core/{async_hooks.js → async-hooks.js} +26 -9
- package/lib/instrumentation/core/globals.js +12 -9
- package/lib/instrumentation/core/http-outbound.js +200 -83
- package/lib/instrumentation/core/http.js +10 -8
- package/lib/instrumentation/core/timers.js +106 -57
- package/lib/instrumentation/fastify.js +1 -3
- package/lib/instrumentation/grpc-js/grpc.js +14 -3
- package/lib/instrumentation/memcached.js +5 -0
- package/lib/instrumentation/mongodb/common.js +88 -83
- package/lib/instrumentation/mongodb/v2-mongo.js +6 -3
- package/lib/instrumentation/mongodb.js +1 -1
- package/lib/instrumentation/nr-winston-transport.js +3 -4
- package/lib/instrumentation/redis.js +112 -88
- package/lib/instrumentation/undici.js +204 -190
- 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/metrics/mapper.js +2 -3
- package/lib/metrics/metric-aggregator.js +1 -3
- package/lib/metrics/names.js +19 -15
- package/lib/metrics/normalizer/rule.js +6 -7
- package/lib/metrics/normalizer/tx_segment.js +3 -2
- package/lib/metrics/normalizer.js +5 -3
- package/lib/parse-proc-cpuinfo.js +11 -6
- package/lib/serverless/api-gateway.js +3 -4
- package/lib/serverless/aws-lambda.js +3 -3
- package/lib/shim/conglomerate-shim.js +2 -3
- package/lib/shim/datastore-shim.js +22 -104
- package/lib/shim/index.js +7 -0
- package/lib/shim/message-shim.js +2 -0
- package/lib/shim/promise-shim.js +48 -42
- package/lib/shim/shim.js +9 -7
- package/lib/shim/webframework-shim.js +21 -0
- package/lib/spans/map-to-streaming-type.js +0 -2
- package/lib/spans/span-context.js +2 -0
- package/lib/spans/span-event-aggregator.js +2 -2
- package/lib/spans/span-event.js +2 -2
- package/lib/spans/span-streamer.js +4 -0
- package/lib/spans/streaming-span-event-aggregator.js +2 -4
- package/lib/spans/streaming-span-event.js +3 -2
- package/lib/symbols.js +1 -0
- package/lib/system-info.js +241 -166
- package/lib/timer.js +8 -6
- package/lib/transaction/dt-payload.js +4 -3
- package/lib/transaction/handle.js +12 -2
- package/lib/transaction/index.js +19 -18
- package/lib/transaction/name-state.js +17 -2
- package/lib/transaction/trace/aggregator.js +4 -12
- package/lib/transaction/trace/index.js +7 -5
- package/lib/transaction/trace/segment.js +6 -11
- package/lib/transaction/tracecontext.js +10 -6
- package/lib/transaction/tracer/index.js +23 -25
- package/lib/transaction/transaction-event-aggregator.js +1 -3
- package/lib/util/async-each-limit.js +30 -0
- package/lib/util/attribute-types.js +1 -2
- package/lib/util/attributes.js +159 -0
- package/lib/util/byte-limit.js +1 -3
- package/lib/util/cat.js +16 -13
- package/lib/util/code-level-metrics.js +58 -0
- package/lib/util/codec.js +0 -2
- package/lib/util/copy.js +1 -2
- package/lib/util/deep-equal.js +11 -137
- package/lib/util/flatten.js +4 -6
- package/lib/util/hashes.js +1 -3
- package/lib/util/process-version.js +2 -3
- package/lib/util/properties.js +2 -5
- package/lib/util/stream-sink.js +2 -0
- package/lib/util/urltils.js +4 -10
- package/newrelic.js +1 -1
- package/package.json +6 -4
- package/lib/instrumentation/promise.js +0 -569
package/lib/environment.js
CHANGED
|
@@ -10,6 +10,8 @@ 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')
|
|
14
|
+
const DISPATCHER_VERSION = 'Dispatcher Version'
|
|
13
15
|
|
|
14
16
|
// As of 1.7.0 you can no longer dynamically link v8
|
|
15
17
|
// https://github.com/nodejs/io.js/commit/d726a177ed
|
|
@@ -71,7 +73,7 @@ async function listPackages(root, packages = []) {
|
|
|
71
73
|
|
|
72
74
|
try {
|
|
73
75
|
const dirs = await fsPromises.readdir(root)
|
|
74
|
-
await
|
|
76
|
+
await asyncEachLimit(dirs, forEachDir, 2)
|
|
75
77
|
_log('Done listing packages in %s', root)
|
|
76
78
|
} catch (err) {
|
|
77
79
|
logger.trace(err, 'Could not list packages in %s (probably not an error)', root)
|
|
@@ -121,7 +123,7 @@ async function listDependencies(root, children = [], visited = Object.create(nul
|
|
|
121
123
|
|
|
122
124
|
try {
|
|
123
125
|
const dirs = await fsPromises.readdir(root)
|
|
124
|
-
await
|
|
126
|
+
await asyncEachLimit(dirs, forEachEntry, 2)
|
|
125
127
|
_log('Done listing dependencies in %s', root)
|
|
126
128
|
} catch (err) {
|
|
127
129
|
logger.trace(err, 'Could not read directories in %s (probably not an error)', root)
|
|
@@ -231,7 +233,7 @@ function getGlobalPackages() {
|
|
|
231
233
|
* package appears at most once, with all the versions joined into a
|
|
232
234
|
* comma-delimited list.
|
|
233
235
|
*
|
|
234
|
-
* @param packages
|
|
236
|
+
* @param {Array} packages list of packages to process
|
|
235
237
|
* @returns {Array.<string[]>} Sorted list of [name, version] pairs.
|
|
236
238
|
*/
|
|
237
239
|
function flattenVersions(packages) {
|
|
@@ -307,15 +309,18 @@ async function getOtherPackages() {
|
|
|
307
309
|
}
|
|
308
310
|
_log('Looking for other packages in %j', paths)
|
|
309
311
|
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
+
)
|
|
317
323
|
|
|
318
|
-
const otherPackages = await Promise.all(pathPromises)
|
|
319
324
|
otherPackages.forEach((pkg) => {
|
|
320
325
|
other.packages.push.apply(other.packages, pkg.packages)
|
|
321
326
|
other.dependencies.push.apply(other.dependencies, pkg.dependencies)
|
|
@@ -418,7 +423,7 @@ function refreshSyncOnly() {
|
|
|
418
423
|
// gather persisted settings
|
|
419
424
|
const framework = getSetting('Framework')
|
|
420
425
|
const dispatcher = getSetting('Dispatcher')
|
|
421
|
-
const dispatcherVersion = getSetting(
|
|
426
|
+
const dispatcherVersion = getSetting(DISPATCHER_VERSION)
|
|
422
427
|
|
|
423
428
|
// clearing and rebuilding a global variable
|
|
424
429
|
settings = Object.create(null)
|
|
@@ -437,7 +442,7 @@ function refreshSyncOnly() {
|
|
|
437
442
|
|
|
438
443
|
if (dispatcherVersion.length) {
|
|
439
444
|
dispatcher.forEach(function addDispatchers(d) {
|
|
440
|
-
addSetting(
|
|
445
|
+
addSetting(DISPATCHER_VERSION, d)
|
|
441
446
|
})
|
|
442
447
|
}
|
|
443
448
|
|
|
@@ -469,7 +474,7 @@ async function refresh() {
|
|
|
469
474
|
* Refreshes settings and returns the settings object.
|
|
470
475
|
*
|
|
471
476
|
* @private
|
|
472
|
-
* @returns {Promise}
|
|
477
|
+
* @returns {Promise} the updated/refreshed settings
|
|
473
478
|
*/
|
|
474
479
|
async function getJSON() {
|
|
475
480
|
_log('Getting environment JSON')
|
|
@@ -503,12 +508,12 @@ module.exports = {
|
|
|
503
508
|
}
|
|
504
509
|
|
|
505
510
|
userSetDispatcher = !!userSet
|
|
506
|
-
clearSetting(
|
|
511
|
+
clearSetting(DISPATCHER_VERSION)
|
|
507
512
|
clearSetting('Dispatcher')
|
|
508
513
|
|
|
509
514
|
// TODO: Decide if this should only happen once for internals as well.
|
|
510
515
|
if (version) {
|
|
511
|
-
addSetting(
|
|
516
|
+
addSetting(DISPATCHER_VERSION, version)
|
|
512
517
|
}
|
|
513
518
|
|
|
514
519
|
addSetting('Dispatcher', dispatcher)
|
|
@@ -520,7 +525,7 @@ module.exports = {
|
|
|
520
525
|
// This method is only used for tests.
|
|
521
526
|
userSetDispatcher = false
|
|
522
527
|
clearSetting('Dispatcher')
|
|
523
|
-
clearSetting(
|
|
528
|
+
clearSetting(DISPATCHER_VERSION)
|
|
524
529
|
},
|
|
525
530
|
listPackages,
|
|
526
531
|
getJSON,
|
|
@@ -10,6 +10,7 @@ const errorsModule = require('./index')
|
|
|
10
10
|
const logger = require('../logger').child({ component: 'error_tracer' })
|
|
11
11
|
const urltils = require('../util/urltils')
|
|
12
12
|
const Exception = require('../errors').Exception
|
|
13
|
+
const Transaction = require('../transaction')
|
|
13
14
|
const errorHelper = require('./helper')
|
|
14
15
|
const createError = errorsModule.createError
|
|
15
16
|
const createEvent = errorsModule.createEvent
|
|
@@ -73,6 +74,7 @@ class ErrorCollector {
|
|
|
73
74
|
*
|
|
74
75
|
* @param {?Transaction} transaction -
|
|
75
76
|
* @param {Error} exception - The error to be checked.
|
|
77
|
+
* @returns {boolean} whether or not the exception has already been tracked
|
|
76
78
|
*/
|
|
77
79
|
_haveSeen(transaction, exception) {
|
|
78
80
|
const txId = transaction ? transaction.id : 'Unknown'
|
|
@@ -107,13 +109,78 @@ class ErrorCollector {
|
|
|
107
109
|
return false
|
|
108
110
|
}
|
|
109
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
|
+
|
|
110
177
|
/**
|
|
111
178
|
* Every finished transaction goes through this handler, so do as little as
|
|
112
179
|
* possible.
|
|
113
180
|
*
|
|
114
|
-
*
|
|
181
|
+
* TODO: Prob shouldn't do any work if errors fully disabled.
|
|
115
182
|
*
|
|
116
|
-
* @
|
|
183
|
+
* @param {Transaction} transaction the completed transaction
|
|
117
184
|
*/
|
|
118
185
|
onTransactionFinished(transaction) {
|
|
119
186
|
if (!transaction) {
|
|
@@ -123,48 +190,30 @@ class ErrorCollector {
|
|
|
123
190
|
return
|
|
124
191
|
}
|
|
125
192
|
|
|
126
|
-
// TODO: Prob shouldn't do any work if errors fully disabled.
|
|
127
|
-
|
|
128
193
|
// collect user errors even if status code is ignored
|
|
129
194
|
let collectedErrors = 0
|
|
130
195
|
let expectedErrors = 0
|
|
131
196
|
|
|
132
197
|
// errors from noticeError are currently exempt from
|
|
133
198
|
// ignore and exclude rules
|
|
134
|
-
|
|
135
|
-
for (let i = 0; i < transaction.userErrors.length; i++) {
|
|
136
|
-
const exception = transaction.userErrors[i]
|
|
137
|
-
if (this.collect(transaction, exception)) {
|
|
138
|
-
++collectedErrors
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
199
|
+
collectedErrors = this._processUserErrors(transaction, collectedErrors)
|
|
142
200
|
|
|
143
201
|
const isErroredTransaction = urltils.isError(this.config, transaction.statusCode)
|
|
144
202
|
const isIgnoredErrorStatusCode = urltils.isIgnoredError(this.config, transaction.statusCode)
|
|
145
203
|
|
|
146
|
-
const isExpectedErrorStatusCode = urltils.isExpectedError(this.config, transaction.statusCode)
|
|
147
|
-
|
|
148
204
|
// collect other exceptions only if status code is not ignored
|
|
149
205
|
if (transaction.exceptions.length && !isIgnoredErrorStatusCode) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
} else if (isErroredTransaction && this.collect(transaction)) {
|
|
164
|
-
++collectedErrors
|
|
165
|
-
if (isExpectedErrorStatusCode) {
|
|
166
|
-
++expectedErrors
|
|
167
|
-
}
|
|
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
|
+
)
|
|
168
217
|
}
|
|
169
218
|
|
|
170
219
|
const unexpectedErrors = collectedErrors - expectedErrors
|
|
@@ -225,7 +274,8 @@ class ErrorCollector {
|
|
|
225
274
|
* NOTE: this interface is unofficial and may change in future.
|
|
226
275
|
*
|
|
227
276
|
* @param {?Transaction} transaction Transaction associated with the error.
|
|
228
|
-
* @param {
|
|
277
|
+
* @param {*} error The error passed into `API#noticeError()`
|
|
278
|
+
* @param {object} customAttributes custom attributes to add to the error
|
|
229
279
|
*/
|
|
230
280
|
addUserError(transaction, error, customAttributes) {
|
|
231
281
|
if (!error) {
|
|
@@ -260,13 +310,38 @@ class ErrorCollector {
|
|
|
260
310
|
*
|
|
261
311
|
* @param {?Transaction} transaction Transaction associated with the error.
|
|
262
312
|
* @param {?Exception} exception The Exception object to be traced.
|
|
263
|
-
* @
|
|
313
|
+
* @returns {boolean} True if the error was collected.
|
|
264
314
|
*/
|
|
265
|
-
collect(transaction, exception) {
|
|
266
|
-
if (!exception) {
|
|
267
|
-
|
|
315
|
+
collect(transaction, exception = new Exception({})) {
|
|
316
|
+
if (!this._isValidException(exception, transaction)) {
|
|
317
|
+
return false
|
|
268
318
|
}
|
|
269
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) {
|
|
270
345
|
if (exception.error) {
|
|
271
346
|
if (this._haveSeen(transaction, exception.error)) {
|
|
272
347
|
return false
|
|
@@ -285,10 +360,20 @@ class ErrorCollector {
|
|
|
285
360
|
|
|
286
361
|
if (exception.error) {
|
|
287
362
|
logger.trace(exception.error, 'Got exception to trace:')
|
|
363
|
+
} else {
|
|
364
|
+
logger.trace(transaction, 'Got transaction error to trace:')
|
|
288
365
|
}
|
|
289
366
|
|
|
290
|
-
|
|
367
|
+
return true
|
|
368
|
+
}
|
|
291
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) {
|
|
292
377
|
const isExpectedError = true === errorTrace[4].intrinsics['error.expected']
|
|
293
378
|
|
|
294
379
|
if (isExpectedError) {
|
|
@@ -304,19 +389,6 @@ class ErrorCollector {
|
|
|
304
389
|
}
|
|
305
390
|
}
|
|
306
391
|
}
|
|
307
|
-
|
|
308
|
-
// defaults true in config/index. can be modified server-side
|
|
309
|
-
if (this.config.collect_errors) {
|
|
310
|
-
this.traceAggregator.add(errorTrace)
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (this.config.error_collector.capture_events === true) {
|
|
314
|
-
const priority = (transaction && transaction.priority) || Math.random()
|
|
315
|
-
const event = createEvent(transaction, errorTrace, exception.timestamp, this.config)
|
|
316
|
-
this.eventAggregator.add(event, priority)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return true
|
|
320
392
|
}
|
|
321
393
|
|
|
322
394
|
// TODO: ideally, this becomes unnecessary
|
package/lib/errors/helper.js
CHANGED
|
@@ -21,20 +21,17 @@ module.exports = {
|
|
|
21
21
|
if (!config.error_collector.expected_messages[type]) {
|
|
22
22
|
return false
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return false
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
config.error_collector.expected_messages[type].length > 0 &&
|
|
27
|
+
config.error_collector.expected_messages[type].indexOf(message) !== -1
|
|
28
|
+
)
|
|
30
29
|
},
|
|
31
30
|
isExpectedErrorClass: function isExpectedErrorClass(config, className) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
return false
|
|
31
|
+
return (
|
|
32
|
+
config.error_collector.expected_classes.length > 0 &&
|
|
33
|
+
config.error_collector.expected_classes.indexOf(className) !== -1
|
|
34
|
+
)
|
|
38
35
|
},
|
|
39
36
|
isExpectedException: function isExpectedException(transaction, exception, config, urltils) {
|
|
40
37
|
const { type, message } = this.extractErrorInformation(transaction, exception, config, urltils)
|
|
@@ -123,11 +120,6 @@ module.exports = {
|
|
|
123
120
|
return false
|
|
124
121
|
}
|
|
125
122
|
|
|
126
|
-
|
|
127
|
-
if (-1 !== configIgnoreMessages.indexOf(errorInfo.message)) {
|
|
128
|
-
return true
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return false
|
|
123
|
+
return configIgnoreMessages.length > 0 && configIgnoreMessages.indexOf(errorInfo.message) !== -1
|
|
132
124
|
}
|
|
133
125
|
}
|
package/lib/errors/index.js
CHANGED
|
@@ -6,10 +6,17 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const DESTINATIONS = require('../config/attribute-filter').DESTINATIONS
|
|
9
|
-
const NAMES = require('../metrics/names')
|
|
10
9
|
const props = require('../util/properties')
|
|
11
10
|
const urltils = require('../util/urltils')
|
|
12
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')
|
|
19
|
+
const ERROR_EXPECTED_PATH = 'error.expected'
|
|
13
20
|
|
|
14
21
|
class Exception {
|
|
15
22
|
constructor({ error, timestamp, customAttributes, agentAttributes }) {
|
|
@@ -45,9 +52,19 @@ class Exception {
|
|
|
45
52
|
* handler, which traps actual instances of Error, try to set sensible
|
|
46
53
|
* defaults for everything.
|
|
47
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
|
+
*
|
|
48
60
|
* @param {Transaction} transaction The agent transaction, coming from the instrumentatation
|
|
49
61
|
* @param {Exception} exception An custom Exception object with the error and other information
|
|
50
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
|
|
51
68
|
*/
|
|
52
69
|
function createError(transaction, exception, config) {
|
|
53
70
|
const error = exception.error
|
|
@@ -67,55 +84,74 @@ function createError(transaction, exception, config) {
|
|
|
67
84
|
if (transaction) {
|
|
68
85
|
// Copy all of the parameters off of the transaction.
|
|
69
86
|
params.intrinsics = transaction.getIntrinsicAttributes()
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
const transactionAgentAttributes =
|
|
88
|
+
transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT) || {}
|
|
72
89
|
|
|
73
90
|
// Merge the agent attributes specific to this error event with the transaction attributes
|
|
74
|
-
|
|
75
|
-
params.agentAttributes = agentAttributes
|
|
91
|
+
params.agentAttributes = Object.assign(exception.agentAttributes, transactionAgentAttributes)
|
|
76
92
|
|
|
77
93
|
// There should be no attributes to copy in HSM, but check status anyway
|
|
78
94
|
if (!config.high_security) {
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
urltils.overwriteParameters(
|
|
96
|
+
transaction.trace.custom.get(DESTINATIONS.ERROR_EVENT),
|
|
97
|
+
params.userAttributes
|
|
98
|
+
)
|
|
81
99
|
}
|
|
82
100
|
}
|
|
83
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) {
|
|
84
118
|
const customAttributes = exception.customAttributes
|
|
85
119
|
if (!config.high_security && config.api.custom_attributes_enabled && customAttributes) {
|
|
86
120
|
for (const key in customAttributes) {
|
|
87
121
|
if (props.hasOwn(customAttributes, key)) {
|
|
88
122
|
const dest = config.attributeFilter.filterTransaction(DESTINATIONS.ERROR_EVENT, key)
|
|
89
123
|
if (dest & DESTINATIONS.ERROR_EVENT) {
|
|
90
|
-
|
|
124
|
+
userAttributes[key] = customAttributes[key]
|
|
91
125
|
}
|
|
92
126
|
}
|
|
93
127
|
}
|
|
94
128
|
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function maybeAddStackTrace(exception, config) {
|
|
132
|
+
const stack = exception.error?.stack
|
|
133
|
+
let parsedStack
|
|
95
134
|
|
|
96
|
-
const stack = exception.error && exception.error.stack
|
|
97
135
|
if (stack) {
|
|
98
|
-
|
|
136
|
+
parsedStack = ('' + stack).split(/[\n\r]/g)
|
|
137
|
+
|
|
99
138
|
if (config.high_security || config.strip_exception_messages.enabled) {
|
|
100
|
-
|
|
139
|
+
parsedStack[0] = exception.error.name + ': <redacted>'
|
|
101
140
|
}
|
|
102
141
|
}
|
|
103
142
|
|
|
104
|
-
|
|
105
|
-
if (errorHelper.isExpected(type, message, transaction, config, urltils)) {
|
|
106
|
-
params.intrinsics['error.expected'] = true
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const res = [0, name, message, type, params]
|
|
110
|
-
if (transaction) {
|
|
111
|
-
res.transaction = transaction.id
|
|
112
|
-
}
|
|
113
|
-
return res
|
|
143
|
+
return parsedStack
|
|
114
144
|
}
|
|
115
145
|
|
|
116
146
|
/**
|
|
117
147
|
* Creates a structure for error event that is sent to the collector.
|
|
118
148
|
* The error parameter is an output of the createError() function for a given exception.
|
|
149
|
+
*
|
|
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
|
|
119
155
|
*/
|
|
120
156
|
function createEvent(transaction, error, timestamp, config) {
|
|
121
157
|
const message = error[2]
|
|
@@ -126,7 +162,7 @@ function createEvent(transaction, error, timestamp, config) {
|
|
|
126
162
|
transaction,
|
|
127
163
|
errorClass,
|
|
128
164
|
message,
|
|
129
|
-
errorParams.intrinsics[
|
|
165
|
+
errorParams.intrinsics[ERROR_EXPECTED_PATH],
|
|
130
166
|
timestamp,
|
|
131
167
|
config
|
|
132
168
|
)
|
|
@@ -136,9 +172,7 @@ function createEvent(transaction, error, timestamp, config) {
|
|
|
136
172
|
const userAttributes = Object.assign(Object.create(null), errorParams.userAttributes)
|
|
137
173
|
const agentAttributes = Object.assign(Object.create(null), errorParams.agentAttributes)
|
|
138
174
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return errorEvent
|
|
175
|
+
return [intrinsicAttributes, userAttributes, agentAttributes]
|
|
142
176
|
}
|
|
143
177
|
|
|
144
178
|
// eslint-disable-next-line max-params
|
|
@@ -160,28 +194,10 @@ function _getErrorEventIntrinsicAttrs(transaction, errorClass, message, expected
|
|
|
160
194
|
attributes.transactionName = transaction.getFullName()
|
|
161
195
|
attributes.duration = transaction.timer.getDurationInMillis() / 1000
|
|
162
196
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
metric = transaction.metrics.getMetric(NAMES.EXTERNAL.ALL)
|
|
169
|
-
if (metric) {
|
|
170
|
-
attributes.externalDuration = metric.total
|
|
171
|
-
attributes.externalCallCount = metric.callCount
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
metric = transaction.metrics.getMetric(NAMES.DB.ALL)
|
|
175
|
-
if (metric) {
|
|
176
|
-
attributes.databaseDuration = metric.total
|
|
177
|
-
attributes.databaseCallCount = metric.callCount
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (transaction.syntheticsData) {
|
|
181
|
-
attributes['nr.syntheticsResourceId'] = transaction.syntheticsData.resourceId
|
|
182
|
-
attributes['nr.syntheticsJobId'] = transaction.syntheticsData.jobId
|
|
183
|
-
attributes['nr.syntheticsMonitorId'] = transaction.syntheticsData.monitorId
|
|
184
|
-
}
|
|
197
|
+
maybeAddQueueAttributes(transaction, attributes)
|
|
198
|
+
maybeAddExternalAttributes(transaction, attributes)
|
|
199
|
+
maybeAddDatabaseAttributes(transaction, attributes)
|
|
200
|
+
maybeAddSyntheticAttributes(transaction, attributes)
|
|
185
201
|
|
|
186
202
|
if (transaction.agent.config.distributed_tracing.enabled) {
|
|
187
203
|
transaction.addDistributedTraceIntrinsics(attributes)
|
package/lib/grpc/connection.js
CHANGED
|
@@ -41,9 +41,9 @@ class GrpcConnection extends EventEmitter {
|
|
|
41
41
|
* Standard property setting/initialization, and sets an initial
|
|
42
42
|
* connection state of disconnected
|
|
43
43
|
*
|
|
44
|
-
* @param {
|
|
44
|
+
* @param {object} traceObserverConfig config item config.infinite_tracing.trace_observer
|
|
45
45
|
* @param {MetricAggregator} metrics metric aggregator, for supportability metrics
|
|
46
|
-
* @param {
|
|
46
|
+
* @param {number} [reconnectDelayMs=15000] number of milliseconds to wait before reconnecting
|
|
47
47
|
* for error states that require a reconnect delay.
|
|
48
48
|
*/
|
|
49
49
|
constructor(traceObserverConfig, metrics, reconnectDelayMs) {
|
|
@@ -97,6 +97,7 @@ class GrpcConnection extends EventEmitter {
|
|
|
97
97
|
*
|
|
98
98
|
* @param {int} state The connection state (See connectionStates above)
|
|
99
99
|
* @param {ClientDuplexStreamImpl} state The GRPC stream, when defined
|
|
100
|
+
* @param stream
|
|
100
101
|
*/
|
|
101
102
|
_setState(state, stream = null) {
|
|
102
103
|
this._state = state
|
|
@@ -146,6 +147,8 @@ class GrpcConnection extends EventEmitter {
|
|
|
146
147
|
*
|
|
147
148
|
* @param {string} licenseKey
|
|
148
149
|
* @param {string} runId
|
|
150
|
+
* @param requestHeadersMap
|
|
151
|
+
* @param env
|
|
149
152
|
*/
|
|
150
153
|
_getMetadata(licenseKey, runId, requestHeadersMap, env) {
|
|
151
154
|
const metadata = new grpc.Metadata()
|
|
@@ -170,6 +173,7 @@ class GrpcConnection extends EventEmitter {
|
|
|
170
173
|
* when appropriate env vars are set
|
|
171
174
|
*
|
|
172
175
|
* @param {Metadata} metadata
|
|
176
|
+
* @param env
|
|
173
177
|
*/
|
|
174
178
|
_setTestMetadata(metadata, env) {
|
|
175
179
|
for (const [key, envVar] of Object.entries(GRPC_TEST_META)) {
|
|
@@ -183,6 +187,7 @@ class GrpcConnection extends EventEmitter {
|
|
|
183
187
|
|
|
184
188
|
/**
|
|
185
189
|
* Disconnects from gRPC endpoint and schedules establishing a new connection.
|
|
190
|
+
*
|
|
186
191
|
* @param {number} reconnectDelayMs number of milliseconds to wait before reconnecting.
|
|
187
192
|
*/
|
|
188
193
|
_reconnect(reconnectDelayMs = 0) {
|
|
@@ -291,6 +296,8 @@ class GrpcConnection extends EventEmitter {
|
|
|
291
296
|
|
|
292
297
|
/**
|
|
293
298
|
* Creates the GRPC credentials needed
|
|
299
|
+
*
|
|
300
|
+
* @param grpcApi
|
|
294
301
|
*/
|
|
295
302
|
_generateCredentials(grpcApi) {
|
|
296
303
|
let certBuffer = null
|
|
@@ -349,6 +356,8 @@ class GrpcConnection extends EventEmitter {
|
|
|
349
356
|
* ChannelImplementation and related objects will stay in memory even after
|
|
350
357
|
* the stream is closed and we do not have a handle to the client. Currently
|
|
351
358
|
* impacting grpc-js@1.2.11 and several earlier versions.
|
|
359
|
+
*
|
|
360
|
+
* @param endpoint
|
|
352
361
|
*/
|
|
353
362
|
_createClient(endpoint) {
|
|
354
363
|
logger.trace('Creating gRPC client for: ', endpoint)
|
|
@@ -360,9 +369,7 @@ class GrpcConnection extends EventEmitter {
|
|
|
360
369
|
const traceApi = protoDescriptor.com.newrelic.trace.v1
|
|
361
370
|
|
|
362
371
|
const credentials = this._generateCredentials(grpc)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
return client
|
|
372
|
+
return new traceApi.IngestService(endpoint, credentials)
|
|
366
373
|
}
|
|
367
374
|
}
|
|
368
375
|
|
package/lib/header-attributes.js
CHANGED
|
@@ -139,7 +139,8 @@ function _collectHeaders(headers, nameMap, prefix, transaction) {
|
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* Adds request headers as request.headers.* attributes to the given transaction.
|
|
142
|
-
*
|
|
142
|
+
*
|
|
143
|
+
* @param {Object<string, string>} headers - Request headers to add attributes for.
|
|
143
144
|
* @param {Transaction} transaction - Transaction to add header attributes to.
|
|
144
145
|
*/
|
|
145
146
|
function collectRequestHeaders(headers, transaction) {
|
|
@@ -148,7 +149,8 @@ function collectRequestHeaders(headers, transaction) {
|
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
151
|
* Adds response headers as response.headers.* attributes to the given transaction.
|
|
151
|
-
*
|
|
152
|
+
*
|
|
153
|
+
* @param {Object<string, string>} headers - Response headers to add attributes for.
|
|
152
154
|
* @param {Transaction} transaction - Transaction to add header attributes to.
|
|
153
155
|
*/
|
|
154
156
|
function collectResponseHeaders(headers, transaction) {
|