newrelic 6.7.0 → 6.10.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.
package/NEWS.md CHANGED
@@ -1,3 +1,86 @@
1
+ ### 6.10.0 (2020-06-22):
2
+
3
+ * Additional Transaction Information applied to Span Events
4
+ * When Distributed Tracing and/or Infinite Tracing are enabled, the Agent will now incorporate additional information from the Transaction Event on to the currently available Span Event of the transaction.
5
+ * The following items are affected:
6
+ * `aws-lambda` related attributes
7
+ * `error.message`
8
+ * `error.class`
9
+ * `error.expected`
10
+ * `http.statusCode`
11
+ * `http.statusText`
12
+ * `message.*`
13
+ * `parent.type`
14
+ * `parent.app`
15
+ * `parent.account`
16
+ * `parent.transportType`
17
+ * `parent.transportDuration`
18
+ * Request Parameters `request.parameters.*`
19
+ * `request.header.*`
20
+ * `request.method`
21
+ * `request.uri`
22
+ * Custom Attributes
23
+ * Custom transaction attributes added via `API.addCustomAttribute` or `API.addCustomAttributes` will now be propagated to the currently active span, if available.
24
+ * **Security Recommendation:**
25
+ * Review your Transaction Event attributes configuration. Any attribute include or exclude setting specific to Transaction Events should be applied to your Span Attributes configuration or global attributes configuration. Please see [Node.js agent attributes](https://docs.newrelic.com/docs/agents/nodejs-agent/attributes/nodejs-agent-attributes#configure-attributes) for more on how to configure.
26
+ * Upgraded @grpc/grpc-js from 1.0.3 to 1.0.4
27
+ * Modified redis callback-less versioned test to use `commandQueueLength` as indicator redis command has completed and test can continue. This is in effort to further reduce these test flickers. Additionally, added wait for client 'ready' before moving on to tests.
28
+ * Updated force secret test runs to run on branch pushes to the main repository.
29
+
30
+ ### 6.9.0 (2020-06-08):
31
+
32
+ * Added AWS API Gateway V2 Support to lambda instrumentation.
33
+
34
+ * Added 'transaction.name' intrinsic to active span at time transaction name is finalized.
35
+
36
+ This enables finding transaction name for traces that may not have a matching transaction event.
37
+
38
+ * Added 'error.expected' attribute to span active at time expected error was noticed.
39
+
40
+ * Dropped errors earlier during collection when error collection is disabled.
41
+
42
+ Error attributes will no longer show up on spans when error collection has been disabled. Other unnecessary work will also be avoided.
43
+
44
+ * Removed allocation of logging-only objects used by transaction naming when those log levels are disabled.
45
+
46
+ * Upgraded escodegen from 1.12.0 to 1.14.1.
47
+
48
+ * Upgraded readable-stream from 3.4.0 to 3.6.0.
49
+
50
+ * Upgraded @grpc/proto-loader from 0.5.3 to 0.5.4.
51
+
52
+ * Converted facts unit test to use tap API.
53
+
54
+ * Converted transaction 'finalizeName...' unit tests to use tap API.
55
+
56
+ * Added several items to .npmignore to prevent accidental publishing.
57
+
58
+ * Fixed Redis client w/o callback versioned test flicker.
59
+
60
+ Doesn't end transaction until error encountered. Increases wait time for first operation which has to complete for the second operation to be successful.
61
+
62
+ ### 6.8.0 (2020-05-21):
63
+
64
+ * Bumped @newrelic/native-metrics to ^5.1.0.
65
+
66
+ Upgraded nan to ^2.14.1 to resolve 'GetContents' deprecation warning with Node 14. This version of the native metrics module is tested against Node 14 and includes a pre-built binary download backup for Node 14.
67
+
68
+ * Added whitespace trimming of license key configuration values.
69
+
70
+ Previously, when a license key was entered with leading or trailing whitespace, it would be used as-is and result in a validation failure. This most commonly occurred with environment variable based configuration.
71
+
72
+ * Moved to GitHub actions for CI.
73
+
74
+ * Updated PR template and added initial issue templates.
75
+
76
+ * Converted most of the collector API unit tests to use the tap API. Split larger test groupings into their own test files.
77
+
78
+ ### 6.7.1 (2020-05-14):
79
+
80
+ * Added synthetics headers to transaction event intrinsics for DT
81
+
82
+ * Fixed stale comment documentation with regards to segment recording
83
+
1
84
  ### 6.7.0 (2020-05-06):
2
85
 
3
86
  * Added a configurable-length span queue to Infinite Tracing:
package/README.md CHANGED
@@ -742,3 +742,5 @@ license and the licenses of its dependencies.
742
742
  [6]: https://github.com/newrelic/node-newrelic/blob/master/lib/config/default.js
743
743
  [7]: https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration
744
744
 
745
+
746
+
@@ -18,6 +18,8 @@ can be found at https://github.com/newrelic/node-newrelic.
18
18
  * [json-stringify-safe](#json-stringify-safe)
19
19
  * [readable-stream](#readable-stream)
20
20
  * [semver](#semver)
21
+ * [@grpc/grpc-js](#@grpc/grpc-js)
22
+ * [@grpc/proto-loader](#@grpc/proto-loader)
21
23
 
22
24
  ## async
23
25
 
package/api.js CHANGED
@@ -272,7 +272,7 @@ API.prototype.setControllerName = function setControllerName(name, action) {
272
272
  * @param {string} value The value you want displayed. Must be serializable.
273
273
  */
274
274
  API.prototype.addCustomAttribute = function addCustomAttribute(key, value) {
275
- var metric = this.agent.metrics.getOrCreateMetric(
275
+ const metric = this.agent.metrics.getOrCreateMetric(
276
276
  NAMES.SUPPORTABILITY.API + '/addCustomAttribute'
277
277
  )
278
278
  metric.incrementCallCount()
@@ -291,24 +291,36 @@ API.prototype.addCustomAttribute = function addCustomAttribute(key, value) {
291
291
  return false
292
292
  }
293
293
 
294
- var transaction = this.agent.tracer.getTransaction()
294
+ const transaction = this.agent.tracer.getTransaction()
295
295
  if (!transaction) {
296
- return logger.warn('No transaction found for custom attributes.')
296
+ logger.warn('No transaction found for custom attributes.')
297
+ return false
297
298
  }
298
299
 
299
- var trace = transaction.trace
300
+ const trace = transaction.trace
300
301
  if (!trace.custom) {
301
- return logger.warn(
302
+ logger.warn(
302
303
  'Could not add attribute %s to nonexistent custom attributes.',
303
304
  key
304
305
  )
306
+ return false
305
307
  }
306
308
 
307
309
  if (CUSTOM_DENYLIST.has(key)) {
308
- return logger.warn('Not overwriting value of NR-only attribute %s.', key)
310
+ logger.warn('Not overwriting value of NR-only attribute %s.', key)
311
+ return false
309
312
  }
310
313
 
311
314
  trace.addCustomAttribute(key, value)
315
+
316
+ const spanContext = this.agent.tracer.getSpanContext()
317
+ if (!spanContext) {
318
+ logger.debug('No span found for custom attributes.')
319
+ // success/failure is ambiguous here. since at least 1 attempt tried, not returning false
320
+ return
321
+ }
322
+
323
+ spanContext.addCustomAttribute(key, value, spanContext.ATTRIBUTE_PRIORITY.LOW)
312
324
  }
313
325
 
314
326
  /**
@@ -395,20 +407,22 @@ API.prototype.addCustomSpanAttribute = function addCustomSpanAttribute(key, valu
395
407
  return false
396
408
  }
397
409
 
398
- const segment = this.agent.tracer.getSegment()
410
+ const spanContext = this.agent.tracer.getSpanContext()
399
411
 
400
- if (!segment) {
401
- return logger.debug(
402
- 'Could not add attribute %s. No available span/segment.',
412
+ if (!spanContext) {
413
+ logger.debug(
414
+ 'Could not add attribute %s. No available span.',
403
415
  key
404
416
  )
417
+ return false
405
418
  }
406
419
 
407
420
  if (CUSTOM_DENYLIST.has(key)) {
408
- return logger.warn('Not overwriting value of NR-only attribute %s.', key)
421
+ logger.warn('Not overwriting value of NR-only attribute %s.', key)
422
+ return false
409
423
  }
410
424
 
411
- segment.addCustomSpanAttribute(key, value)
425
+ spanContext.addCustomAttribute(key, value)
412
426
  }
413
427
 
414
428
  API.prototype.setIgnoreTransaction = util.deprecate(
@@ -21,5 +21,14 @@ fi
21
21
 
22
22
  export AGENT_PATH=`pwd`
23
23
 
24
+ # @koa/router 8.03+ breaks segment naming for nested routes
25
+ skip="koa"
26
+
27
+ # Don't run the aws-sdk tests if we don't have the keys set
28
+ if [[ -z "$AWS_ACCESS_KEY_ID" ]]; then
29
+ skip="${skip},aws-sdk"
30
+ fi
31
+
32
+
24
33
  # This is meant to be temporary. Remove once new major version with fixes rolled into agent.
25
- time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 -s koa ${directories[@]}
34
+ time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 -s $skip ${directories[@]}
package/bin/ssl.sh CHANGED
@@ -8,7 +8,7 @@ set -x # be chatty and show the lines we're running
8
8
  # that are mysterious and not understood, so let
9
9
  # bail early if we detect that's the case
10
10
  ENGINE_OPENSSL=`openssl version | awk '{print $(1)}'`
11
- if [ "$ENGINE_OPENSSL" == "LibreSSL" ]
11
+ if [ "$ENGINE_OPENSSL" = "LibreSSL" ]
12
12
  then
13
13
  echo "LibreSSL is not supported, please install a stock openssl and \n"
14
14
  echo "make sure that openssl binary is in your PATH"
@@ -25,7 +25,7 @@ CERTIFICATE="test/lib/self-signed-test-certificate.crt"
25
25
 
26
26
  # USAGE: ./bin/ssl.sh clear
27
27
  # a sub command to remove all the generated files and start over
28
- if [ "$1" == "clear" ]
28
+ if [ "$1" = "clear" ]
29
29
  then
30
30
  rm $SSLKEY
31
31
  rm $CACERT
@@ -37,7 +37,7 @@ fi
37
37
 
38
38
  # if there's already a certificate, then exit, but
39
39
  # exit with a success code so build continue
40
- if [ -a $CERTIFICATE ]; then
40
+ if [ -e $CERTIFICATE ]; then
41
41
  exit 0;
42
42
  fi
43
43
 
@@ -281,6 +281,11 @@ Config.prototype._fromServer = function _fromServer(params, key) {
281
281
  this.run_id = params.agent_run_id
282
282
  break
283
283
 
284
+ // if it's undefined or null, so be it
285
+ case 'request_headers_map':
286
+ this.request_headers_map = params.request_headers_map
287
+ break
288
+
284
289
  // handled by config.onConnect
285
290
  case 'high_security':
286
291
  break
@@ -1323,6 +1328,10 @@ Config.prototype._canonicalize = function _canonicalize() {
1323
1328
 
1324
1329
  this.serverless_mode.enabled = this.serverless_mode.enabled
1325
1330
  && this.feature_flag.serverless_mode
1331
+
1332
+ if (this.license_key) {
1333
+ this.license_key = this.license_key.trim()
1334
+ }
1326
1335
  }
1327
1336
 
1328
1337
  function _parseCodes(codes) {
@@ -193,6 +193,12 @@ class ErrorCollector {
193
193
  return
194
194
  }
195
195
 
196
+ const shouldCollectErrors = this._shouldCollectErrors()
197
+ if (!shouldCollectErrors) {
198
+ logger.trace('error_collector.enabled is false, dropping application error.')
199
+ return
200
+ }
201
+
196
202
  if (errorHelper.shouldIgnoreError(transaction, error, this.config)) {
197
203
  logger.trace('Ignoring error')
198
204
  return
@@ -223,7 +229,15 @@ class ErrorCollector {
223
229
  * @param {Exception} exception The Exception to be traced.
224
230
  */
225
231
  addUserError(transaction, error, customAttributes) {
226
- if (!error) return
232
+ if (!error) {
233
+ return
234
+ }
235
+
236
+ const shouldCollectErrors = this._shouldCollectErrors()
237
+ if (!shouldCollectErrors) {
238
+ logger.trace('error_collector.enabled is false, dropping user reported error.')
239
+ return
240
+ }
227
241
 
228
242
  const timestamp = Date.now()
229
243
  const exception = new Exception({error, timestamp, customAttributes})
@@ -270,18 +284,6 @@ class ErrorCollector {
270
284
  return false
271
285
  }
272
286
 
273
- // allow enabling & disabling the error tracer at runtime
274
- // TODO: it would be better to check config in the public add() to prevents collecting
275
- // errors on the transaction unnecessarily. Should we allow the work above or
276
- // short-circuit sooner?
277
- if (
278
- !this.config.collect_errors ||
279
- !this.config.error_collector ||
280
- !this.config.error_collector.enabled
281
- ) {
282
- return false
283
- }
284
-
285
287
  if (exception.error) {
286
288
  logger.trace(exception.error, 'Got exception to trace:')
287
289
  }
@@ -304,7 +306,10 @@ class ErrorCollector {
304
306
  }
305
307
  }
306
308
 
307
- this.traceAggregator.add(errorTrace)
309
+ // defaults true in config/index. can be modified server-side
310
+ if (this.config.collect_errors) {
311
+ this.traceAggregator.add(errorTrace)
312
+ }
308
313
 
309
314
  if (this.config.error_collector.capture_events === true) {
310
315
  const priority = transaction && transaction.priority || Math.random()
@@ -328,6 +333,17 @@ class ErrorCollector {
328
333
  this.seenObjectsByTransaction = Object.create(null)
329
334
  }
330
335
 
336
+ _shouldCollectErrors() {
337
+ const errorCollectorEnabled =
338
+ this.config.error_collector && this.config.error_collector.enabled
339
+
340
+ const shouldCaptureTraceOrEvent =
341
+ this.config.collect_errors || // are traces enabled
342
+ (this.config.error_collector && this.config.error_collector.capture_events)
343
+
344
+ return errorCollectorEnabled && shouldCaptureTraceOrEvent
345
+ }
346
+
331
347
  reconfigure(config) {
332
348
  this.config = config
333
349
 
@@ -19,10 +19,7 @@ const protoOptions = {keepCase: true,
19
19
  const pathProtoDefinition = __dirname +
20
20
  '../../../lib/grpc/endpoints/infinite-tracing/v1.proto'
21
21
 
22
- const defaultBackoffOpts = {
23
- initialSeconds: 0,
24
- seconds:15
25
- }
22
+ const DEFAULT_RECONNECT_DELAY_MS = 15 * 1000
26
23
 
27
24
  /**
28
25
  * Class for managing the GRPC connection
@@ -41,31 +38,21 @@ class GrpcConnection extends EventEmitter {
41
38
  *
42
39
  * @param {Object} traceObserverConfig config item config.infinite_tracing.trace_observer
43
40
  * @param {MetricAggregator} metrics metric aggregator, for supportability metrics
44
- * @param {Object} backoffOpts allows injecting specific backoff times
41
+ * @param {Number} [reconnectDelayMs=15000] number of milliseconds to wait before reconnecting
42
+ * for error states that require a reconnect delay.
45
43
  */
46
- constructor(traceObserverConfig, metrics, backoffOpts = defaultBackoffOpts) {
44
+ constructor(traceObserverConfig, metrics, reconnectDelayMs) {
47
45
  super()
48
46
 
49
- // only set opts if the object is valid
50
- if ( (backoffOpts.initialSeconds || backoffOpts.initialSeconds === 0) &&
51
- (backoffOpts.seconds || backoffOpts.seconds === 0)) {
52
- this._backoffOpts = backoffOpts
53
- } else {
54
- this._backoffOpts = defaultBackoffOpts
55
- logger.trace(
56
- 'invalid backoff information passed to constructor, using default values'
57
- )
58
- }
47
+ this._reconnectDelayMs = reconnectDelayMs || DEFAULT_RECONNECT_DELAY_MS
59
48
 
60
- // initial "stream connection" has a 0 second backoff, unless
61
- // different values are injected via _backoffOpts
62
- this._streamBackoffSeconds = this._backoffOpts.initialSeconds
63
49
  this._metrics = metrics
64
50
  this._setState(connectionStates.disconnected)
65
51
 
66
52
  this._endpoint = this.getTraceObserverEndpoint(traceObserverConfig)
67
53
  this._licensekey = null
68
54
  this._runId = null
55
+ this._requestHeadersMap = null
69
56
 
70
57
  this.stream = null
71
58
  }
@@ -79,11 +66,13 @@ class GrpcConnection extends EventEmitter {
79
66
  * @param {string} endpoint the GRPC server's endpoint
80
67
  * @param {string} license_key the agent license key
81
68
  * @param {string} run_id the current agent run id (also called agent run token)
69
+ * @param {object} requestHeadersMap request headers map received from server connect.
82
70
  * @param {string} [rootCerts] string of root (ca) certificates to attach to the connection.
83
71
  */
84
- setConnectionDetails(license_key, run_id, rootCerts) {
72
+ setConnectionDetails(license_key, run_id, requestHeadersMap, rootCerts) {
85
73
  this._licenseKey = license_key
86
74
  this._runId = run_id
75
+ this._requestHeadersMap = requestHeadersMap
87
76
  this._rootCerts = rootCerts
88
77
 
89
78
  return this
@@ -118,35 +107,21 @@ class GrpcConnection extends EventEmitter {
118
107
  }
119
108
 
120
109
  this._setState(connectionStates.connecting)
121
- logger.trace('connecting to grpc endpoint in [%s] seconds', this._getBackoffSeconds())
122
-
123
- setTimeout(() => {
124
- try {
125
- this.stream = this._connectSpans(this._endpoint, this._licenseKey, this._runId)
110
+ logger.trace('Connecting to gRPC endpoint.')
126
111
 
127
- // May not actually be "connected" at this point but we can write to the stream
128
- // immediately.
129
- this._setState(connectionStates.connected, this.stream)
112
+ try {
113
+ this.stream = this._connectSpans()
130
114
 
131
- // any future stream connect/disconnect after initial connection
132
- // should have a 15 second backoff
133
- this._setStreamBackoffAfterInitialStreamSetup()
134
- } catch (err) {
135
- logger.trace(
136
- err,
137
- 'Unexpected error establishing GRPC stream, will not attempt reconnect.'
138
- )
139
- this._disconnectWithoutReconnect()
140
- }
141
- }, this._getBackoffSeconds() * 1000)
142
- }
143
-
144
- _setStreamBackoffAfterInitialStreamSetup() {
145
- this._streamBackoffSeconds = this._backoffOpts.seconds
146
- }
147
-
148
- _setStreamBackoffToInitialValue() {
149
- this._streamBackoffSeconds = this._backoffOpts.initialSeconds
115
+ // May not actually be "connected" at this point but we can write to the stream
116
+ // immediately.
117
+ this._setState(connectionStates.connected, this.stream)
118
+ } catch (err) {
119
+ logger.trace(
120
+ err,
121
+ 'Unexpected error establishing gRPC stream, will not attempt reconnect.'
122
+ )
123
+ this._disconnect()
124
+ }
150
125
  }
151
126
 
152
127
  /**
@@ -159,14 +134,7 @@ class GrpcConnection extends EventEmitter {
159
134
  return
160
135
  }
161
136
 
162
- this._disconnectWithoutReconnect()
163
- }
164
-
165
- /**
166
- * Calculates backoff seconds
167
- */
168
- _getBackoffSeconds() {
169
- return this._streamBackoffSeconds
137
+ this._disconnect()
170
138
  }
171
139
 
172
140
  /**
@@ -175,11 +143,19 @@ class GrpcConnection extends EventEmitter {
175
143
  * @param {string} license_key
176
144
  * @param {string} run_id
177
145
  */
178
- _getMetadata(license_key, run_id, env) {
146
+ _getMetadata(license_key, run_id, requestHeadersMap, env) {
179
147
  const metadata = new grpc.Metadata()
180
148
  metadata.add('license_key', license_key)
181
149
  metadata.add('agent_run_token', run_id)
182
150
 
151
+ // p17 spec: If request_headers_map is empty or absent,
152
+ // the agent SHOULD NOT apply anything to its requests.
153
+ if (requestHeadersMap) {
154
+ for (const [key, value] of Object.entries(requestHeadersMap)) {
155
+ metadata.add(key.toLowerCase(), value) // keys MUST be lowercase for Infinite Tracing
156
+ }
157
+ }
158
+
183
159
  // check environment variables for testing parameters and
184
160
  // pass to server via meta-data.
185
161
  const flaky = parseInt(env.NEWRELIC_GRPCCONNECTION_METADATA_FLAKY, 10)
@@ -197,18 +173,23 @@ class GrpcConnection extends EventEmitter {
197
173
  }
198
174
 
199
175
  /**
200
- * Sets internal object state to disconnected initiates a new connection
201
- *
202
- * Called when we receive a stream status that indicates a potential
203
- * problem with the stream. We set the state (which will emit an event),
204
- * increment tries to honor the backoff, and then reconnect.
176
+ * Disconnects from gRPC endpoint and schedules establishing a new connection.
177
+ * @param {number} reconnectDelayMs number of milliseconds to wait before reconnecting.
205
178
  */
206
- _reconnect() {
179
+ _reconnect(reconnectDelayMs = 0) {
207
180
  this._disconnect()
208
- this.connectSpans()
181
+
182
+ logger.trace('Reconnecting to gRPC endpoint in [%s] seconds', reconnectDelayMs)
183
+
184
+ setTimeout(
185
+ this.connectSpans.bind(this),
186
+ reconnectDelayMs
187
+ )
209
188
  }
210
189
 
211
190
  _disconnect() {
191
+ logger.trace('Disconnecting from gRPC endpoint.')
192
+
212
193
  if (this.stream) {
213
194
  this.stream.removeAllListeners()
214
195
 
@@ -221,17 +202,6 @@ class GrpcConnection extends EventEmitter {
221
202
  this._setState(connectionStates.disconnected)
222
203
  }
223
204
 
224
- /**
225
- * Disconnects Without Reconnect
226
- *
227
- * Certain GRPC statuses require us to disconnect and _not_
228
- * attempt a stream reconnect.
229
- */
230
- _disconnectWithoutReconnect() {
231
- this._disconnect()
232
- this._setStreamBackoffToInitialValue()
233
- }
234
-
235
205
  /**
236
206
  * Central location to setup stream observers
237
207
  *
@@ -244,14 +214,14 @@ class GrpcConnection extends EventEmitter {
244
214
  // listen for responses from server and log
245
215
  if (logger.traceEnabled()) {
246
216
  stream.on('data', function data(response) {
247
- logger.trace("grpc span response stream: %s", JSON.stringify(response))
217
+ logger.trace("gRPC span response stream: %s", JSON.stringify(response))
248
218
  })
249
219
  }
250
220
 
251
221
  // listen for status that indicate stream has ended,
252
222
  // and we need to disconnect
253
223
  stream.on('status', (grpcStatus) => {
254
- logger.trace('GRPC Status Received [%s]: %s', grpcStatus.code, grpcStatus.details)
224
+ logger.trace('gRPC Status Received [%s]: %s', grpcStatus.code, grpcStatus.details)
255
225
  const grpcStatusName =
256
226
  grpc.status[grpcStatus.code] ? grpc.status[grpcStatus.code] : 'UNKNOWN'
257
227
 
@@ -263,19 +233,15 @@ class GrpcConnection extends EventEmitter {
263
233
  // per the spec, An UNIMPLEMENTED status code from gRPC indicates
264
234
  // that the versioned Trace Observer is no longer available. Agents
265
235
  // MUST NOT attempt to reconnect in this case
266
- this._disconnectWithoutReconnect()
236
+ this._disconnect()
237
+ } else if (grpc.status[grpc.status.OK] === grpcStatusName) {
238
+ this._reconnect()
267
239
  } else {
268
- // all statuses are treated as "bad stuff" happened, and
269
- // as a signal we need to reconnect (except UNIMPLEMENTED).
270
- // Even an "OK status" indicates the call completed successfully,
271
- // which indicates the stream is over.
272
- if (grpc.status[grpc.status.OK] !== grpcStatusName) {
273
- this._metrics.getOrCreateMetric(
274
- util.format(NAMES.INFINITE_TRACING.SPAN_RESPONSE_GRPC_STATUS, grpcStatusName)
275
- ).incrementCallCount()
276
- }
240
+ this._metrics.getOrCreateMetric(
241
+ util.format(NAMES.INFINITE_TRACING.SPAN_RESPONSE_GRPC_STATUS, grpcStatusName)
242
+ ).incrementCallCount()
277
243
 
278
- this._reconnect()
244
+ this._reconnect(this._reconnectDelayMs)
279
245
  }
280
246
  })
281
247
 
@@ -322,7 +288,7 @@ class GrpcConnection extends EventEmitter {
322
288
  * this makes an http2 request with the metadata, and then returns
323
289
  * a stream for further writing.
324
290
  */
325
- _connectSpans(endpoint, license_key, run_id) {
291
+ _connectSpans() {
326
292
  const packageDefinition = protoLoader.loadSync(pathProtoDefinition, protoOptions)
327
293
 
328
294
  const serviceDefinition = grpc.loadPackageDefinition(
@@ -331,14 +297,18 @@ class GrpcConnection extends EventEmitter {
331
297
 
332
298
  const credentials = this._generateCredentials(grpc)
333
299
  const client = new serviceDefinition.IngestService(
334
- endpoint,
300
+ this._endpoint,
335
301
  credentials
336
302
  )
337
- const metadata = this._getMetadata(license_key, run_id, process.env)
303
+
304
+ const metadata =
305
+ this._getMetadata(this._licenseKey, this._runId, this._requestHeadersMap, process.env)
306
+
338
307
  const stream = client.recordSpan(metadata)
339
308
  this._setupSpanStreamObservers(stream)
340
309
 
341
310
  return stream
342
311
  }
343
312
  }
313
+
344
314
  module.exports = GrpcConnection
@@ -110,6 +110,8 @@ function _collectHeaders(headers, nameMap, prefix, transaction) {
110
110
  ? COLLECTED_REQUEST_HEADERS
111
111
  : Object.keys(headers)
112
112
 
113
+ const segment = transaction.agent.tracer.getSegment()
114
+
113
115
  for (var i = 0; i < headerKeys.length; i++) {
114
116
  var headerKey = headerKeys[i]
115
117
  var header = headers[headerKey]
@@ -128,6 +130,8 @@ function _collectHeaders(headers, nameMap, prefix, transaction) {
128
130
  attributeName,
129
131
  header
130
132
  )
133
+
134
+ segment.addSpanAttribute(attributeName, header)
131
135
  }
132
136
  }
133
137
  }
@@ -133,7 +133,7 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
133
133
  if (parsed.parameters) {
134
134
  // Scrub and parse returns on object with a null prototype.
135
135
  for (let key in parsed.parameters) { // eslint-disable-line guard-for-in
136
- segment.addAttribute(`request.parameters.${key}`, parsed.parameters[key])
136
+ segment.addSpanAttribute(`request.parameters.${key}`, parsed.parameters[key])
137
137
  }
138
138
  }
139
139
  segment.addAttribute('url', `${proto}//${hostname}${parsed.path}`)
@@ -196,8 +196,8 @@ function handleError(segment, req, error) {
196
196
  */
197
197
  function handleResponse(segment, hostname, req, res) {
198
198
  // Add response attributes for spans
199
- segment.addAttribute('http.statusCode', res.statusCode)
200
- segment.addAttribute('http.statusText', res.statusMessage)
199
+ segment.addSpanAttribute('http.statusCode', res.statusCode)
200
+ segment.addSpanAttribute('http.statusText', res.statusMessage)
201
201
 
202
202
  // If CAT is enabled, grab those headers!
203
203
  const agent = segment.transaction.agent