newrelic 9.7.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 (36) hide show
  1. package/NEWS.md +40 -20
  2. package/THIRD_PARTY_NOTICES.md +203 -2
  3. package/api.js +155 -103
  4. package/lib/agent.js +29 -82
  5. package/lib/collector/api.js +234 -201
  6. package/lib/config/attribute-filter.js +35 -25
  7. package/lib/config/default.js +45 -19
  8. package/lib/config/index.js +260 -199
  9. package/lib/environment.js +16 -12
  10. package/lib/errors/error-collector.js +124 -55
  11. package/lib/errors/index.js +59 -49
  12. package/lib/instrumentation/@hapi/hapi.js +56 -50
  13. package/lib/instrumentation/@node-redis/client.js +50 -41
  14. package/lib/instrumentation/amqplib.js +116 -151
  15. package/lib/instrumentation/core/{async_hooks.js → async-hooks.js} +26 -11
  16. package/lib/instrumentation/core/globals.js +1 -1
  17. package/lib/instrumentation/core/http-outbound.js +193 -78
  18. package/lib/instrumentation/core/timers.js +106 -59
  19. package/lib/instrumentation/mongodb/common.js +87 -85
  20. package/lib/instrumentation/redis.js +112 -90
  21. package/lib/instrumentation/undici.js +204 -192
  22. package/lib/instrumentation/{when.js → when/constants.js} +13 -10
  23. package/lib/instrumentation/when/contextualizer.js +168 -0
  24. package/lib/instrumentation/when/index.js +354 -0
  25. package/lib/instrumentation/when/nr-hooks.js +15 -0
  26. package/lib/instrumentations.js +1 -1
  27. package/lib/shim/shim.js +2 -0
  28. package/lib/shim/webframework-shim.js +19 -0
  29. package/lib/symbols.js +1 -0
  30. package/lib/system-info.js +240 -163
  31. package/lib/util/async-each-limit.js +30 -0
  32. package/lib/util/attributes.js +159 -0
  33. package/lib/util/code-level-metrics.js +58 -0
  34. package/lib/util/deep-equal.js +11 -144
  35. package/package.json +5 -4
  36. package/lib/instrumentation/promise.js +0 -572
package/api.js CHANGED
@@ -5,8 +5,6 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- /* eslint sonarjs/cognitive-complexity: ["error", 21] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
9
-
10
8
  const util = require('util')
11
9
  const logger = require('./lib/logger').child({ component: 'api' })
12
10
  const recordWeb = require('./lib/metrics/recorders/http')
@@ -58,7 +56,7 @@ const CUSTOM_EVENT_TYPE_REGEX = /^[a-zA-Z0-9:_ ]+$/
58
56
  * You do not need to directly instantiate this class, as an instance of this is
59
57
  * the return from `require('newrelic')`.
60
58
  *
61
- * @param agent
59
+ * @param {object} agent Instantiation of lib/agent.js
62
60
  * @class
63
61
  */
