newrelic 6.4.0 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.eslintignore +1 -1
  2. package/{.eslintrc → .eslintrc.js} +2 -1
  3. package/.travis.yml +5 -6
  4. package/NEWS.md +105 -1
  5. package/THIRD_PARTY_NOTICES.md +420 -0
  6. package/api.js +72 -1
  7. package/index.js +3 -3
  8. package/lib/agent.js +22 -8
  9. package/lib/aggregators/base-aggregator.js +1 -1
  10. package/lib/attributes.js +15 -6
  11. package/lib/collector/serverless.js +8 -0
  12. package/lib/config/default.js +23 -1
  13. package/lib/config/env.js +7 -1
  14. package/lib/config/hsm.js +6 -1
  15. package/lib/config/index.js +36 -8
  16. package/lib/config/lasp.js +16 -1
  17. package/lib/errors/error-collector.js +51 -90
  18. package/lib/errors/helper.js +13 -13
  19. package/lib/errors/index.js +36 -18
  20. package/lib/grpc/connection/states.js +9 -0
  21. package/lib/grpc/connection.js +344 -0
  22. package/lib/grpc/endpoints/infinite-tracing/v1.proto +29 -0
  23. package/lib/header-processing.js +75 -0
  24. package/lib/instrumentation/core/child_process.js +12 -0
  25. package/lib/instrumentation/core/http-outbound.js +4 -0
  26. package/lib/instrumentation/core/http.js +98 -145
  27. package/lib/instrumentation/hapi/hapi-17.js +10 -3
  28. package/lib/metrics/names.js +12 -1
  29. package/lib/proxy/grpc.js +15 -0
  30. package/lib/serverless/aws-lambda.js +2 -0
  31. package/lib/spans/create-span-event-aggregator.js +112 -0
  32. package/lib/spans/map-to-streaming-type.js +44 -0
  33. package/lib/spans/span-event-aggregator.js +5 -0
  34. package/lib/spans/span-event.js +22 -9
  35. package/lib/spans/span-streamer.js +109 -0
  36. package/lib/spans/streaming-span-attributes.js +59 -0
  37. package/lib/spans/streaming-span-event-aggregator.js +98 -0
  38. package/lib/spans/streaming-span-event.js +261 -0
  39. package/lib/transaction/index.js +36 -20
  40. package/lib/transaction/trace/index.js +13 -10
  41. package/lib/transaction/trace/segment.js +12 -1
  42. package/lib/transaction/tracecontext.js +4 -0
  43. package/package.json +10 -10
  44. package/stub_api.js +5 -0
package/index.js CHANGED
@@ -43,10 +43,10 @@ function initialize() {
43
43
  preAgentTime
44
44
  )
45
45
 
