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
@@ -5,6 +5,7 @@
5
5
 
6
6
  'use strict'
7
7
 
8
+ const http = require('http')
8
9
  const CollectorResponse = require('./response')
9
10
  const facts = require('./facts')
10
11
  const logger = require('../logger').child({ component: 'collector_api' })
@@ -36,8 +37,8 @@ const FAILURE_DISCARD_DATA = new Set([400, 403, 404, 405, 407, 411, 413, 414, 41
36
37
  const AGENT_RUN_BEHAVIOR = CollectorResponse.AGENT_RUN_BEHAVIOR
37
38
 
38
39
  /**
39
- * @param errors
40
- * @param name
40
+ * @param {Array} errors list of errors
41
+ * @param {string} name collector endpoint name
41
42
  */
42
43
  function dumpErrors(errors, name) {
43
44
  let index = 1
@@ -54,7 +55,7 @@ function dumpErrors(errors, name) {
54
55
  }
55
56
 
56
57
  /**
57
- * @param agent
58
+ * @param {Agent} agent New Relic agent
58
59
  */
59
60
  function CollectorAPI(agent) {
60
61
  this._agent = agent
@@ -89,11 +90,15 @@ function CollectorAPI(agent) {
89
90
  }
90
91
  }
91
92
 
93
+ CollectorAPI.prototype._throwCallbackError = function _throwCallbackError() {
94
+ throw new TypeError('callback is required')
95
+ }
96
+
92
97
  /**
93
98
  * Updates all methods except preconnect w/ new host/port pairs sent down from server
94
99
  * during preconnect (via redirect_host). Preconnect does not update.
95
100
  *
96
- * @param endpoint
101
+ * @param {string} endpoint collector name
97
102
  */
98
103
  CollectorAPI.prototype._updateEndpoints = function _updateEndpoints(endpoint) {
99
104
  logger.trace('Updating endpoints to: ', endpoint)
@@ -107,112 +112,116 @@ CollectorAPI.prototype._updateEndpoints = function _updateEndpoints(endpoint) {
107
112
 
108
113
  CollectorAPI.prototype.connect = function connect(callback) {
109
114
  if (!callback) {
110
- throw new TypeError('callback is required')
115
+ this._throwCallbackError()
111
116
  }
112
117
 
113
118
  logger.trace('Starting collector.')
114
119
 
115
120
  this._agent.setState('connecting')
116
121
 
117
- const api = this
118
- const max = BACKOFFS.length
119
- const errors = []
120
- let attempts = 1
121
-
122
- const metric = this._agent.metrics.getOrCreateMetric(
123
- NAMES.SUPPORTABILITY.REGISTRATION + '/Attempts'
124
- )
125
-
126
122
  // Reset headers map for good measure
127
123
  if (this._reqHeadersMap) {
128
124
  this._reqHeadersMap = null
129
125
  }
130
126
 
131
- /**
132
- * Checks if proxy is configured to connect via `proxy_host` and `proxy_port`
133
- * and if error code is EPROTO or ECONNRESET. This is an indication their proxy
134
- * server only accepts HTTP connections, and we should provide an actionable warning to
135
- * fix the misconfiguration by setting `proxy` to a fully qualified URL
136
- *
137
- * @param {Error} error
138
- * @returns {boolean}
139
- */
140
- function isProxyMisconfigured(error) {
141
- const config = api._agent.config
142
- return (
143
- error &&
144
- ['EPROTO', 'ECONNRESET'].includes(error.code) &&
145
- config.proxy_host &&
146
- config.proxy_port &&
147
- !config.proxy
148
- )
127
+ const ctx = {
128
+ callback,
129
+ max: BACKOFFS.length,
130
+ errors: [],
131
+ attempts: 1
149
132
  }
133
+ this._login(this._retry.bind(this, ctx))
134
+ }
150
135
 
151
- /**
152
- * @param error
153
- * @param response
154
- */
155
- function retry(error, response) {
156
- metric.incrementCallCount()
136
+ /**
137
+ * Checks if proxy is configured to connect via `proxy_host` and `proxy_port`
138
+ * and if error code is EPROTO or ECONNRESET. This is an indication their proxy
139
+ * server only accepts HTTP connections, and we should provide an actionable warning to
140
+ * fix the misconfiguration by setting `proxy` to a fully qualified URL
141
+ *
142
+ * @param {Error} error response error
143
+ * @returns {boolean} determines if proxy is properly configured
144
+ */
145
+ CollectorAPI.prototype._isProxyMisconfigured = function _isProxyMisconfigured(error) {
146
+ const config = this._agent.config
147
+ return (
148
+ error &&
149
+ ['EPROTO', 'ECONNRESET'].includes(error.code) &&
150
+ config.proxy_host &&
151
+ config.proxy_port &&
152
+ !config.proxy
153
+ )
154
+ }
157
155
 
158
- if (error) {
159
- errors.push(error)
160
- } else if (response && SUCCESS.has(response.status)) {
161
- dumpErrors(errors, 'connect')
162
- return callback(null, CollectorResponse.success(response.payload))
163
- }
164
- if (!response) {
165
- response = CollectorResponse.retry()
166
- }
156
+ /**
157
+ * @param {object} ctx context object to pass from parent and between recursions.
158
+ * @param {Error} error response error
159
+ * @param {http.ServerResponse} response response from collector
160
+ * @returns {void}
161
+ */
162
+ CollectorAPI.prototype._retry = function _retry(ctx, error, response) {
163
+ const api = this
164
+ const metric = this._agent.metrics.getOrCreateMetric(
165
+ NAMES.SUPPORTABILITY.REGISTRATION + '/Attempts'
166
+ )
167
167
 
168
- // Retry everything except for an explicit Disconnect response code.
169
- if (response.status === 410 || response.agentRun === AGENT_RUN_BEHAVIOR.SHUTDOWN) {
170
- logger.error('The New Relic collector rejected this agent.')
171
- return callback(null, CollectorResponse.fatal(response.payload))
172
- } else if (response.status === 401) {
173
- logger.warn(
174
- error,
175
- 'Your license key appears to be invalid. Reattempting connection to New' +
176
- ' Relic. If the problem persists, please contact support@newrelic.com.' +
177
- ' (status code %s)',
178
- response.status
179
- )
180
- } else if (isProxyMisconfigured(error)) {
181
- logger.warn(
182
- error,
183
- 'Your proxy server appears to be configured to accept connections over http. ' +
184
- 'When setting `proxy_host` and `proxy_port` New Relic attempts to connect over ' +
185
- 'SSL(https). If your proxy is configured to accept connections over http, try ' +
186
- 'setting `proxy` to a fully qualified URL(e.g http://proxy-host:8080).'
187
- )
188
- }
168
+ metric.incrementCallCount()
189
169
 
190
- const backoff = BACKOFFS[Math.min(attempts, max) - 1]
191
- if (backoff.warn) {
192
- logger.warn('No connection has been established to New Relic after %d attempts.', attempts)
193
- }
170
+ if (error) {
171
+ ctx.errors.push(error)
172
+ } else if (response && SUCCESS.has(response.status)) {
173
+ dumpErrors(ctx.errors, 'connect')
174
+ ctx.callback(null, CollectorResponse.success(response.payload))
175
+ return
176
+ }
177
+ if (!response) {
178
+ response = CollectorResponse.retry()
179
+ }
194
180
 
195
- logger.debug(
181
+ // Retry everything except for an explicit Disconnect response code.
182
+ if (response.status === 410 || response.agentRun === AGENT_RUN_BEHAVIOR.SHUTDOWN) {
183
+ logger.error('The New Relic collector rejected this agent.')
184
+ return ctx.callback(null, CollectorResponse.fatal(response.payload))
185
+ } else if (response.status === 401) {
186
+ logger.warn(
187
+ error,
188
+ 'Your license key appears to be invalid. Reattempting connection to New' +
189
+ ' Relic. If the problem persists, please contact support@newrelic.com.' +
190
+ ' (status code %s)',
191
+ response.status
192
+ )
193
+ } else if (this._isProxyMisconfigured(error)) {
194
+ logger.warn(
196
195
  error,
197
- 'Failed to connect to New Relic after attempt %d, waiting %ds to retry.',
198
- attempts,
199
- backoff.interval
196
+ 'Your proxy server appears to be configured to accept connections over http. ' +
197
+ 'When setting `proxy_host` and `proxy_port` New Relic attempts to connect over ' +
198
+ 'SSL(https). If your proxy is configured to accept connections over http, try ' +
199
+ 'setting `proxy` to a fully qualified URL(e.g http://proxy-host:8080).'
200
200
  )
201
+ }
201
202
 
202
- ++attempts
203
- const timeout = setTimeout(function again() {
204
- api._login(retry)
205
- }, backoff.interval * TO_MILLIS)
206
- timeout.unref()
203
+ const backoff = BACKOFFS[Math.min(ctx.attempts, ctx.max) - 1]
204
+ if (backoff.warn) {
205
+ logger.warn('No connection has been established to New Relic after %d attempts.', ctx.attempts)
207
206
  }
208
207
 
209
- this._login(retry)
208
+ logger.debug(
209
+ error,
210
+ 'Failed to connect to New Relic after attempt %d, waiting %ds to retry.',
211
+ ctx.attempts,
212
+ backoff.interval
213
+ )
214
+
215
+ ++ctx.attempts
216
+ const timeout = setTimeout(function again() {
217
+ api._login(api._retry.bind(api, ctx))
218
+ }, backoff.interval * TO_MILLIS)
219
+ timeout.unref()
210
220
  }
211
221
 
212
222
  CollectorAPI.prototype._login = function _login(callback) {
213
223
  const methods = this._methods
214
224
  const agent = this._agent
215
- const self = this
216
225
 
217
226
  const preconnectData = { high_security: agent.config.high_security }
218
227
  if (agent.config.security_policies_token) {
@@ -221,56 +230,73 @@ CollectorAPI.prototype._login = function _login(callback) {
221
230
 
222
231
  const payload = [preconnectData]
223
232
 
224
- methods.preconnect.invoke(payload, onPreConnect)
233
+ methods.preconnect.invoke(payload, this._onPreConnect.bind(this, callback))
234
+ }
225
235
 
226
- /**
227
- * @param error
228
- * @param response
229
- */
230
- function onPreConnect(error, response) {
231
- if (error || !SUCCESS.has(response.status)) {
232
- return callback(error, response)
233
- }
236
+ /**
237
+ * @param {Function} callback function to run after processing response
238
+ * @param {Error} error response error
239
+ * @param {http.ServerResponse} response collector response from pre connect
240
+ * @returns {void}
241
+ */
242
+ CollectorAPI.prototype._onPreConnect = function _onPreConnect(callback, error, response) {
243
+ const agent = this._agent
244
+ if (error || !SUCCESS.has(response.status)) {
245
+ callback(error, response)
246
+ return
247
+ }
234
248
 
235
- const res = response.payload || Object.create(null)
236
- if (!res.redirect_host) {
249
+ const res = response.payload || Object.create(null)
250
+ this._handlePreConnectResponse(res)
251
+
252
+ const policies = res.security_policies || Object.create(null)
253
+
254
+ const laspResponse = agent.config.applyLasp(agent, policies)
255
+ if (laspResponse.shouldShutdownRun()) {
256
+ callback(null, laspResponse)
257
+ return
258
+ }
259
+
260
+ this._getFacts(laspResponse.payload, callback)
261
+ }
262
+
263
+ /**
264
+ * Checks the redirect_host and determines based on the URL if the collector
265
+ * needs to update the endpoint with new endpoint
266
+ *
267
+ * @param {http.ServerResponse} res collector response
268
+ */
269
+ CollectorAPI.prototype._handlePreConnectResponse = function _handlePreConnectResponse(res) {
270
+ const agent = this._agent
271
+
272
+ if (!res.redirect_host) {
273
+ logger.error(
274
+ "Requesting this account's collector from %s failed; trying default.",
275
+ agent.config.host
276
+ )
277
+ } else {
278
+ const parts = res.redirect_host.split(':')
279
+ if (parts.length > 2) {
237
280
  logger.error(
238
- "Requesting this account's collector from %s failed; trying default.",
239
- agent.config.host
281
+ "Requesting collector from %s returned bogus result '%s'; trying default.",
282
+ agent.config.host,
283
+ res.redirect_host
240
284
  )
241
285
  } else {
242
- const parts = res.redirect_host.split(':')
243
- if (parts.length > 2) {
244
- logger.error(
245
- "Requesting collector from %s returned bogus result '%s'; trying default.",
246
- agent.config.host,
247
- res.redirect_host
248
- )
249
- } else {
250
- logger.debug(
251
- "Requesting this account's collector from %s returned %s; reconfiguring.",
252
- agent.config.host,
253
- res.redirect_host
254
- )
255
-
256
- const [host, port] = parts
257
- const newEndpoint = {
258
- host: host,
259
- port: port || DEFAULT_PORT
260
- }
286
+ logger.debug(
287
+ "Requesting this account's collector from %s returned %s; reconfiguring.",
288
+ agent.config.host,
289
+ res.redirect_host
290
+ )
261
291
 
262
- self._updateEndpoints(newEndpoint)
292
+ const [host, port] = parts
293
+ const newEndpoint = {
294
+ host: host,
295
+ port: port || DEFAULT_PORT
263
296
  }
264
- }
265
297
 
266
- const policies = res.security_policies || Object.create(null)
267
-
268
- const laspResponse = agent.config.applyLasp(agent, policies)
269
- if (laspResponse.shouldShutdownRun()) {
270
- return callback(null, laspResponse)
298
+ this._updateEndpoints(newEndpoint)
271
299
  }
272
-
273
- self._getFacts(laspResponse.payload, callback)
274
300
  }
275
301
  }
276
302
 
@@ -292,50 +318,56 @@ CollectorAPI.prototype._getFacts = function _getFacts(lasp, callback) {
292
318
  }
293
319
 
294
320
  CollectorAPI.prototype._connect = function _connect(env, callback) {
295
- const collector = this
296
321
  const methods = this._methods
297
- const agent = this._agent
322
+ methods.connect.invoke(env, this._onConnect.bind(this, callback))
323
+ }
298
324
 
299
- methods.connect.invoke(env, onConnect)
325
+ /**
326
+ *
327
+ * Handles the response to the connect call
328
+ *
329
+ * @param {Function} callback function to run after processing response
330
+ * @param {Error} error collector response error
331
+ * @param {http.ServerOptions} res collector response
332
+ */
333
+ CollectorAPI.prototype._onConnect = function _onConnect(callback, error, res) {
334
+ const agent = this._agent
335
+ const methods = this._methods
300
336
 
301
- /**
302
- * @param error
303
- * @param res
304
- */
305
- function onConnect(error, res) {
306
- if (error || !SUCCESS.has(res.status)) {
307
- return callback(error, res)
308
- }
337
+ if (error || !SUCCESS.has(res.status)) {
338
+ callback(error, res)
339
+ return
340
+ }
309
341
 
310
- const config = res.payload
311
- if (!config || !config.agent_run_id) {
312
- return callback(new Error('No agent run ID received from handshake.'), res)
313
- }
342
+ const config = res.payload
343
+ if (!config || !config.agent_run_id) {
344
+ callback(new Error('No agent run ID received from handshake.'), res)
345
+ return
346
+ }
314
347
 
315
- agent.setState('connected')
348
+ agent.setState('connected')
316
349
 
317
- logger.info(
318
- 'Connected to %s:%d with agent run ID %s.',
319
- methods.connect.endpoint.host,
320
- methods.connect.endpoint.port,
321
- config.agent_run_id
322
- )
350
+ logger.info(
351
+ 'Connected to %s:%d with agent run ID %s.',
352
+ methods.connect.endpoint.host,
353
+ methods.connect.endpoint.port,
354
+ config.agent_run_id
355
+ )
323
356
 
324
- // Log "Reporting to..." message from connect response.
325
- if (config.messages) {
326
- config.messages.forEach((element) => {
327
- logger.info(element.message)
328
- })
329
- }
357
+ // Log "Reporting to..." message from connect response.
358
+ if (config.messages) {
359
+ config.messages.forEach((element) => {
360
+ logger.info(element.message)
361
+ })
362
+ }
330
363
 
331
- // Store request headers for future collector requests if they're present
332
- collector._reqHeadersMap = config.request_headers_map
364
+ // Store request headers for future collector requests if they're present
365
+ this._reqHeadersMap = config.request_headers_map
333
366
 
334
- // pass configuration data from the API so automatic reconnect works
335
- agent.reconfigure(config)
367
+ // pass configuration data from the API so automatic reconnect works
368
+ agent.reconfigure(config)
336
369
 
337
- callback(null, res)
338
- }
370
+ callback(null, res)
339
371
  }
340
372
 
341
373
  /**
@@ -373,13 +405,15 @@ CollectorAPI.prototype.reportSettings = function reportSettings(callback) {
373
405
  *
374
406
  * @param {Array} errors The encoded errors list.
375
407
  * @param {Function} callback The continuation / error handler.
408
+ * @returns {void}
376
409
  */
377
410
  CollectorAPI.prototype.error_data = function errorData(errors, callback) {
378
411
  if (!callback) {
379
- throw new TypeError('callback is required')
412
+ this._throwCallbackError()
380
413
  }
381
414
  if (!errors) {
382
- return callback(new TypeError('must pass errors to send'))
415
+ callback(new TypeError('must pass errors to send'))
416
+ return
383
417
  }
384
418
 
385
419
  this._sendData(this._methods.errors, errors, callback)
@@ -387,26 +421,30 @@ CollectorAPI.prototype.error_data = function errorData(errors, callback) {
387
421
 
388
422
  CollectorAPI.prototype.error_event_data = function errorEvents(events, callback) {
389
423
  if (!callback) {
390
- throw new TypeError('callback is required')
424
+ this._throwCallbackError()
391
425
  }
392
426
  if (!events) {
393
- return callback(new TypeError('must pass errors to send'))
427
+ callback(new TypeError('must pass errors to send'))
428
+ return
394
429
  }
395
430
 
396
431
  this._sendData(this._methods.errorEvents, events, callback)
397
432
  }
398
433
 
399
434
  CollectorAPI.prototype.analytic_event_data = transactionEvents
435
+
400
436
  /**
401
- * @param events
402
- * @param callback
437
+ * @param {Array} events list of events to send to collector
438
+ * @param {Function} callback handler on response
439
+ * @returns {void}
403
440
  */
404
441
  function transactionEvents(events, callback) {
405
442
  if (!callback) {
406
- throw new TypeError('callback is required')
443
+ this._throwCallbackError()
407
444
  }
408
445
  if (!events) {
409
- return callback(new TypeError('must pass events to send'))
446
+ callback(new TypeError('must pass events to send'))
447
+ return
410
448
  }
411
449
 
412
450
  this._sendData(this._methods.events, events, callback)
@@ -414,10 +452,11 @@ function transactionEvents(events, callback) {
414
452
 
415
453
  CollectorAPI.prototype.custom_event_data = function customEvents(events, callback) {
416
454
  if (!callback) {
417
- throw new TypeError('callback is required')
455
+ this._throwCallbackError()
418
456
  }
419
457
  if (!events) {
420
- return callback(new TypeError('must pass events to send'))
458
+ callback(new TypeError('must pass events to send'))
459
+ return
421
460
  }
422
461
 
423
462
  this._sendData(this._methods.customEvents, events, callback)
@@ -441,10 +480,11 @@ CollectorAPI.prototype.custom_event_data = function customEvents(events, callbac
441
480
  */
442
481
  CollectorAPI.prototype.metric_data = function metricData(metrics, callback) {
443
482
  if (!callback) {
444
- throw new TypeError('callback is required')
483
+ this._throwCallbackError()
445
484
  }
446
485
  if (!metrics) {
447
- return callback(new TypeError('must pass metrics to send'))
486
+ callback(new TypeError('must pass metrics to send'))
487
+ return
448
488
  }
449
489
 
450
490
  this._sendData(this._methods.metrics, metrics, callback)
@@ -464,20 +504,22 @@ CollectorAPI.prototype.metric_data = function metricData(metrics, callback) {
464
504
  */
465
505
  CollectorAPI.prototype.sql_trace_data = function queryData(queries, callback) {
466
506
  if (!callback) {
467
- throw new TypeError('callback is required')
507
+ this._throwCallbackError()
468
508
  }
469
509
  if (!queries) {
470
- return callback(new TypeError('must pass queries to send'))
510
+ callback(new TypeError('must pass queries to send'))
511
+ return
471
512
  }
472
513
  this._sendData(this._methods.queryData, queries, callback)
473
514
  }
474
515
 
475
516
  CollectorAPI.prototype.span_event_data = function spanEvents(events, callback) {
476
517
  if (!callback) {
477
- throw new TypeError('callback is required')
518
+ this._throwCallbackError()
478
519
  }
479
520
  if (!events) {
480
- return callback(new TypeError('must pass spans to send'))
521
+ callback(new TypeError('must pass spans to send'))
522
+ return
481
523
  }
482
524
  this._sendData(this._methods.spanEvents, events, callback)
483
525
  }
@@ -497,16 +539,18 @@ CollectorAPI.prototype.span_event_data = function spanEvents(events, callback) {
497
539
  * @param {Function} callback The continuation / error handler.
498
540
  */
499
541
  CollectorAPI.prototype.transaction_sample_data = transactionSampleData
542
+
500
543
  /**
501
- * @param traces
502
- * @param callback
544
+ * @param {Array} traces list of traces to send
545
+ * @param {Function} callback handler on response
503
546
  */
504
547
  function transactionSampleData(traces, callback) {
505
548
  if (!callback) {
506
- throw new TypeError('callback is required')
549
+ this._throwCallbackError()
507
550
  }
508
551
  if (!traces) {
509
- return callback(new TypeError('must pass traces to send'))
552
+ callback(new TypeError('must pass traces to send'))
553
+ return
510
554
  }
511
555
 
512
556
  this._sendData(this._methods.traces, traces, callback)
@@ -521,16 +565,16 @@ function transactionSampleData(traces, callback) {
521
565
  * 1. An array of logs.
522
566
  * 2. A list of common attributes(optional).
523
567
  *
524
- * @param {Array} logsRecords The encoded log entries.
525
- * @param logRecords
526
- * @param {Function} callback The continuation / error handler.
568
+ * @param {Array} logRecords The encoded log entries.
569
+ * @param {Function} callback The continuation / error handler.
527
570
  */
528
571
  CollectorAPI.prototype.log_event_data = function logEventData(logRecords, callback) {
529
572
  if (!callback) {
530
- throw new TypeError('callback is required')
573
+ this._throwCallbackError()
531
574
  }
532
575
  if (!logRecords) {
533
- return callback(new TypeError('must pass logRecords to send'))
576
+ callback(new TypeError('must pass logRecords to send'))
577
+ return
534
578
  }
535
579
 
536
580
  this._sendData(this._methods.logEvents, logRecords, callback)
@@ -540,12 +584,11 @@ CollectorAPI.prototype.log_event_data = function logEventData(logRecords, callba
540
584
  * Sends no data aside from the message itself. Clears the run ID, which
541
585
  * effectively disconnects the agent from the collector.
542
586
  *
543
- * @param Function callback Runs after the run ID has been cleared.
544
- * @param callback
587
+ * @param {Function} callback Runs after the run ID has been cleared.
545
588
  */
546
589
  CollectorAPI.prototype.shutdown = function shutdown(callback) {
547
590
  if (!callback) {
548
- throw new TypeError('callback is required')
591
+ this._throwCallbackError()
549
592
  }
550
593
 
551
594
  logger.info('Shutting down collector.')
@@ -554,8 +597,8 @@ CollectorAPI.prototype.shutdown = function shutdown(callback) {
554
597
  this._methods.shutdown.invoke(null, this._reqHeadersMap, onShutdown)
555
598
 
556
599
  /**
557
- * @param error
558
- * @param response
600
+ * @param {Error} error response error
601
+ * @param {http.ServerResponse} response response from collector
559
602
  */
560
603
  function onShutdown(error, response) {
561
604
  if (error) {
@@ -593,7 +636,8 @@ CollectorAPI.prototype._runLifecycle = function _runLifecycle(method, body, call
593
636
  const api = this
594
637
  method.invoke(body, this._reqHeadersMap, function standardHandler(error, response) {
595
638
  if (error) {
596
- return callback(error)
639
+ callback(error)
640
+ return
597
641
  }
598
642
 
599
643
  return api._handleResponseCode(response, method.name, callback)
@@ -620,6 +664,7 @@ CollectorAPI.prototype._sendData = function _sendData(method, data, callback) {
620
664
 
621
665
  // TODO: log the payload if exists?
622
666
 
667
+ /*
623
668
  if (response.agentRun === AGENT_RUN_BEHAVIOR.SHUTDOWN) {
624
669
  // TODO: for now, shut handled in _handleResponseCode for consistency
625
670
  // either find way to safely change while side-by-side or move
@@ -635,7 +680,8 @@ CollectorAPI.prototype._sendData = function _sendData(method, data, callback) {
635
680
  // })
636
681
 
637
682
  callback(null, { retainData: response.retainData })
638
- } else if (response.agentRun === AGENT_RUN_BEHAVIOR.RESTART) {
683
+ */
684
+ if (response.agentRun === AGENT_RUN_BEHAVIOR.RESTART) {
639
685
  // TODO: almost seems better to leg aggregator finish (cb) then restart?
640
686
 
641
687
  // TODO: ensure harvesting stopped for all other endpoints. same for shutdown.
@@ -666,60 +712,55 @@ CollectorAPI.prototype.isConnected = function isConnected() {
666
712
  return !!this._agent.config.run_id
667
713
  }
668
714
 
715
+ CollectorAPI.prototype._handleResponseCode = _handleResponseCode
716
+
669
717
  /**
670
718
  * Returns appropriate CollectorResponse object according to response code.
671
719
  *
672
- * @param {object} response
720
+ * @param {http.ServerResponse} response response from collector
673
721
  * @param {number} response.status - Status code from collector response
674
722
  * @param {object} response.payload - Parsed response body, if any
675
723
  * @param {string} endpoint - Collector endpoint name
676
724
  * @param {Function} cb - CollectorAPI method invocation callback
677
725
  */
678
- CollectorAPI.prototype._handleResponseCode = _handleResponseCode
679
- /**
680
- * @param response
681
- * @param endpoint
682
- * @param cb
683
- */
684
726
  function _handleResponseCode(response, endpoint, cb) {
685
727
  const code = response.status
686
728
 
687
729
  /* eslint-disable padded-blocks */
688
730
  if (SUCCESS.has(code)) {
689
731
  // The request was a success!
690
- return setImmediate(cb, null, CollectorResponse.success(response.payload))
732
+ setImmediate(cb, null, CollectorResponse.success(response.payload))
691
733
  } else if (RESTART.has(code)) {
692
734
  // The agent needs to disconnect and restart.
693
735
  logFailure(endpoint, code, 'Restarting')
694
- return setImmediate(cb, null, CollectorResponse.reconnect(0, null))
736
+ setImmediate(cb, null, CollectorResponse.reconnect(0, null))
695
737
  } else if (FAILURE_DISCARD_DATA.has(code)) {
696
738
  // Something was wrong with our payload so we must delete our data.
697
739
  logFailure(endpoint, code, 'Discarding harvest data')
698
- return setImmediate(cb, null, CollectorResponse.discard(null))
740
+ setImmediate(cb, null, CollectorResponse.discard(null))
699
741
  } else if (FAILURE_SAVE_DATA.has(code)) {
700
742
  // Something was wrong with the request, but it wasn't our fault. We'll try again.
701
743
  logFailure(endpoint, code, 'Retaining data for next harvest')
702
- return setImmediate(cb, null, CollectorResponse.error(response.payload))
744
+ setImmediate(cb, null, CollectorResponse.error(response.payload))
703
745
  } else if (code === 410) {
704
746
  // New Relic doesn't like us and we shouldn't try to talk to them any more.
705
747
  logFailure(endpoint, code, 'Disconnecting from New Relic')
706
748
 
707
- return this._agent.stop(function onShutdown() {
749
+ this._agent.stop(function onShutdown() {
708
750
  cb(null, CollectorResponse.fatal(response.payload))
709
751
  })
752
+ } else {
753
+ // We're not sure what New Relic is trying to tell us. Let's get rid of our
754
+ // data just in case it is our fault.
755
+ logger.error('Agent endpoint %s returned unexpected status %s.', endpoint, code)
756
+ setImmediate(cb, null, CollectorResponse.discard(null))
710
757
  }
711
- /* eslint-enable padded-blocks */
712
-
713
- // We're not sure what New Relic is trying to tell us. Let's get rid of our
714
- // data just in case it is our fault.
715
- logger.error('Agent endpoint %s returned unexpected status %s.', endpoint, code)
716
- return setImmediate(cb, null, CollectorResponse.discard(null))
717
758
  }
718
759
 
719
760
  /**
720
- * @param endpoint
721
- * @param code
722
- * @param action
761
+ * @param {string} endpoint called endpoint
762
+ * @param {string} code http status code
763
+ * @param {string} action describes collector action
723
764
  */
724
765
  function logFailure(endpoint, code, action) {
725
766
  logger.error('Agent endpoint %s returned %s status. %s.', endpoint, code, action)