64
62
  function API(agent) {
@@ -78,6 +76,7 @@ function API(agent) {
78
76
  *
79
77
  * @param {string} name The name you want to give the web request in the New
80
78
  * Relic UI. Will be prefixed with 'Custom/' when sent.
79
+ * @returns {void}
81
80
  */
82
81
  API.prototype.setTransactionName = function setTransactionName(name) {
83
82
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -143,8 +142,8 @@ API.prototype.getTransaction = function getTransaction() {
143
142
  * utilization.full_hostname. If utilization.full_hostname is null or empty,
144
143
  * this will be the hostname specified in the connect request as host.
145
144
  *
146
- * @param omitSupportability
147
- * @returns {LinkingMetadata} The linking object with the data above
145
+ * @param {boolean} omitSupportability Whether or not to log the supportability metric, true means skip
146
+ * @returns {object} The LinkingMetadata object with the data above
148
147
  */
149
148
  API.prototype.getLinkingMetadata = function getLinkingMetadata(omitSupportability) {
150
149
  if (omitSupportability !== true) {
@@ -207,6 +206,7 @@ API.prototype.setDispatcher = function setDispatcher(name, version) {
207
206
  * sent.
208
207
  * @param {string} action The action being invoked on the controller. Defaults
209
208
  * to the HTTP method used for the request.
209
+ * @returns {void}
210
210
  */
211
211
  API.prototype.setControllerName = function setControllerName(name, action) {
212
212
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -241,6 +241,7 @@ API.prototype.setControllerName = function setControllerName(name, action) {
241
241
  *
242
242
  * @param {string} key The key you want displayed in the RPM UI.
243
243
  * @param {string} value The value you want displayed. Must be serializable.
244
+ * @returns {false|undefined} Retruns false when disabled/errored, otherwise undefined
244
245
  */
245
246
  API.prototype.addCustomAttribute = function addCustomAttribute(key, value) {
246
247
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -296,7 +297,7 @@ API.prototype.addCustomAttribute = function addCustomAttribute(key, value) {
296
297
  *
297
298
  * newrelic.addCustomAttributes({test: 'value', test2: 'value2'});
298
299
  *
299
- * @param {object} [atts]
300
+ * @param {object} [atts] Attribute object
300
301
  * @param {string} [atts.KEY] The name you want displayed in the RPM UI.
301
302
  * @param {string} [atts.KEY.VALUE] The value you want displayed. Must be serializable.
302
303
  */
@@ -324,7 +325,7 @@ API.prototype.addCustomAttributes = function addCustomAttributes(atts) {
324
325
  *
325
326
  * newrelic.addCustomSpanAttribute({test: 'value', test2: 'value2'})
326
327
  *
327
- * @param {object} [atts]
328
+ * @param {object} [atts] Attribute object
328
329
  * @param {string} [atts.KEY] The name you want displayed in the RPM UI.API.
329
330
  * @param {string} [atts.KEY.VALUE] The value you want displayed. Must be serializable.
330
331
  */
@@ -349,6 +350,7 @@ API.prototype.addCustomSpanAttributes = function addCustomSpanAttributes(atts) {
349
350
  *
350
351
  * @param {string} key The key you want displayed in the RPM UI.
351
352
  * @param {string} value The value you want displayed. Must be serializable.
353
+ * @returns {false|undefined} Retruns false when disabled/errored, otherwise undefined
352
354
  */
353
355
  API.prototype.addCustomSpanAttribute = function addCustomSpanAttribute(key, value) {
354
356
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -388,6 +390,17 @@ API.prototype.addCustomSpanAttribute = function addCustomSpanAttribute(key, valu
388
390
  * `Error` or one of its subtypes, but the API will handle strings and objects
389
391
  * that have an attached `.message` or `.stack` property.
390
392
  *
393
+ * An example of using this function is
394
+ *
395
+ * try {
396
+ * performSomeTask();
397
+ * } catch (err) {
398
+ * newrelic.noticeError(
399
+ * err,
400
+ * {extraInformation: "error already handled in the application"}
401
+ * );
402
+ * }
403
+ *
391
404
  * NOTE: Errors that are recorded using this method do _not_ obey the
392
405
  * `ignore_status_codes` configuration.
393
406
  *
@@ -395,6 +408,7 @@ API.prototype.addCustomSpanAttribute = function addCustomSpanAttribute(key, valu
395
408
  * The error to be traced.
396
409
  * @param {object} [customAttributes]
397
410
  * Optional. Any custom attributes to be displayed in the New Relic UI.
411
+ * @returns {false|undefined} Retruns false when disabled/errored, otherwise undefined
398
412
  */
399
413
  API.prototype.noticeError = function noticeError(error, customAttributes) {
400
414
  const metric = this.agent.metrics.getOrCreateMetric(NAMES.SUPPORTABILITY.API + '/noticeError')
@@ -536,6 +550,7 @@ API.prototype.recordLogEvent = function recordLogEvent(logEvent = {}) {
536
550
  *
537
551
  * @param {RegExp} pattern The pattern to rename (with capture groups).
538
552
  * @param {string} name The name to use for the transaction.
553
+ * @returns {void}
539
554
  */
540
555
  API.prototype.addNamingRule = function addNamingRule(pattern, name) {
541
556
  const metric = this.agent.metrics.getOrCreateMetric(NAMES.SUPPORTABILITY.API + '/addNamingRule')
@@ -560,6 +575,7 @@ API.prototype.addNamingRule = function addNamingRule(pattern, name) {
560
575
  * newrelic.addIgnoringRule('^/socket\\.io/')
561
576
  *
562
577
  * @param {RegExp} pattern The pattern to ignore.
578
+ * @returns {void}
563
579
  */
564
580
  API.prototype.addIgnoringRule = function addIgnoringRule(pattern) {
565
581
  const metric = this.agent.metrics.getOrCreateMetric(NAMES.SUPPORTABILITY.API + '/addIgnoringRule')
@@ -572,6 +588,52 @@ API.prototype.addIgnoringRule = function addIgnoringRule(pattern) {
572
588
  this.agent.userNormalizer.addSimple(pattern, null)
573
589
  }
574
590
 
591
+ /**
592
+ * Gracefully fail.
593
+ *
594
+ * Output an HTML comment and log a warning the comment is meant to be
595
+ * innocuous to the end user.
596
+ *
597
+ * @see RUM_ISSUES
598
+ * @param {number} errorCode Error code from `RUM_ISSUES`.
599
+ * @param {boolean} [quiet=false] Be quiet about this failure.
600
+ * @returns {string} HTML comment for debugging purposes with specific error code
601
+ */
602
+ function _gracefail(errorCode, quiet) {
603
+ if (quiet) {
604
+ logger.debug(RUM_ISSUES[errorCode])
605
+ } else {
606
+ logger.warn(RUM_ISSUES[errorCode])
607
+ }
608
+ return '<!-- NREUM: (' + errorCode + ') -->'
609
+ }
610
+
611
+ /**
612
+ * Function for generating a fully formed RUM header based on configuration options
613
+ *
614
+ * @param {object} options Configuration options for RUM
615
+ * @param {string} [options.nonce] Nonce to inject into `<script>` header.
616
+ * @param {boolean} [options.hasToRemoveScriptWrapper] Used to import agent script without `<script>` tag wrapper.
617
+ * @param {string} metadata Stringified representation of rumHash metadata
618
+ * @param {string} loader Agent Loader script
619
+ * @returns {string} fully formed RUM header
620
+ */
621
+ function _generateRUMHeader(options = {}, metadata, loader) {
622
+ const formatArgs = []
623
+
624
+ if (options.hasToRemoveScriptWrapper) {
625
+ formatArgs.push(RUM_STUB)
626
+ } else if (options.nonce) {
627
+ formatArgs.push(RUM_STUB_SHELL_WITH_NONCE_PARAM, `nonce="${options.nonce}"`)
628
+ } else {
629
+ formatArgs.push(RUM_STUB_SHELL)
630
+ }
631
+
632
+ formatArgs.push(metadata, loader)
633
+
634
+ return util.format(...formatArgs)
635
+ }
636
+
575
637
  /**
576
638
  * Get the script header necessary for Browser Monitoring
577
639
  * This script must be manually injected into your templates, as high as possible
@@ -587,9 +649,9 @@ API.prototype.addIgnoringRule = function addIgnoringRule(pattern) {
587
649
  *
588
650
  * Do *not* reuse the headers between users, or even between requests.
589
651
  *
652
+ * @param {object} options configuration options
590
653
  * @param {string} [options.nonce] - Nonce to inject into `<script>` header.
591
654
  * @param {boolean} [options.hasToRemoveScriptWrapper] - Used to import agent script without `<script>` tag wrapper.
592
- * @param options
593
655
  * @returns {string} The script content to be injected in `<head>` or put inside `<script>` tag (depending on options)
594
656
  */
595
657
  API.prototype.getBrowserTimingHeader = function getBrowserTimingHeader(options) {
@@ -600,26 +662,6 @@ API.prototype.getBrowserTimingHeader = function getBrowserTimingHeader(options)
600
662
 
601
663
  const config = this.agent.config
602
664
 
603
- /**
604
- * Gracefully fail.
605
- *
606
- * Output an HTML comment and log a warning the comment is meant to be
607
- * innocuous to the end user.
608
- *
609
- * @param {number} num - Error code from `RUM_ISSUES`.
610
- * @param {bool} [quite=false] - Be quiet about this failure.
611
- * @param quiet
612
- * @see RUM_ISSUES
613
- */
614
- function _gracefail(num, quiet) {
615
- if (quiet) {
616
- logger.debug(RUM_ISSUES[num])
617
- } else {
618
- logger.warn(RUM_ISSUES[num])
619
- }
620
- return '<!-- NREUM: (' + num + ') -->'
621
- }
622
-
623
665
  const browserMonitoring = config.browser_monitoring
624
666
 
625
667
  // config.browser_monitoring should always exist, but we don't want the agent
@@ -738,17 +780,7 @@ API.prototype.getBrowserTimingHeader = function getBrowserTimingHeader(options)
738
780
  const json = JSON.stringify(rumHash, null, tabs)
739
781
 
740
782
  // the complete header to be written to the browser
741
- const out =
742
- options && options.hasToRemoveScriptWrapper
743
- ? util.format(RUM_STUB, json, jsAgentLoader)
744
- : options && options.nonce
745
- ? util.format(
746
- RUM_STUB_SHELL_WITH_NONCE_PARAM,
747
- 'nonce="' + options.nonce + '"',
748
- json,
749
- jsAgentLoader
750
- )
751
- : util.format(RUM_STUB_SHELL, json, jsAgentLoader)
783
+ const out = _generateRUMHeader(options, json, jsAgentLoader)
752
784
 
753
785
  logger.trace('generating RUM header', out)
754
786
 
@@ -773,7 +805,7 @@ API.prototype.getBrowserTimingHeader = function getBrowserTimingHeader(options)
773
805
  * })
774
806
  * @param {string} name
775
807
  * The name to give the new segment. This will also be the name of the metric.
776
- * @param {bool} record
808
+ * @param {boolean} record
777
809
  * Indicates if the segment should be recorded as a metric. Metrics will show
778
810
  * up on the transaction breakdown table and server breakdown graph. Segments
779
811
  * just show up in transaction traces.
@@ -845,6 +877,7 @@ API.prototype.startSegment = function startSegment(name, record, handler, callba
845
877
  * so it should be a generic name and not iclude any variable parameters.
846
878
  * @param {Function} handle
847
879
  * Function that represents the transaction work.
880
+ * @returns {null|*} Returns null if handle is not a function, otherwise the return value of handle
848
881
  */
849
882
  API.prototype.startWebTransaction = function startWebTransaction(url, handle) {
850
883
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -935,7 +968,8 @@ API.prototype.startBackgroundTransaction = startBackgroundTransaction
935
968
  * https://docs.newrelic.com/docs/apm/applications-menu/monitoring/transactions-page#txn-type-dropdown
936
969
  * @param {Function} handle
937
970
  * Function that represents the background work.
938
- * @memberOf API#
971
+ * @memberof API#
972
+ * @returns {null|*} Returns null if handle is not a function, otherwise the return value of handle
939
973
  */
940
974
  function startBackgroundTransaction(name, group, handle) {
941
975
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -1040,7 +1074,7 @@ API.prototype.endTransaction = function endTransaction() {
1040
1074
  * of these keys match the names of the keys used by the platform API.
1041
1075
  *
1042
1076
  * @param {string} name The name of the metric.
1043
- * @param {number|object} value
1077
+ * @param {number|object} value The value of the metric to record
1044
1078
  */
1045
1079
  API.prototype.recordMetric = function recordMetric(name, value) {
1046
1080
  const supportMetric = this.agent.metrics.getOrCreateMetric(
@@ -1128,6 +1162,7 @@ API.prototype.incrementMetric = function incrementMetric(name, value) {
1128
1162
  * @param {object} attributes Object of key and value pairs. The keys must be shorter
1129
1163
  * than 255 characters, and the values must be string, number,
1130
1164
  * or boolean.
1165
+ * @returns {false|undefined} Returns false explicitly if failed/disabled, otherwise undefined
1131
1166
  */
1132
1167
  API.prototype.recordCustomEvent = function recordCustomEvent(eventType, attributes) {
1133
1168
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -1210,15 +1245,13 @@ API.prototype.recordCustomEvent = function recordCustomEvent(eventType, attribut
1210
1245
  * - `newrelic.instrument(moduleName, onRequire [,onError])`
1211
1246
  * - `newrelic.instrument(options)`
1212
1247
  *
1213
- * @param {object} options The options for this custom instrumentation.
1214
- * @param {string} options.moduleName The module name given to require to load the module
1215
- * @param {Function} options.onResolved The function to call prior to module load after the filepath has been resolved
1216
- * @param {Function} options.onRequire The function to call when the module has been loaded
1217
- * @param {Function} [options.onError] If provided, should `onRequire` throw an error, the error will be passed to
1218
- * this function.
1219
- * @param moduleName
1220
- * @param onRequire
1221
- * @param onError
1248
+ * @param {string|object} moduleName The module name given to require to load the module, or the instrumentation specification
1249
+ * @param {string} moduleName.moduleName The module name given to require to load the module
1250
+ * @param {Function} moduleName.onResolved The function to call prior to module load after the filepath has been resolved
1251
+ * @param {Function} moduleName.onRequire The function to call when the module has been loaded
1252
+ * @param {Function} [moduleName.onError] If provided, should `onRequire` throw an error, the error will be passed to
1253
+ * @param {Function} onRequire The function to call when the module has been loaded
1254
+ * @param {Function} onError If provided, should `onRequire` throw an error, the error will be passed to this function.
1222
1255
  */
1223
1256
  API.prototype.instrument = function instrument(moduleName, onRequire, onError) {
1224
1257
  const metric = this.agent.metrics.getOrCreateMetric(NAMES.SUPPORTABILITY.API + '/instrument')
@@ -1243,15 +1276,13 @@ API.prototype.instrument = function instrument(moduleName, onRequire, onError) {
1243
1276
  * - `newrelic.instrumentConglomerate(moduleName, onRequire [, onError])`
1244
1277
  * - `newrelic.isntrumentConglomerate(options)`
1245
1278
  *
1246
- * @param {object} options The options for this custom instrumentation.
1247
- * @param {string} options.moduleName The module name given to require to load the module
1248
- * @param {Function} options.onResolved The function to call prior to module load after the filepath has been resolved
1249
- * @param {Function} options.onRequire The function to call when the module has been loaded
1250
- * @param {Function} [options.onError] If provided, should `onRequire` throw an error, the error will be passed to
1251
- * this function.
1252
- * @param moduleName
1253
- * @param onRequire
1254
- * @param onError
1279
+ * @param {string|object} moduleName The module name given to require to load the module, or the instrumentation specification
1280
+ * @param {string} moduleName.moduleName The module name given to require to load the module
1281
+ * @param {Function} moduleName.onResolved The function to call prior to module load after the filepath has been resolved
1282
+ * @param {Function} moduleName.onRequire The function to call when the module has been loaded
1283
+ * @param {Function} [moduleName.onError] If provided, should `onRequire` throw an error, the error will be passed to
1284
+ * @param {Function} onRequire The function to call when the module has been loaded
1285
+ * @param {Function} onError If provided, should `onRequire` throw an error, the error will be passed to this function.
1255
1286
  */
1256
1287
  API.prototype.instrumentConglomerate = function instrumentConglomerate(
1257
1288
  moduleName,
@@ -1277,15 +1308,13 @@ API.prototype.instrumentConglomerate = function instrumentConglomerate(
1277
1308
  * - `newrelic.instrumentDatastore(moduleName, onRequire [,onError])`
1278
1309
  * - `newrelic.instrumentDatastore(options)`
1279
1310
  *
1280
- * @param {object} options The options for this custom instrumentation.
1281
- * @param {string} options.moduleName The module name given to require to load the module
1282
- * @param {Function} options.onResolved The function to call prior to module load after the filepath has been resolved
1283
- * @param {Function} options.onRequire The function to call when the module has been loaded
1284
- * @param {Function} [options.onError] If provided, should `onRequire` throw an error, the error will be passed to
1285
- * this function.
1286
- * @param moduleName
1287
- * @param onRequire
1288
- * @param onError
1311
+ * @param {string|object} moduleName The module name given to require to load the module, or the instrumentation specification
1312
+ * @param {string} moduleName.moduleName The module name given to require to load the module
1313
+ * @param {Function} moduleName.onResolved The function to call prior to module load after the filepath has been resolved
1314
+ * @param {Function} moduleName.onRequire The function to call when the module has been loaded
1315
+ * @param {Function} [moduleName.onError] If provided, should `onRequire` throw an error, the error will be passed to
1316
+ * @param {Function} onRequire The function to call when the module has been loaded
1317
+ * @param {Function} onError If provided, should `onRequire` throw an error, the error will be passed to this function.
1289
1318
  */
1290
1319
  API.prototype.instrumentDatastore = function instrumentDatastore(moduleName, onRequire, onError) {
1291
1320
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -1312,15 +1341,13 @@ API.prototype.instrumentDatastore = function instrumentDatastore(moduleName, onR
1312
1341
  * - `newrelic.instrumentWebframework(moduleName, onRequire [,onError])`
1313
1342
  * - `newrelic.instrumentWebframework(options)`
1314
1343
  *
1315
- * @param {object} options The options for this custom instrumentation.
1316
- * @param {string} options.moduleName The module name given to require to load the module
1317
- * @param {Function} options.onResolved The function to call prior to module load after the filepath has been resolved
1318
- * @param {Function} options.onRequire The function to call when the module has been loaded
1319
- * @param {Function} [options.onError] If provided, should `onRequire` throw an error, the error will be passed to
1320
- * this function.
1321
- * @param moduleName
1322
- * @param onRequire
1323
- * @param onError
1344
+ * @param {string|object} moduleName The module name given to require to load the module, or the instrumentation specification
1345
+ * @param {string} moduleName.moduleName The module name given to require to load the module
1346
+ * @param {Function} moduleName.onResolved The function to call prior to module load after the filepath has been resolved
1347
+ * @param {Function} moduleName.onRequire The function to call when the module has been loaded
1348
+ * @param {Function} [moduleName.onError] If provided, should `onRequire` throw an error, the error will be passed to
1349
+ * @param {Function} onRequire The function to call when the module has been loaded
1350
+ * @param {Function} onError If provided, should `onRequire` throw an error, the error will be passed to this function.
1324
1351
  */
1325
1352
  API.prototype.instrumentWebframework = function instrumentWebframework(
1326
1353
  moduleName,
@@ -1351,15 +1378,13 @@ API.prototype.instrumentWebframework = function instrumentWebframework(
1351
1378
  * - `newrelic.instrumentMessages(moduleName, onRequire [,onError])`
1352
1379
  * - `newrelic.instrumentMessages(options)`
1353
1380
  *
1354
- * @param {object} options The options for this custom instrumentation.
1355
- * @param {string} options.moduleName The module name given to require to load the module
1356
- * @param {Function} options.onResolved The function to call prior to module load after the filepath has been resolved
1357
- * @param {Function} options.onRequire The function to call when the module has been loaded
1358
- * @param {Function} [options.onError] If provided, should `onRequire` throw an error, the error will be passed to
1359
- * this function.
1360
- * @param moduleName
1361
- * @param onRequire
1362
- * @param onError
1381
+ * @param {string|object} moduleName The module name given to require to load the module, or the instrumentation specification
1382
+ * @param {string} moduleName.moduleName The module name given to require to load the module
1383
+ * @param {Function} moduleName.onResolved The function to call prior to module load after the filepath has been resolved
1384
+ * @param {Function} moduleName.onRequire The function to call when the module has been loaded
1385
+ * @param {Function} [moduleName.onError] If provided, should `onRequire` throw an error, the error will be passed to
1386
+ * @param {Function} onRequire The function to call when the module has been loaded
1387
+ * @param {Function} onError If provided, should `onRequire` throw an error, the error will be passed to this function.
1363
1388
  */
1364
1389
  API.prototype.instrumentMessages = function instrumentMessages(moduleName, onRequire, onError) {
1365
1390
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -1398,6 +1423,7 @@ API.prototype.instrumentMessages = function instrumentMessages(moduleName, onReq
1398
1423
  * into an instrumentation key.
1399
1424
  * @param {object} module
1400
1425
  * The actual module object or function we're instrumenting
1426
+ * @returns {boolean} Whether or not the module was successfully instrumented
1401
1427
  */
1402
1428
  API.prototype.instrumentLoadedModule = function instrumentLoadedModule(moduleName, module) {
1403
1429
  const metric = this.agent.metrics.getOrCreateMetric(
@@ -1458,9 +1484,8 @@ API.prototype.getTraceMetadata = function getTraceMetadata() {
1458
1484
  * Time in milliseconds to wait before shutting down.
1459
1485
  * @param {boolean} [options.waitForIdle=false]
1460
1486
  * If true, the agent will not shut down until there are no active transactions.
1461
- * @param {Function} [callback]
1487
+ * @param {Function} [cb]
1462
1488
  * Callback function that runs when agent stops.
1463
- * @param cb
1464
1489
  */
1465
1490
  API.prototype.shutdown = function shutdown(options, cb) {
1466
1491
  this.agent.metrics.getOrCreateMetric(`${NAMES.SUPPORTABILITY.API}/shutdown`).incrementCallCount()
@@ -1483,9 +1508,31 @@ API.prototype.shutdown = function shutdown(options, cb) {
1483
1508
  }
1484
1509
 
1485
1510
  /**
1486
- * @param api
1487
- * @param options
1488
- * @param callback
1511
+ * Helper function for logging if an error occurs, and where
1512
+ *
1513
+ * @param {Error} error If defined, the error that occurred
1514
+ * @param {string} phase Where in the process the error happened
1515
+ * @returns {void}
1516
+ */
1517
+ function _logErrorCallback(error, phase) {
1518
+ if (error) {
1519
+ logger.error(error, `An error occurred during ${phase}`)
1520
+ }
1521
+ }
1522
+
1523
+ /**
1524
+ * Function for handling the graceful shutdown process, including processing of data and handling errors
1525
+ *
1526
+ * @param {object} api instantiation of this file
1527
+ * @param {object} options shutdown options object
1528
+ * @param {boolean} [options.collectPendingData=false]
1529
+ * If true, the agent will send any pending data to the collector before
1530
+ * shutting down.
1531
+ * @param {number} [options.timeout=0]
1532
+ * Time in milliseconds to wait before shutting down.
1533
+ * @param {boolean} [options.waitForIdle=false]
1534
+ * If true, the agent will not shut down until there are no active transactions.
1535
+ * @param {Function} callback callback function to execute after shutdown process is complete (successful or not)
1489
1536
  */
1490
1537
  function _doShutdown(api, options, callback) {
1491
1538
  const agent = api.agent
@@ -1503,12 +1550,12 @@ function _doShutdown(api, options, callback) {
1503
1550
  }
1504
1551
 
1505
1552
  /**
1506
- * @param error
1553
+ * Callback function for after harvest cycles happen as part of shutdown process
1554
+ *
1555
+ * @param {Error} error If defined, the error that occurred during harvest
1507
1556
  */
1508
1557
  function afterHarvest(error) {
1509
- if (error) {
1510
- logger.error(error, 'An error occurred while running last harvest before shutdown.')
1511
- }
1558
+ _logErrorCallback(error, 'last harvest before shutdown')
1512
1559
  agent.stop(callback)
1513
1560
  }
1514
1561
 
@@ -1526,10 +1573,8 @@ function _doShutdown(api, options, callback) {
1526
1573
  })
1527
1574
 
1528
1575
  agent.on('errored', function logShutdownError(error) {
1576
+ _logErrorCallback(error, 'after shutdown')
1529
1577
  agent.stop(callback)
1530
- if (error) {
1531
- logger.error(error, 'The agent encountered an error after calling shutdown.')
1532
- }
1533
1578
  })
1534
1579
  } else if (options.collectPendingData) {
1535
1580
  agent.forceHarvestAll(afterHarvest)
@@ -1539,8 +1584,12 @@ function _doShutdown(api, options, callback) {
1539
1584
  }
1540
1585
 
1541
1586
  /**
1542
- * @param object
1543
- * @param maxLength
1587
+ * Validates that all keys in a given object have values that are less than or equal to a given length
1588
+ * Assumes all values have .length property (string/array)
1589
+ *
1590
+ * @param {object} object The object to validate
1591
+ * @param {number} maxLength The max allowed length
1592
+ * @returns {boolean} Whether or not the object passes validation
1544
1593
  */
1545
1594
  function _checkKeyLength(object, maxLength) {
1546
1595
  const keys = Object.keys(object)
@@ -1571,8 +1620,11 @@ API.prototype.setLambdaHandler = function setLambdaHandler(handler) {
1571
1620
  }
1572
1621
 
1573
1622
  /**
1574
- * @param attributes
1575
- * @param name
1623
+ * Function for removing invalid attribute key/value pairs from an object
1624
+ *
1625
+ * @param {object} attributes The attribute object
1626
+ * @param {string} name Caller name, used for debugging/logging purposes only
1627
+ * @returns {object} Attribute object containing only valid key/value pairs
1576
1628
  */
1577
1629
  function _filterAttributes(attributes, name) {
1578
1630
  const filteredAttributes = Object.create(null)