46
- // TODO: Update this check when Node v6 is deprecated.
47
- if (psemver.satisfies('<6.0.0')) {
46
+ // TODO: Update this check when Node v8 is deprecated.
47
+ if (psemver.satisfies('<8.0.0')) {
48
48
  message = 'New Relic for Node.js requires a version of Node equal to or\n' +
49
- 'greater than 6.0.0. Not starting!'
49
+ 'greater than 8.0.0. Not starting!'
50
50
 
51
51
  logger.error(message)
52
52
  throw new Error(message)
package/lib/agent.js CHANGED
@@ -18,12 +18,12 @@ const NAMES = require('./metrics/names')
18
18
  const QueryTraceAggregator = require('./db/query-trace-aggregator')
19
19
  const sampler = require('./sampler')
20
20
  const TransactionTraceAggregator = require('./transaction/trace/aggregator')
21
- const SpanEventAggregator = require('./spans/span-event-aggregator')
22
21
  const TransactionEventAggregator = require('./transaction/transaction-event-aggregator')
23
22
  const Tracer = require('./transaction/tracer')
24
23
  const TxSegmentNormalizer = require('./metrics/normalizer/tx_segment')
25
24
  const uninstrumented = require('./uninstrumented')
26
25
  const util = require('util')
26
+ const createSpanEventAggregator = require('./spans/create-span-event-aggregator')
27
27
 
28
28
  // Map of valid states to whether or not data collection is valid
29
29
  const STATES = {
@@ -84,13 +84,7 @@ function Agent(config) {
84
84
  this._beforeMetricDataSend.bind(this)
85
85
  )
86
86
 
87
- // Open tracing.
88
- this.spanEventAggregator = new SpanEventAggregator({
89
- periodMs: config.event_harvest_config.report_period_ms,
90
- limit: config.event_harvest_config.harvest_limits.span_event_data
91
- },
92
- this.collector,
93
- this.metrics)
87
+ this.spanEventAggregator = createSpanEventAggregator(config, this.collector, this.metrics)
94
88
 
95
89
  this.transactionNameNormalizer = new MetricNormalizer(this.config, 'transaction name')
96
90
  // Segment term based tx renaming for MGI mitigation.
@@ -249,6 +243,10 @@ Agent.prototype.start = function start(callback) {
249
243
  agent.startAggregators()
250
244
  callback(null, config)
251
245
  } else {
246
+ // For data collection that streams immediately, dont delay capture/sending until
247
+ // the harvest of everything else has completed.
248
+ agent.startStreaming()
249
+
252
250
  // Harvest immediately for quicker data display, but after at least 1
253
251
  // second or the collector will throw away the data.
254
252
  //
@@ -409,6 +407,16 @@ Agent.prototype.stopAggregators = function stopAggregators() {
409
407
  this.customEventAggregator.stop()
410
408
  }
411
409
 
410
+ Agent.prototype.startStreaming = function startStreaming() {
411
+ if (
412
+ this.spanEventAggregator.isStream &&
413
+ this.config.distributed_tracing.enabled &&
414
+ this.config.span_events.enabled
415
+ ) {
416
+ this.spanEventAggregator.start()
417
+ }
418
+ }
419
+
412
420
  Agent.prototype.startAggregators = function startAggregators() {
413
421
  this.metrics.start()
414
422
  this.errors.start()
@@ -798,6 +806,12 @@ Agent.prototype.setLambdaArn = function setLambdaArn(arn) {
798
806
  }
799
807
  }
800
808
 
809
+ Agent.prototype.setLambdaFunctionVersion = function setLambdaFunctionVersion(function_version) {
810
+ if (this.collector instanceof ServerlessCollector) {
811
+ this.collector.setLambdaFunctionVersion(function_version)
812
+ }
813
+ }
814
+
801
815
  /**
802
816
  * Get the current transaction (if there is one) from the tracer.
803
817
  *
@@ -88,7 +88,7 @@ class Aggregator extends EventEmitter {
88
88
  }
89
89
 
90
90
  send() {
91
- logger.info(`${this.method} Aggregator data send.`)
91
+ logger.debug(`${this.method} Aggregator data send.`)
92
92
  this.emit(`starting ${this.method} data send.`)
93
93
 
94
94
  const data = this._getMergeData()
package/lib/attributes.js CHANGED
@@ -6,6 +6,8 @@ const isValidType = require('./util/attribute-types')
6
6
  const byteUtils = require('./util/byte-limit')
7
7
  const properties = require('./util/properties')
8
8
 
9
+ const MAXIMUM_CUSTOM_ATTRIBUTES = 64
10
+
9
11
  /**
10
12
  * @class
11
13
  * @private
@@ -23,6 +25,7 @@ class Attributes {
23
25
  this.filter = makeFilter(scope)
24
26
  this.limit = limit
25
27
  this.attributes = Object.create(null)
28
+ this.attributeCount = 0
26
29
  }
27
30
 
28
31
  /**
@@ -57,7 +60,6 @@ class Attributes {
57
60
  */
58
61
  get(dest) {
59
62
  const attrs = Object.create(null)
60
- let attrCount = 0
61
63
  for (let key in this.attributes) { // eslint-disable-line guard-for-in
62
64
  const attr = this.attributes[key]
63
65
  if (!(attr.destinations & dest)) {
@@ -67,10 +69,6 @@ class Attributes {
67
69
  attrs[key] = typeof attr.value === 'string' && !attr.truncateExempt
68
70
  ? byteUtils.truncate(attr.value, 255)
69
71
  : attr.value
70
-
71
- if (++attrCount >= this.limit) {
72
- break
73
- }
74
72
  }
75
73
 
76
74
  return attrs
@@ -103,6 +101,13 @@ class Attributes {
103
101
  * @param {boolean} [truncateExempt=false] - Flag marking value exempt from truncation
104
102
  */
105
103
  addAttribute(destinations, key, value, truncateExempt = false) {
104
+ if (this.attributeCount + 1 > this.limit) {
105
+ return logger.debug(
106
+ `Maximum number of custom attributes have been added.
107
+ Dropping attribute ${key} with ${value} type.`
108
+ )
109
+ }
110
+
106
111
  if (!isValidType(value)) {
107
112
  return logger.debug(
108
113
  'Not adding attribute %s with %s value type. This is expected for undefined' +
@@ -123,6 +128,7 @@ class Attributes {
123
128
  // Only set the attribute if at least one destination passed
124
129
  const validDestinations = this.filter(destinations, key)
125
130
  if (validDestinations) {
131
+ this.attributeCount = this.attributeCount + 1
126
132
  this._set(validDestinations, key, value, truncateExempt)
127
133
  }
128
134
  }
@@ -159,4 +165,7 @@ function makeFilter(scope) {
159
165
  }
160
166
  }
161
167
 
162
- module.exports = Attributes
168
+ module.exports = {
169
+ Attributes: Attributes,
170
+ MAXIMUM_CUSTOM_ATTRIBUTES: MAXIMUM_CUSTOM_ATTRIBUTES
171
+ }
@@ -24,6 +24,7 @@ class ServerlessCollector {
24
24
  this.enabled = true
25
25
  this.metadata = {
26
26
  arn: null,
27
+ function_version: null,
27
28
  execution_environment: process.env.AWS_EXECUTION_ENV,
28
29
  protocol_version: 16,
29
30
  agent_version: agent.version
@@ -38,6 +39,13 @@ class ServerlessCollector {
38
39
  this.metadata.arn = arn
39
40
  }
40
41
 
42
+ /**
43
+ * Sets the function_version to be sent up in the metadata.
44
+ */
45
+ setLambdaFunctionVersion(function_version) {
46
+ this.metadata.function_version = function_version
47
+ }
48
+
41
49
  /**
42
50
  * Checks if the collector is currently collecting.
43
51
  *
@@ -456,6 +456,8 @@ exports.config = () => ({
456
456
  * or 'obfuscated' and other criteria (slow_sql.enabled etc) are met
457
457
  * for a query. The raw or obfuscated sql will be included in the
458
458
  * transaction trace and a slow query sample will be collected.
459
+ *
460
+ * @env NEW_RELIC_RECORD_SQL
459
461
  */
460
462
  record_sql: 'off',
461
463
 
@@ -882,6 +884,26 @@ exports.config = () => ({
882
884
  segment_parameters: {enabled: true}
883
885
  },
884
886
 
887
+ /**
888
+ * Controls the use of infinite tracing.
889
+ */
890
+ infinite_tracing: {
891
+ trace_observer: {
892
+ /**
893
+ * The URI HOST of the observer. Setting this enables infinite tracing.
894
+ *
895
+ * @env NEW_RELIC_INFINITE_TRACING_TRACE_OBSERVER_HOST
896
+ */
897
+ host: '',
898
+ /**
899
+ * The URI PORT of the observer.
900
+ *
901
+ * @env NEW_RELIC_INFINITE_TRACING_TRACE_OBSERVER_PORT
902
+ */
903
+ port: '443'
904
+ }
905
+ },
906
+
885
907
  /**
886
908
  * Specifies whether the agent will be used to monitor serverless functions.
887
909
  * For example: AWS Lambda
@@ -898,5 +920,5 @@ exports.config = () => ({
898
920
  * loop data.
899
921
  */
900
922
  native_metrics: {enabled: true}
901
- },
923
+ }
902
924
  })
package/lib/config/env.js CHANGED
@@ -152,7 +152,13 @@ const ENV_MAPPING = {
152
152
  },
153
153
  trusted_account_key: 'NEW_RELIC_TRUSTED_ACCOUNT_KEY',
154
154
  primary_application_id: 'NEW_RELIC_PRIMARY_APPLICATION_ID',
155
- account_id: 'NEW_RELIC_ACCOUNT_ID'
155
+ account_id: 'NEW_RELIC_ACCOUNT_ID',
156
+ infinite_tracing: {
157
+ trace_observer: {
158
+ host: 'NEW_RELIC_INFINITE_TRACING_TRACE_OBSERVER_HOST',
159
+ port: 'NEW_RELIC_INFINITE_TRACING_TRACE_OBSERVER_PORT'
160
+ }
161
+ }
156
162
  }
157
163
 
158
164
  // List of environment values which are comma-delimited lists.
package/lib/config/hsm.js CHANGED
@@ -51,7 +51,12 @@ const HIGH_SECURITY_SETTINGS = {
51
51
  const HIGH_SECURITY_KEYS = flatten.keys(HIGH_SECURITY_SETTINGS)
52
52
 
53
53
  // blank out these config values before sending to the collector
54
- const REDACT_BEFORE_SEND = new Set(['proxy_pass', 'proxy_user', 'proxy'])
54
+ const REDACT_BEFORE_SEND = new Set([
55
+ 'proxy_pass',
56
+ 'proxy_user',
57
+ 'proxy',
58
+ 'certificates' // should be public but in case user mistake and also these are huge
59
+ ])
55
60
 
56
61
  // process.domain needs to be stripped befeore sending
57
62
  const REMOVE_BEFORE_SEND = new Set(['domain'])
@@ -1152,15 +1152,15 @@ Config.prototype._loggingManuallySet = function _loggingManuallySet(inputConfig)
1152
1152
  /**
1153
1153
  * Returns true if native-metrics has been manually enabled via configuration
1154
1154
  * file or enveironment variable
1155
- *
1155
+ *
1156
1156
  * @param {*} inputConfig configuration pass to the Config constructor
1157
- *
1157
+ *
1158
1158
  * @returns {boolean}
1159
1159
  */
1160
- Config.prototype._nativeMetricsManuallySet =
1160
+ Config.prototype._nativeMetricsManuallySet =
1161
1161
  function _nativeMetricsManuallySet(inputConfig) {
1162
- const inputEnabled = inputConfig
1163
- && inputConfig.plugins
1162
+ const inputEnabled = inputConfig
1163
+ && inputConfig.plugins
1164
1164
  && inputConfig.plugins.native_metrics
1165
1165
  && inputConfig.plugins.native_metrics.enabled
1166
1166
  const envEnabled = process.env.NEW_RELIC_NATIVE_METRICS_ENABLED
@@ -1201,6 +1201,12 @@ Config.prototype._enforceServerless = function _enforceServerless(inputConfig) {
1201
1201
  logger.info('Cross application tracing is explicitly disabled in serverless_mode.')
1202
1202
  }
1203
1203
 
1204
+ if (this.infinite_tracing.trace_observer.host) {
1205
+ this.infinite_tracing.trace_observer.host = ''
1206
+ this.infinite_tracing.trace_observer.port = ''
1207
+ logger.info('Infinite tracing is explicitly disabled in serverless_mode.')
1208
+ }
1209
+
1204
1210
  if (!this._loggingManuallySet(inputConfig)) {
1205
1211
  this.logging.enabled = false
1206
1212
 
@@ -1211,7 +1217,7 @@ Config.prototype._enforceServerless = function _enforceServerless(inputConfig) {
1211
1217
  )
1212
1218
  }
1213
1219
 
1214
- if (this._nativeMetricsManuallySet(inputConfig) &&
1220
+ if (this._nativeMetricsManuallySet(inputConfig) &&
1215
1221
  this.plugins.native_metrics.enabled) {
1216
1222
  logger.info(
1217
1223
  'Enabling the native-metrics module when in serverless mode may greatly ' +
@@ -1253,6 +1259,7 @@ Config.prototype._enforceServerless = function _enforceServerless(inputConfig) {
1253
1259
 
1254
1260
  return
1255
1261
  }
1262
+
1256
1263
  const DT_KEYS = ['account_id', 'primary_application_id', 'trusted_account_key']
1257
1264
  // Don't allow DT config settings to be set if serverless_mode is disabled
1258
1265
  DT_KEYS.forEach((key) => {
@@ -1458,8 +1465,12 @@ Config.prototype.applyLasp = function applyLasp(agent, policies) {
1458
1465
  }
1459
1466
  var valueName = splitConfigName[splitConfigName.length - 1]
1460
1467
  var localVal = settingBlock[valueName]
1468
+
1469
+ // Indexes into "allowed values" based on "enabled" setting
1470
+ // to retreive proper mapping.
1461
1471
  var policyValues = localMapping.allowedValues
1462
1472
  var policyValue = policyValues[policy.enabled ? 1 : 0]
1473
+
1463
1474
  // get the most secure setting between local config and the policy
1464
1475
  var finalValue = settingBlock[valueName] = config._getMostSecure(
1465
1476
  name,
@@ -1522,6 +1533,20 @@ Config.prototype.validateFlags = function validateFlags() {
1522
1533
  })
1523
1534
  }
1524
1535
 
1536
+ function redactValue(value) {
1537
+ const REDACT_VALUE = '****'
1538
+
1539
+ let result = null
1540
+ if (Array.isArray(value)) {
1541
+ // Redact each value so we know if was configured and how many values
1542
+ result = value.map(() => REDACT_VALUE)
1543
+ } else {
1544
+ result = REDACT_VALUE
1545
+ }
1546
+
1547
+ return result
1548
+ }
1549
+
1525
1550
  /**
1526
1551
  * Get a JSONifiable object containing all settings we want to report to the
1527
1552
  * collector and store in the environment_values table.
@@ -1532,9 +1557,10 @@ Config.prototype.publicSettings = function publicSettings() {
1532
1557
  var settings = Object.create(null)
1533
1558
 
1534
1559
  for (var key in this) {
1535
- if (this.hasOwnProperty(key)) {
1560
+ if (this.hasOwnProperty(key) && !REMOVE_BEFORE_SEND.has(key)) {
1536
1561
  if (HSM.REDACT_BEFORE_SEND.has(key)) {
1537
- settings[key] = '****'
1562
+ const value = this[key]
1563
+ settings[key] = redactValue(value)
1538
1564
  } else if (!HSM.REMOVE_BEFORE_SEND.has(key)) {
1539
1565
  settings[key] = this[key]
1540
1566
  }
@@ -1695,6 +1721,8 @@ function createInstance(config) {
1695
1721
  return _configInstance
1696
1722
  }
1697
1723
 
1724
+ const REMOVE_BEFORE_SEND = new Set(['attributeFilter'])
1725
+
1698
1726
  /**
1699
1727
  * Preserve the legacy initializer, but also allow consumers to manage their
1700
1728
  * own configuration if they choose.
@@ -1,12 +1,25 @@
1
1
  'use strict'
2
2
 
3
+ // TODO: would likely be easier to understand if the allowedValues mapping
4
+ // just took the raw enabled/disabled and translated. This is not a hot path.
5
+
6
+ /**
7
+ * path: Full nested path for the local configuration item.
8
+ * allowedValues:
9
+ * Array of valid config values to map for incoming enabled/disabled.
10
+ * policy.enabled: false uses index 0, policy.enabled: true uses index 1.
11
+ * filter: Allows for calculating most secure setting to use
12
+ * applyAdditionalSettings: Applies additional settings that are required
13
+ * when the policy is disabled.
14
+ * clearData: Clears the relevant agent collection.
15
+ */
3
16
  const LASP_MAP = {
4
17
  // LASP key
5
18
  record_sql: {
6
19
  // full path to corresponding config key
7
20
  path: 'transaction_tracer.record_sql',
8
21
  // Mapping from policy enabled status to usable config value
9
- // first element is policy is off, second is policy is on
22
+ // policy.enabled: false === off, policy.enabled: true === 'obfuscated'
10
23
  allowedValues: ['off', 'obfuscated'],
11
24
  // Tracks the precedent of settings controlled by LASP.
12
25
  filter: function mostSecureRecordSQL(first, second) {
@@ -45,6 +58,8 @@ const LASP_MAP = {
45
58
  // TODO: rename config key, because the names contradict each other's behavior
46
59
  allow_raw_exception_messages: {
47
60
  path: 'strip_exception_messages.enabled',
61
+ // if raw messages are allowed, then we should not strip them
62
+ // policy.enabled: false === true, policy.enabled: true === false
48
63
  allowedValues: [true, false],
49
64
  filter: function mostSecureStripException(first, second) {
50
65
  return first || second
@@ -4,6 +4,7 @@ const errorsModule = require('./index')
4
4
 
5
5
  const logger = require('../logger').child({component: 'error_tracer'})
6
6
  const urltils = require('../util/urltils')
7
+ const Exception = require('../errors').Exception
7
8
  const errorHelper = require('./helper')
8
9
  const createError = errorsModule.createError
9
10
  const createEvent = errorsModule.createEvent
@@ -129,7 +130,7 @@ class ErrorCollector {
129
130
  if (transaction.userErrors.length) {
130
131
  for (let i = 0; i < transaction.userErrors.length; i++) {
131
132
  const exception = transaction.userErrors[i]
132
- if (this._collect(transaction, exception[0], exception[1], exception[2])) {
133
+ if (this.collect(transaction, exception)) {
133
134
  ++collectedErrors
134
135
  }
135
136
  }
@@ -145,13 +146,13 @@ class ErrorCollector {
145
146
  if (transaction.exceptions.length) {
146
147
  for (let i = 0; i < transaction.exceptions.length; i++) {
147
148
  const exception = transaction.exceptions[i]
148
- if (this.collect(transaction, exception[0], exception[1], exception[2])) {
149
+ if (this.collect(transaction, exception)) {
149
150
  ++collectedErrors
150
151
  // if we could collect it, then check if expected
151
152
  if (isExpectedErrorStatusCode ||
152
153
  errorHelper.isExpectedException(
153
154
  transaction,
154
- exception[0],
155
+ exception.error,
155
156
  this.config,
156
157
  urltils
157
158
  )
@@ -178,31 +179,32 @@ class ErrorCollector {
178
179
  }
179
180
 
180
181
  /**
181
- * This function collects the error right away when transaction is not supplied.
182
- * Otherwise it delays collecting the error until the transaction ends.
182
+ * This function collects the error right away when transaction is not supplied. Otherwise it
183
+ * delays collecting the error until the transaction ends.
183
184
  *
184
185
  * NOTE: this interface is unofficial and may change in future.
185
186
  *
186
- * @param {?Transaction} transaction
187
- * Transaction associated with the error.
188
- *
189
- * @param {Error} exception
190
- * The error to be traced.
191
- *
192
- * @param {object} customAttributes
193
- * Any custom attributes associated with the request (optional).
187
+ * @param {?Transaction} transaction Transaction associated with the error.
188
+ * @param {Error} error The error to be traced.
189
+ * @param {?object} customAttributes Custom attributes associated with the request (optional).
194
190
  */
195
- add(transaction, exception, customAttributes) {
196
- if (!exception) {
191
+ add(transaction, error, customAttributes) {
192
+ if (!error) {
193
+ return
194
+ }
195
+
196
+ if (errorHelper.shouldIgnoreError(transaction, error, this.config)) {
197
+ logger.trace('Ignoring error')
197
198
  return
198
199
  }
199
200
 
200
201
  const timestamp = Date.now()
202
+ const exception = new Exception({error, timestamp, customAttributes})
201
203
 
202
204
  if (transaction) {
203
- transaction.addException(exception, customAttributes, timestamp)
205
+ transaction.addException(exception)
204
206
  } else {
205
- this.collect(transaction, exception, customAttributes, timestamp)
207
+ this.collect(transaction, exception)
206
208
  }
207
209
  }
208
210
 
@@ -217,95 +219,54 @@ class ErrorCollector {
217
219
  *
218
220
  * NOTE: this interface is unofficial and may change in future.
219
221
  *
220
- * @param {?Transaction} transaction
221
- * Transaction associated with the error.
222
- *
223
- * @param {Error} exception
224
- * The error to be traced.
225
- *
226
- * @param {object} [customAttributes=null]
227
- * Any custom attributes associated with the request (optional).
222
+ * @param {?Transaction} transaction Transaction associated with the error.
223
+ * @param {Exception} exception The Exception to be traced.
228
224
  */
229
- addUserError(transaction, exception, customAttributes) {
230
- if (!exception) return
225
+ addUserError(transaction, error, customAttributes) {
226
+ if (!error) return
231
227
 
232
- var timestamp = Date.now()
228
+ const timestamp = Date.now()
229
+ const exception = new Exception({error, timestamp, customAttributes})
233
230
 
234
231
  if (transaction) {
235
- transaction.addUserError(exception, customAttributes, timestamp)
232
+ transaction.addUserError(exception)
236
233
  } else {
237
- this._collect(transaction, exception, customAttributes, timestamp)
238
- }
239
- }
240
-
241
- /**
242
- * Wrapper for _collect, include logic for whether an error should
243
- * be ignored or not. Exists to allow userErrors to bypass ignore
244
- * logic.
245
- *
246
- * NOTE: this interface is unofficial and may change in future.
247
- *
248
- * @param {?Transaction} transaction
249
- * Transaction associated with the error.
250
- *
251
- * @param {?Error} exception
252
- * The error to be traced.
253
- *
254
- * @param {?object} customAttributes
255
- * Any custom attributes associated with the request.
256
- *
257
- * @param {number} timestamp
258
- *
259
- * @return {bool} True if the error was collected.
260
- */
261
- collect(transaction, exception, customAttributes, timestamp) {
262
- if (errorHelper.shouldIgnoreError(transaction, exception, this.config)) {
263
- logger.trace("Ignoring error")
264
- return
234
+ this.collect(transaction, exception)
265
235
  }
266
- return this._collect(transaction, exception, customAttributes, timestamp)
267
236
  }
268
237
 
269
238
  /**
270
239
  * Collects the error and also creates the error event.
271
240
  *
272
- * @private
273
- *
274
- * This function uses an array of seen exceptions to ensure errors don't get
275
- * double-counted. It can also be used as an unofficial means of marking that
276
- * user errors shouldn't be traced.
241
+ * This function uses an array of seen exceptions to ensure errors don't get double-counted. It
242
+ * can also be used as an unofficial means of marking that user errors shouldn't be traced.
277
243
  *
278
- * For an error to be traced, at least one of the transaction or the error
279
- * must be present.
244
+ * For an error to be traced, at least one of the transaction or the error must be present.
280
245
  *
281
246
  * NOTE: this interface is unofficial and may change in future.
282
247
  *
283
- * @param {?Transaction} transaction
284
- * Transaction associated with the error.
285
- *
286
- * @param {?Error} exception
287
- * The error to be traced.
288
- *
289
- * @param {?object} customAttributes
290
- * Any custom attributes associated with the request.
291
- *
292
- * @param {number} timestamp
293
- *
294
- * @return {bool} True if the error was collected.
248
+ * @param {?Transaction} transaction Transaction associated with the error.
249
+ * @param {?Exception} exception The Exception object to be traced.
250
+ * @return {bool} True if the error was collected.
295
251
  */
296
- _collect(transaction, exception, customAttributes, timestamp) {
297
- if (exception) {
298
- if (this._haveSeen(transaction, exception)) {
252
+ collect(transaction, exception) {
253
+ if (!exception) {
254
+ exception = new Exception({})
255
+ }
256
+
257
+ if (exception.error) {
258
+ if (this._haveSeen(transaction, exception.error)) {
299
259
  return false
300
260
  }
301
261
 
302
- if (typeof exception !== 'string' && !exception.message && !exception.stack) {
303
- logger.trace(exception, 'Got error that is not an instance of Error or string.')
304
- exception = null
262
+ const error = exception.error
263
+ if (typeof error !== 'string' && !error.message && !error.stack) {
264
+ logger.trace(error, 'Got error that is not an instance of Error or string.')
265
+ exception.error = null
305
266
  }
306
267
  }
307
268
 
308
- if (!exception && (!transaction || !transaction.statusCode || transaction.error)) {
269
+ if (!exception.error && (!transaction || !transaction.statusCode || transaction.error)) {
309
270
  return false
310
271
  }
311
272
 
@@ -321,13 +282,13 @@ class ErrorCollector {
321
282
  return false
322
283
  }
323
284
 
324
- if (exception) {
325
- logger.trace(exception, 'Got exception to trace:')
285
+ if (exception.error) {
286
+ logger.trace(exception.error, 'Got exception to trace:')
326
287
  }
327
288
 
328
- const error = createError(transaction, exception, customAttributes, this.config)
289
+ const errorTrace = createError(transaction, exception, this.config)
329
290
 
330
- const isExpectedError = true === error[4].intrinsics['error.expected']
291
+ const isExpectedError = true === errorTrace[4].intrinsics['error.expected']
331
292
 
332
293
  if (isExpectedError) {
333
294
  this.metrics.getOrCreateMetric(NAMES.ERRORS.EXPECTED).incrementCallCount()
@@ -343,11 +304,11 @@ class ErrorCollector {
343
304
  }
344
305
  }
345
306
 
346
- this.traceAggregator.add(error)
307
+ this.traceAggregator.add(errorTrace)
347
308
 
348
309
  if (this.config.error_collector.capture_events === true) {
349
310
  const priority = transaction && transaction.priority || Math.random()
350
- const event = createEvent(transaction, error, timestamp, this.config)
311
+ const event = createEvent(transaction, errorTrace, exception.timestamp, this.config)
351
312
  this.eventAggregator.add(event, priority)
352
313
  }
353
314
 
@@ -48,7 +48,7 @@ module.exports = {
48
48
 
49
49
  extractErrorInformation: function extractErrorInformation(
50
50
  transaction,
51
- exception,
51
+ error,
52
52
  config,
53
53
  urltils
54
54
  ) {
@@ -58,21 +58,21 @@ module.exports = {
58
58
 
59
59
  // String errors do not provide us with as much information to provide to the
60
60
  // user, but it is a common pattern.
61
- if (typeof exception === 'string') {
62
- message = exception
61
+ if (typeof error === 'string') {
62
+ message = error
63
63
  } else if (
64
- exception !== null &&
65
- typeof exception === 'object' &&
66
- exception.message &&
64
+ error !== null &&
65
+ typeof error === 'object' &&
66
+ error.message &&
67
67
  !config.high_security &&
68
68
  !config.strip_exception_messages.enabled
69
69
  ) {
70
- message = exception.message
70
+ message = error.message
71
71
 
72
- if (exception.name) {
73
- type = exception.name
74
- } else if (exception.constructor && exception.constructor.name) {
75
- type = exception.constructor.name
72
+ if (error.name) {
73
+ type = error.name
74
+ } else if (error.constructor && error.constructor.name) {
75
+ type = error.constructor.name
76
76
  }
77
77
  } else if (transaction && transaction.statusCode &&
78
78
  urltils.isError(config, transaction.statusCode)) {
@@ -95,9 +95,9 @@ module.exports = {
95
95
  }
96
96
  },
97
97
 
98
- shouldIgnoreError: function shouldIgnoreError(transaction, exception, config) {
98
+ shouldIgnoreError: function shouldIgnoreError(transaction, error, config) {
99
99
  // extract _just_ the error information, not transaction stuff
100
- let errorInfo = this.extractErrorInformation(null, exception, config, null)
100
+ let errorInfo = this.extractErrorInformation(null, error, config, null)
101
101
 
102
102
  return this.shouldIgnoreErrorClass(errorInfo, config) ||
103
103
  this.shouldIgnoreErrorMessage(errorInfo, config) ||