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.
Files changed (100) hide show
  1. package/NEWS.md +40 -16
  2. package/THIRD_PARTY_NOTICES.md +203 -2
  3. package/api.js +155 -101
  4. package/bin/test-naming-rules.js +1 -1
  5. package/esm-loader.mjs +1 -3
  6. package/lib/adaptive-sampler.js +1 -2
  7. package/lib/agent.js +29 -80
  8. package/lib/aggregators/event-aggregator.js +2 -2
  9. package/lib/attributes.js +2 -4
  10. package/lib/collector/api.js +252 -211
  11. package/lib/collector/facts.js +1 -3
  12. package/lib/collector/parse-response.js +3 -1
  13. package/lib/collector/remote-method.js +8 -9
  14. package/lib/config/attribute-filter.js +42 -34
  15. package/lib/config/default.js +60 -17
  16. package/lib/config/harvest-config-validator.js +3 -5
  17. package/lib/config/index.js +263 -199
  18. package/lib/environment.js +22 -17
  19. package/lib/errors/error-collector.js +123 -51
  20. package/lib/errors/helper.js +10 -18
  21. package/lib/errors/index.js +63 -47
  22. package/lib/grpc/connection.js +12 -5
  23. package/lib/header-attributes.js +4 -2
  24. package/lib/header-processing.js +2 -2
  25. package/lib/instrumentation/@hapi/hapi.js +56 -48
  26. package/lib/instrumentation/@node-redis/client.js +50 -39
  27. package/lib/instrumentation/amqplib.js +116 -149
  28. package/lib/instrumentation/core/{async_hooks.js → async-hooks.js} +26 -9
  29. package/lib/instrumentation/core/globals.js +12 -9
  30. package/lib/instrumentation/core/http-outbound.js +200 -83
  31. package/lib/instrumentation/core/http.js +10 -8
  32. package/lib/instrumentation/core/timers.js +106 -57
  33. package/lib/instrumentation/fastify.js +1 -3
  34. package/lib/instrumentation/grpc-js/grpc.js +14 -3
  35. package/lib/instrumentation/memcached.js +5 -0
  36. package/lib/instrumentation/mongodb/common.js +88 -83
  37. package/lib/instrumentation/mongodb/v2-mongo.js +6 -3
  38. package/lib/instrumentation/mongodb.js +1 -1
  39. package/lib/instrumentation/nr-winston-transport.js +3 -4
  40. package/lib/instrumentation/redis.js +112 -88
  41. package/lib/instrumentation/undici.js +204 -190
  42. package/lib/instrumentation/{when.js → when/constants.js} +13 -10
  43. package/lib/instrumentation/when/contextualizer.js +168 -0
  44. package/lib/instrumentation/when/index.js +354 -0
  45. package/lib/instrumentation/when/nr-hooks.js +15 -0
  46. package/lib/instrumentations.js +1 -1
  47. package/lib/metrics/mapper.js +2 -3
  48. package/lib/metrics/metric-aggregator.js +1 -3
  49. package/lib/metrics/names.js +19 -15
  50. package/lib/metrics/normalizer/rule.js +6 -7
  51. package/lib/metrics/normalizer/tx_segment.js +3 -2
  52. package/lib/metrics/normalizer.js +5 -3
  53. package/lib/parse-proc-cpuinfo.js +11 -6
  54. package/lib/serverless/api-gateway.js +3 -4
  55. package/lib/serverless/aws-lambda.js +3 -3
  56. package/lib/shim/conglomerate-shim.js +2 -3
  57. package/lib/shim/datastore-shim.js +22 -104
  58. package/lib/shim/index.js +7 -0
  59. package/lib/shim/message-shim.js +2 -0
  60. package/lib/shim/promise-shim.js +48 -42
  61. package/lib/shim/shim.js +9 -7
  62. package/lib/shim/webframework-shim.js +21 -0
  63. package/lib/spans/map-to-streaming-type.js +0 -2
  64. package/lib/spans/span-context.js +2 -0
  65. package/lib/spans/span-event-aggregator.js +2 -2
  66. package/lib/spans/span-event.js +2 -2
  67. package/lib/spans/span-streamer.js +4 -0
  68. package/lib/spans/streaming-span-event-aggregator.js +2 -4
  69. package/lib/spans/streaming-span-event.js +3 -2
  70. package/lib/symbols.js +1 -0
  71. package/lib/system-info.js +241 -166
  72. package/lib/timer.js +8 -6
  73. package/lib/transaction/dt-payload.js +4 -3
  74. package/lib/transaction/handle.js +12 -2
  75. package/lib/transaction/index.js +19 -18
  76. package/lib/transaction/name-state.js +17 -2
  77. package/lib/transaction/trace/aggregator.js +4 -12
  78. package/lib/transaction/trace/index.js +7 -5
  79. package/lib/transaction/trace/segment.js +6 -11
  80. package/lib/transaction/tracecontext.js +10 -6
  81. package/lib/transaction/tracer/index.js +23 -25
  82. package/lib/transaction/transaction-event-aggregator.js +1 -3
  83. package/lib/util/async-each-limit.js +30 -0
  84. package/lib/util/attribute-types.js +1 -2
  85. package/lib/util/attributes.js +159 -0
  86. package/lib/util/byte-limit.js +1 -3
  87. package/lib/util/cat.js +16 -13
  88. package/lib/util/code-level-metrics.js +58 -0
  89. package/lib/util/codec.js +0 -2
  90. package/lib/util/copy.js +1 -2
  91. package/lib/util/deep-equal.js +11 -137
  92. package/lib/util/flatten.js +4 -6
  93. package/lib/util/hashes.js +1 -3
  94. package/lib/util/process-version.js +2 -3
  95. package/lib/util/properties.js +2 -5
  96. package/lib/util/stream-sink.js +2 -0
  97. package/lib/util/urltils.js +4 -10
  98. package/newrelic.js +1 -1
  99. package/package.json +6 -4
  100. package/lib/instrumentation/promise.js +0 -569
@@ -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 Promise.all(dirs.map(forEachDir))
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 Promise.all(dirs.map(forEachEntry))
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 pathPromises = paths.map((nodePath) => {
311
- if (nodePath[0] !== '/') {
312
- nodePath = path.resolve(process.cwd(), nodePath)
313
- }
314
- _log('Getting other packages from %s', nodePath)
315
- return getPackages(nodePath)
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('Dispatcher Version')
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('Dispatcher Version', d)
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('Dispatcher Version')
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('Dispatcher Version', version)
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('Dispatcher Version')
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
- * @param {Transaction} transaction
181
+ * TODO: Prob shouldn't do any work if errors fully disabled.
115
182
  *
116
- * @return {number} The number of unexpected errors
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
- if (transaction.userErrors.length) {
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
- for (let i = 0; i < transaction.exceptions.length; i++) {
151
- const exception = transaction.exceptions[i]
152
- if (this.collect(transaction, exception)) {
153
- ++collectedErrors
154
- // if we could collect it, then check if expected
155
- if (
156
- isExpectedErrorStatusCode ||
157
- errorHelper.isExpectedException(transaction, exception.error, this.config, urltils)
158
- ) {
159
- ++expectedErrors
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 {Exception} exception The Exception to be traced.
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
- * @return {bool} True if the error was collected.
313
+ * @returns {boolean} True if the error was collected.
264
314
  */
265
- collect(transaction, exception) {
266
- if (!exception) {
267
- exception = new Exception({})
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
- const errorTrace = createError(transaction, exception, this.config)
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
@@ -21,20 +21,17 @@ module.exports = {
21
21
  if (!config.error_collector.expected_messages[type]) {
22
22
  return false
23
23
  }
24
- if (config.error_collector.expected_messages[type].length > 0) {
25
- if (-1 !== config.error_collector.expected_messages[type].indexOf(message)) {
26
- return true
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
- if (config.error_collector.expected_classes.length > 0) {
33
- if (-1 !== config.error_collector.expected_classes.indexOf(className)) {
34
- return true
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
- if (configIgnoreMessages.length > 0) {
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
  }
@@ -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
- let transactionAgentAttributes = transaction.trace.attributes.get(DESTINATIONS.ERROR_EVENT)
71
- transactionAgentAttributes = transactionAgentAttributes || {}
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
- const agentAttributes = Object.assign(exception.agentAttributes, transactionAgentAttributes)
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
- const custom = transaction.trace.custom.get(DESTINATIONS.ERROR_EVENT)
80
- urltils.overwriteParameters(custom, params.userAttributes)
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
- params.userAttributes[key] = customAttributes[key]
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
- params.stack_trace = ('' + stack).split(/[\n\r]/g)
136
+ parsedStack = ('' + stack).split(/[\n\r]/g)
137
+
99
138
  if (config.high_security || config.strip_exception_messages.enabled) {
100
- params.stack_trace[0] = exception.error.name + ': <redacted>'
139
+ parsedStack[0] = exception.error.name + ': <redacted>'
101
140
  }
102
141
  }
103
142
 
104
- params.intrinsics['error.expected'] = false
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['error.expected'],
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
- const errorEvent = [intrinsicAttributes, userAttributes, agentAttributes]
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
- let metric = transaction.metrics.getMetric(NAMES.QUEUETIME)
164
- if (metric) {
165
- attributes.queueDuration = metric.total
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)
@@ -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 {Object} traceObserverConfig config item config.infinite_tracing.trace_observer
44
+ * @param {object} traceObserverConfig config item config.infinite_tracing.trace_observer
45
45
  * @param {MetricAggregator} metrics metric aggregator, for supportability metrics
46
- * @param {Number} [reconnectDelayMs=15000] number of milliseconds to wait before reconnecting
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
- const client = new traceApi.IngestService(endpoint, credentials)
364
-
365
- return client
372
+ return new traceApi.IngestService(endpoint, credentials)
366
373
  }
367
374
  }
368
375
 
@@ -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
- * @param {Object.<string, string>} headers - Request headers to add attributes for.
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
- * @param {Object.<string, string>} headers - Response headers to add attributes for.
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) {