newrelic 5.3.0 → 5.6.2

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.
@@ -140,6 +140,7 @@ function Config(config) {
140
140
  // product-level related
141
141
  this.collect_traces = true
142
142
  this.collect_errors = true
143
+ this.collect_span_events = true
143
144
 
144
145
  // override options for utilization stats
145
146
  this.utilization.logical_processors = null
@@ -320,15 +321,25 @@ Config.prototype._fromServer = function _fromServer(params, key) {
320
321
  case 'collect_analytics_events':
321
322
  // never enable from server-side
322
323
  // but we allow the server to disable
323
- if (params.collect_analytics_events === false)
324
+ if (params.collect_analytics_events === false) {
324
325
  this.transaction_events.enabled = false
326
+ }
325
327
  break
326
328
 
327
329
  case 'collect_custom_events':
328
330
  // never enable from server-side
329
331
  // but we allow the server to disable
330
- if (params.collect_custom_events === false)
332
+ if (params.collect_custom_events === false) {
331
333
  this.custom_insights_events.enabled = false
334
+ }
335
+ break
336
+
337
+ case 'collect_span_events':
338
+ // never enable from server-side
339
+ // but we allow the server to disable
340
+ if (params.collect_span_events === false) {
341
+ this.span_events.enabled = false
342
+ }
332
343
  break
333
344
 
334
345
  case 'allow_all_headers':
@@ -924,7 +935,7 @@ Config.prototype._loggingManuallySet = function _loggingManuallySet(inputConfig)
924
935
  * @param {*} inputConfig configuration passed to the Config constructor
925
936
  */
926
937
  Config.prototype._enforceServerless = function _enforceServerless(inputConfig) {
927
- const DT_KEYS = ['trusted_account_key', 'application_id', 'account_id']
938
+ const DT_KEYS = ['trusted_account_key', 'primary_application_id', 'account_id']
928
939
  if (this.serverless_mode.enabled) {
929
940
  // Explicitly disable old CAT in serverless_mode
930
941
  if (this.cross_application_tracer.enabled) {
@@ -1267,7 +1278,8 @@ Config.prototype.publicSettings = function publicSettings() {
1267
1278
  * config file.
1268
1279
  */
1269
1280
  function initialize(config) {
1270
- /* When the logger is required here, it bootstraps itself and then
1281
+ /**
1282
+ * When the logger is required here, it bootstraps itself and then
1271
1283
  * injects itself into this module's closure via setLogger on the
1272
1284
  * instance of the logger it creates.
1273
1285
  */
@@ -1342,16 +1354,39 @@ function _noConfigFile() {
1342
1354
  */
1343
1355
  function getOrCreateInstance() {
1344
1356
  if (_configInstance === null) {
1345
- _configInstance = initialize()
1357
+ try {
1358
+ _configInstance = initialize()
1359
+ } catch (err) {
1360
+ /* eslint-disable no-console */
1361
+ console.error('New Relic for Node.js is disabled due to an error:')
1362
+ console.error(err.stack)
1363
+ /* eslint-enable no-console */
1364
+
1365
+ // Config construction has potential to throw due to invalid settings.
1366
+ // This allows the agent to return a stub api without crashing the process.
1367
+ _configInstance = Object.assign(defaultConfig(), {agent_enabled: false})
1368
+ _configInstance.setLogger = Config.prototype.setLogger
1369
+ }
1346
1370
  }
1347
1371
  return _configInstance
1348
1372
  }
1349
1373
 
1374
+ function getInstance() {
1375
+ return _configInstance
1376
+ }
1377
+
1378
+ function createInstance(config) {
1379
+ _configInstance = initialize(config)
1380
+ return _configInstance
1381
+ }
1382
+
1350
1383
  /**
1351
1384
  * Preserve the legacy initializer, but also allow consumers to manage their
1352
1385
  * own configuration if they choose.
1353
1386
  */
1354
1387
  Config.initialize = initialize
1355
1388
  Config.getOrCreateInstance = getOrCreateInstance
1389
+ Config.getInstance = getInstance
1390
+ Config.createInstance = createInstance
1356
1391
 
1357
1392
  module.exports = Config
@@ -77,8 +77,7 @@ function createError(transaction, exception, customAttributes, config) {
77
77
  if (!config.high_security && config.api.custom_attributes_enabled && customAttributes) {
78
78
  for (var key in customAttributes) {
79
79
  if (props.hasOwn(customAttributes, key)) {
80
- var dest = config.attributeFilter.filter(
81
- 'transaction',
80
+ var dest = config.attributeFilter.filterTransaction(
82
81
  DESTINATIONS.ERROR_EVENT,
83
82
  key
84
83
  )
@@ -67,7 +67,6 @@ const REQUEST_HEADER_PREFIX = 'request.headers.'
67
67
  const RESPONSE_HEADER_PREFIX = 'response.headers.'
68
68
  const REQUEST_HEADER_NAMES = Object.create(null)
69
69
  const RESPONSE_HEADER_NAMES = Object.create(null)
70
- const TRANSACTION_SCOPE = 'transaction'
71
70
 
72
71
  _setHeaderAttrNames(REQUEST_HEADER_NAMES, REQUEST_HEADER_PREFIX)
73
72
  _setHeaderAttrNames(RESPONSE_HEADER_NAMES, RESPONSE_HEADER_PREFIX)
@@ -125,7 +124,6 @@ function _collectHeaders(headers, nameMap, prefix, transaction) {
125
124
 
126
125
  var attributeName = nameMap[headerKey] || prefix + _headerToCamelCase(headerKey)
127
126
  transaction.trace.attributes.addAttribute(
128
- TRANSACTION_SCOPE,
129
127
  DESTS.TRANS_COMMON,
130
128
  attributeName,
131
129
  header
@@ -61,10 +61,22 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
61
61
 
62
62
  const name = NAMES.EXTERNAL.PREFIX + hostname
63
63
 
64
+ const parent = agent.tracer.getSegment()
65
+ if (parent && parent.opaque) {
66
+ logger.trace(
67
+ 'Not capturing data for outbound request (%s) because parent segment opaque (%s)',
68
+ name,
69
+ parent.name
70
+ )
71
+
72
+ return makeRequest(opts)
73
+ }
74
+
75
+
64
76
  return agent.tracer.addSegment(
65
77
  name,
66
78
  recordExternal(hostname, 'http'),
67
- null,
79
+ parent,
68
80
  false,
69
81
  instrumentRequest
70
82
  )
@@ -32,7 +32,6 @@ const NEWRELIC_TRANSACTION_HEADER = 'x-newrelic-transaction'
32
32
  const NEWRELIC_SYNTHETICS_HEADER = 'x-newrelic-synthetics'
33
33
  const CONTENT_LENGTH_REGEX = /^Content-Length$/i
34
34
  const TRANSACTION_INFO_KEY = '__NR_transactionInfo'
35
- const TRANSACTION_SCOPE = 'transaction'
36
35
 
37
36
 
38
37
  // For incoming requests this instrumentation functions by wrapping
@@ -90,7 +89,6 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
90
89
 
91
90
  // URL is sent as an agent attribute with transaction events
92
91
  transaction.trace.attributes.addAttribute(
93
- TRANSACTION_SCOPE,
94
92
  DESTS.TRANS_EVENT | DESTS.ERROR_EVENT,
95
93
  'request.uri',
96
94
  transaction.url
@@ -185,7 +183,6 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
185
183
  if (response.statusCode != null) {
186
184
  var responseCode = String(response.statusCode)
187
185
  transaction.trace.attributes.addAttribute(
188
- TRANSACTION_SCOPE,
189
186
  DESTS.TRANS_COMMON,
190
187
  'httpResponseCode',
191
188
  responseCode
@@ -193,7 +190,6 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
193
190
 
194
191
  if (/^\d+$/.test(responseCode)) {
195
192
  transaction.trace.attributes.addAttribute(
196
- TRANSACTION_SCOPE,
197
193
  DESTS.TRANS_COMMON,
198
194
  'response.status',
199
195
  responseCode
@@ -202,7 +198,6 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
202
198
  }
203
199
  if (response.statusMessage !== undefined) {
204
200
  transaction.trace.attributes.addAttribute(
205
- TRANSACTION_SCOPE,
206
201
  DESTS.TRANS_COMMON,
207
202
  'httpResponseMessage',
208
203
  response.statusMessage
@@ -258,7 +253,6 @@ function initializeRequest(transaction, request) {
258
253
 
259
254
  if (request.method != null) {
260
255
  transaction.trace.attributes.addAttribute(
261
- TRANSACTION_SCOPE,
262
256
  DESTS.TRANS_COMMON,
263
257
  'request.method',
264
258
  request.method
@@ -46,11 +46,12 @@ module.exports = function initialize(agent, memcached, moduleName, shim) {
46
46
  // Capture connection info for datastore instance metric.
47
47
  var location = null
48
48
  if (typeof server === 'string') {
49
- location = server.split(':')
49
+ location = server
50
50
  } else if (this.HashRing && this.HashRing.get && metacall.key) {
51
- location = this.HashRing.get(metacall.key).split(':')
51
+ location = this.HashRing.get(metacall.key)
52
52
  }
53
53
  if (location) {
54
+ location = location.split(':')
54
55
  parameters.host = location[0]
55
56
  parameters.port_path_or_id = location[1]
56
57
  }
@@ -13,7 +13,7 @@ const ATTR_DEST = require('../config/attribute-filter').DESTINATIONS
13
13
  const COLD_START_KEY = 'aws.lambda.coldStart'
14
14
  const EVENT_SOURCE_ARN_KEY = 'aws.lambda.eventSource.arn'
15
15
  const NAMES = require('../metrics/names')
16
- const TRANSACTION_SCOPE = 'transaction'
16
+
17
17
 
18
18
  // A function with no references used to stub out closures
19
19
  function cleanClosure() {}
@@ -134,7 +134,6 @@ class AwsLambda {
134
134
 
135
135
  const awsAttributes = awsLambda._getAwsAgentAttributes(event, context)
136
136
  transaction.trace.attributes.addAttributes(
137
- TRANSACTION_SCOPE,
138
137
  ATTR_DEST.TRANS_COMMON,
139
138
  awsAttributes
140
139
  )
@@ -223,7 +222,6 @@ function setWebRequest(shim, transaction, request) {
223
222
  transaction.url = urltils.scrub(request.url.path)
224
223
  transaction.verb = request.method
225
224
  transaction.trace.attributes.addAttribute(
226
- TRANSACTION_SCOPE,
227
225
  ATTR_DEST.TRANS_COMMON,
228
226
  'request.method',
229
227
  request.method
@@ -234,7 +232,6 @@ function setWebRequest(shim, transaction, request) {
234
232
 
235
233
  // URL is sent as an agent attribute with transaction events
236
234
  transaction.trace.attributes.addAttribute(
237
- TRANSACTION_SCOPE,
238
235
  ATTR_DEST.TRANS_EVENT | ATTR_DEST.ERROR_EVENT,
239
236
  'request.uri',
240
237
  request.url.path
@@ -277,7 +274,6 @@ function setWebResponse(transaction, response) {
277
274
 
278
275
  const responseCode = String(response.statusCode)
279
276
  transaction.trace.attributes.addAttribute(
280
- TRANSACTION_SCOPE,
281
277
  ATTR_DEST.TRANS_COMMON,
282
278
  'httpResponseCode',
283
279
  responseCode
@@ -285,7 +281,6 @@ function setWebResponse(transaction, response) {
285
281
 
286
282
  if (/^\d+$/.test(responseCode)) {
287
283
  transaction.trace.attributes.addAttribute(
288
- TRANSACTION_SCOPE,
289
284
  ATTR_DEST.TRANS_COMMON,
290
285
  'response.status',
291
286
  responseCode)
@@ -9,9 +9,7 @@ const ParsedStatement = require('../db/parsed-statement')
9
9
  const Shim = require('./shim')
10
10
  const urltils = require('../util/urltils')
11
11
  const util = require('util')
12
- const {DESTINATIONS} = require('../config/attribute-filter')
13
12
 
14
- const SEGMENT_SCOPE = 'segment'
15
13
 
16
14
  /**
17
15
  * An enumeration of well-known datastores so that new instrumentations can use
@@ -321,6 +319,8 @@ function setDatastore(datastore) {
321
319
  ALL: metrics.DB.PREFIX + datastore + '/' + metrics.ALL
322
320
  }
323
321
 
322
+ this._datastore = datastore
323
+
324
324
  this._logger = this._logger.child({datastore: this._metrics.PREFIX})
325
325
  this.logger.trace({metrics: this._metrics}, 'Datastore metric names set')
326
326
  }
@@ -645,16 +645,16 @@ function captureInstanceAttributes(host, port, database) {
645
645
 
646
646
  // Normalize the instance attributes.
647
647
  var attributes = _normalizeParameters.call(this, {
648
- host: host,
648
+ host,
649
649
  port_path_or_id: port,
650
650
  database_name: database
651
651
  })
652
652
 
653
- segment.attributes.addAttributes(
654
- SEGMENT_SCOPE,
655
- DESTINATIONS.TRANS_SEGMENT,
656
- attributes
657
- )
653
+ for (let key in attributes) {
654
+ if (attributes[key]) {
655
+ segment.addAttribute(key, attributes[key])
656
+ }
657
+ }
658
658
  }
659
659
 
660
660
  /**
@@ -882,6 +882,8 @@ function _normalizeParameters(parameters) {
882
882
  var config = this.agent.config
883
883
  var dsTracerConf = config.datastore_tracer
884
884
 
885
+ parameters.product = parameters.product || this._datastore
886
+
885
887
  // Add database name if provided and enabled.
886
888
  if (!dsTracerConf.database_name_reporting.enabled) {
887
889
  delete parameters.database_name
@@ -10,7 +10,7 @@ var Shim = require('./shim') // For Shim.defineProperty
10
10
  var util = require('util')
11
11
 
12
12
  var ATTR_DESTS = require('../config/attribute-filter').DESTINATIONS
13
- const TRANSACTION_SCOPE = 'transaction'
13
+
14
14
 
15
15
  /**
16
16
  * Enumeration of well-known message brokers.
@@ -23,6 +23,7 @@ const LIBRARY_NAMES = {
23
23
  IRONMQ: 'IronMQ',
24
24
  KAFKA: 'Kafka',
25
25
  RABBITMQ: 'RabbitMQ',
26
+ SNS: 'SNS',
26
27
  SQS: 'SQS'
27
28
  }
28
29
 
@@ -746,7 +747,6 @@ function recordSubscribedConsume(nodule, properties, spec) {
746
747
  for (var key in msgDesc.parameters) {
747
748
  if (props.hasOwn(msgDesc.parameters, key)) {
748
749
  tx.trace.attributes.addAttribute(
749
- TRANSACTION_SCOPE,
750
750
  ATTR_DESTS.NONE,
751
751
  'message.parameters.' + key,
752
752
  msgDesc.parameters[key])
@@ -758,7 +758,6 @@ function recordSubscribedConsume(nodule, properties, spec) {
758
758
  if (!shim.agent.config.high_security) {
759
759
  if (msgDesc.routingKey) {
760
760
  tx.trace.attributes.addAttribute(
761
- TRANSACTION_SCOPE,
762
761
  ATTR_DESTS.TRANS_COMMON,
763
762
  'message.routingKey',
764
763
  msgDesc.routingKey
@@ -766,7 +765,6 @@ function recordSubscribedConsume(nodule, properties, spec) {
766
765
  }
767
766
  if (shim.isString(msgDesc.queue)) {
768
767
  tx.trace.attributes.addAttribute(
769
- TRANSACTION_SCOPE,
770
768
  ATTR_DESTS.TRANS_COMMON,
771
769
  'message.queueName',
772
770
  msgDesc.queue
package/lib/shim/shim.js CHANGED
@@ -922,6 +922,7 @@ function record(nodule, properties, recordNamer) {
922
922
  var shouldCreateSegment = !(
923
923
  parent.opaque || (segDesc.internal && parent.internal && shim === parent.shim)
924
924
  )
925
+
925
926
  var segment = shouldCreateSegment ? _rawCreateSegment(shim, segDesc) : parent
926
927
 
927
928
  return _doRecord.call(this, segment, args, segDesc, shouldCreateSegment)
@@ -1445,6 +1446,17 @@ function createSegment(name, recorder, parent) {
1445
1446
  }
1446
1447
 
1447
1448
  function _rawCreateSegment(shim, opts) {
1449
+ // Grab parent segment when none in opts so we can check opaqueness
1450
+ opts.parent = opts.parent || shim.getActiveSegment()
1451
+
1452
+ // When parent exists and is opaque, no new segment will be created
1453
+ // by tracer.createSegment and the parent will be returned. We bail
1454
+ // out early so we do not risk modifying the parent segment.
1455
+ if (opts.parent && opts.parent.opaque) {
1456
+ shim.logger.trace(opts, 'Did not create segment because parent is opaque')
1457
+ return opts.parent
1458
+ }
1459
+
1448
1460
  var segment = shim.tracer.createSegment(opts.name, opts.recorder, opts.parent)
1449
1461
  if (segment) {
1450
1462
  segment.internal = opts.internal
@@ -1,13 +1,12 @@
1
1
  'use strict'
2
2
 
3
+ const Config = require('../config')
3
4
  const {truncate} = require('../util/byte-limit')
4
- const Attributes = require('../attributes')
5
5
 
6
6
  const {DESTINATIONS} = require('../config/attribute-filter')
7
7
  const NAMES = require('../metrics/names')
8
8
  const HTTP_LIBRARY = 'http'
9
- const SPAN_KIND_ATTRIBUTE = 'client'
10
- const SEGMENT_SCOPE = 'segment'
9
+ const CLIENT_KIND = 'client'
11
10
  const CATEGORIES = {
12
11
  HTTP: 'http',
13
12
  DATASTORE: 'datastore',
@@ -15,6 +14,28 @@ const CATEGORIES = {
15
14
  }
16
15
  const EMPTY_USER_ATTRS = Object.freeze(Object.create(null))
17
16
 
17
+ /**
18
+ * All the intrinsic attributes for span events, regardless of kind.
19
+ */
20
+ class SpanIntrinsics {
21
+ constructor() {
22
+ this.type = 'Span'
23
+ this.traceId = null
24
+ this.guid = null
25
+ this.parentId = null
26
+ this.transactionId = null
27
+ this.sampled = null
28
+ this.priority = null
29
+ this.name = null
30
+ this.category = CATEGORIES.GENERIC
31
+ this.component = null
32
+ this.timestamp = null
33
+ this.duration = null
34
+ this['nr.entryPoint'] = null
35
+ this['span.kind'] = null
36
+ }
37
+ }
38
+
18
39
  /**
19
40
  * General span event class.
20
41
  *
@@ -25,25 +46,9 @@ const EMPTY_USER_ATTRS = Object.freeze(Object.create(null))
25
46
  * @class
26
47
  */
27
48
  class SpanEvent {
28
- constructor(segment) {
29
- let filter = null
30
- if (segment && segment.transaction) {
31
- filter = segment.transaction.agent.config.attributeFilter
32
- }
33
- this.attributes = new Attributes({filter})
34
- this.intrinsics = {
35
- type: 'Span',
36
- traceId: null,
37
- guid: null,
38
- parentId: null,
39
- transactionId: null,
40
- sampled: null,
41
- priority: null,
42
- name: null,
43
- category: CATEGORIES.GENERIC,
44
- timestamp: null,
45
- duration: null
46
- }
49
+ constructor(attributes) {
50
+ this.attributes = attributes
51
+ this.intrinsics = new SpanIntrinsics()
47
52
  }
48
53
 
49
54
  static get CATEGORIES() {
@@ -70,61 +75,53 @@ class SpanEvent {
70
75
  * @return {SpanEvent} The constructed event.
71
76
  */
72
77
  static fromSegment(segment, parentId = null) {
78
+ const attributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
79
+
73
80
  let span = null
74
81
  if (HttpSpanEvent.testSegment(segment)) {
75
- span = new HttpSpanEvent(segment)
82
+ span = new HttpSpanEvent(attributes)
76
83
  } else if (DatastoreSpanEvent.testSegment(segment)) {
77
- span = new DatastoreSpanEvent(segment)
84
+ span = new DatastoreSpanEvent(attributes)
78
85
  } else {
79
- span = new SpanEvent()
86
+ span = new SpanEvent(attributes)
80
87
  }
81
88
 
82
89
  const tx = segment.transaction
83
90
 
84
- Object.assign(span.intrinsics, {
85
- traceId: tx.traceId || tx.id,
86
- guid: segment.id,
87
- parentId,
88
- transactionId: tx.id,
89
- sampled: tx.sampled,
90
- priority: tx.priority,
91
- name: segment.name,
92
- 'nr.entryPoint': tx.baseSegment === segment ? true : null,
93
- // Timestamp in milliseconds, duration in seconds. Yay consistency!
94
- timestamp: segment.timer.start,
95
- duration: segment.timer.getDurationInMillis() / 1000
96
- })
91
+ span.intrinsics.traceId = tx.traceId || tx.id
92
+ span.intrinsics.guid = segment.id
93
+ span.intrinsics.parentId = parentId
94
+ span.intrinsics.transactionId = tx.id
95
+ span.intrinsics.sampled = tx.sampled
96
+ span.intrinsics.priority = tx.priority
97
+ span.intrinsics.name = segment.name
98
+
99
+ // Only set this if it will be `true`. Must be `null` otherwise.
100
+ if (tx.baseSegment === segment) {
101
+ span.intrinsics['nr.entryPoint'] = true
102
+ }
103
+
104
+ // Timestamp in milliseconds, duration in seconds. Yay consistency!
105
+ span.intrinsics.timestamp = segment.timer.start
106
+ span.intrinsics.duration = segment.timer.getDurationInMillis() / 1000
97
107
 
98
108
  return span
99
109
  }
100
110
 
101
111
  toJSON() {
102
- const intrinsics = Object.create(null)
103
- for (let key in this.intrinsics) {
104
- if (this.intrinsics[key] != null) {
105
- intrinsics[key] = this.intrinsics[key]
106
- }
107
- }
108
-
109
112
  return [
110
- intrinsics,
113
+ _filterNulls(this.intrinsics),
111
114
  EMPTY_USER_ATTRS,
112
- this.attributes.get(DESTINATIONS.SPAN_EVENT)
115
+ _filterNulls(this.attributes)
113
116
  ]
114
117
  }
115
118
 
116
119
  addAttribute(key, value, truncateExempt = false) {
117
- this.attributes.addAttribute(
118
- SEGMENT_SCOPE,
119
- DESTINATIONS.SPAN_EVENT,
120
- key,
121
- value,
122
- truncateExempt
123
- )
124
- }
125
-
126
- getAttributes() {
127
- return this.attributes.get(DESTINATIONS.SPAN_EVENT)
120
+ const {attributeFilter} = Config.getInstance()
121
+ const dest = attributeFilter.filterSegment(DESTINATIONS.SPAN_EVENT, key)
122
+ if (dest & DESTINATIONS.SPAN_EVENT) {
123
+ this.attributes[key] = truncateExempt ? value : _truncate(value)
124
+ }
128
125
  }
129
126
  }
130
127
 
@@ -135,23 +132,25 @@ class SpanEvent {
135
132
  * @class
136
133
  */
137
134
  class HttpSpanEvent extends SpanEvent {
138
- constructor(segment) {
139
- super(segment)
135
+ constructor(attributes) {
136
+ super(attributes)
140
137
 
141
- const segmentAttributes = segment.getAttributes()
138
+ this.intrinsics.category = CATEGORIES.HTTP
139
+ this.intrinsics.component = attributes.library || HTTP_LIBRARY
140
+ this.intrinsics['span.kind'] = CLIENT_KIND
142
141
 
143
- Object.assign(this.intrinsics, {
144
- category: CATEGORIES.HTTP,
145
- component: segmentAttributes.library || HTTP_LIBRARY,
146
- 'span.kind': SPAN_KIND_ATTRIBUTE,
147
- })
142
+ if (attributes.library) {
143
+ attributes.library = null
144
+ }
148
145
 
149
- if (segmentAttributes.url) {
150
- this.addAttribute('http.url', segmentAttributes.url)
146
+ if (attributes.url) {
147
+ this.addAttribute('http.url', attributes.url)
148
+ attributes.url = null
151
149
  }
152
150
 
153
- if (segmentAttributes.procedure) {
154
- this.addAttribute('http.method', segmentAttributes.procedure)
151
+ if (attributes.procedure) {
152
+ this.addAttribute('http.method', attributes.procedure)
153
+ attributes.procedure = null
155
154
  }
156
155
  }
157
156
 
@@ -167,45 +166,50 @@ class HttpSpanEvent extends SpanEvent {
167
166
  * @class.
168
167
  */
169
168
  class DatastoreSpanEvent extends SpanEvent {
170
- constructor(segment) {
171
- super(segment)
169
+ constructor(attributes) {
170
+ super(attributes)
172
171
 
173
- const segmentAttributes = segment.getAttributes()
172
+ this.intrinsics.category = CATEGORIES.DATASTORE
173
+ this.intrinsics['span.kind'] = CLIENT_KIND
174
174
 
175
- Object.assign(this.intrinsics, {
176
- category: CATEGORIES.DATASTORE,
177
- 'span.kind': SPAN_KIND_ATTRIBUTE,
178
- })
175
+ if (attributes.product) {
176
+ this.intrinsics.component = attributes.product
177
+ attributes.product = null
178
+ }
179
179
 
180
- if (segmentAttributes.product) {
181
- this.addAttribute( 'component', segmentAttributes.product)
180
+ if (attributes.collection) {
181
+ this.addAttribute('db.collection', attributes.collection)
182
+ attributes.collection = null
182
183
  }
183
184
 
184
- if (segmentAttributes.sql || segmentAttributes.sql_obfuscated) {
185
+ if (attributes.sql || attributes.sql_obfuscated) {
185
186
  let sql = null
186
- if (segmentAttributes.sql_obfuscated) {
187
- sql = _truncate(segmentAttributes.sql_obfuscated)
188
- } else if (segmentAttributes.sql) {
189
- sql = _truncate(segmentAttributes.sql)
187
+ if (attributes.sql_obfuscated) {
188
+ sql = _truncate(attributes.sql_obfuscated)
189
+ attributes.sql_obfuscated = null
190
+ } else if (attributes.sql) {
191
+ sql = _truncate(attributes.sql)
192
+ attributes.sql = null
190
193
  }
191
194
 
192
195
  // Flag as exempt from normal attribute truncation
193
196
  this.addAttribute('db.statement', sql, true)
194
197
  }
195
198
 
196
- if (segmentAttributes.database_name) {
197
- this.addAttribute('db.instance', segmentAttributes.database_name)
199
+ if (attributes.database_name) {
200
+ this.addAttribute('db.instance', attributes.database_name)
201
+ attributes.database_name = null
198
202
  }
199
203
 
200
- if (segmentAttributes.host) {
201
- this.addAttribute('peer.hostname', segmentAttributes.host)
204
+ if (attributes.host) {
205
+ this.addAttribute('peer.hostname', attributes.host)
202
206
 
203
- if (segmentAttributes.port_path_or_id) {
204
- this.addAttribute(
205
- 'peer.address',
206
- `${segmentAttributes.host}:${segmentAttributes.port_path_or_id}`
207
- )
207
+ if (attributes.port_path_or_id) {
208
+ const address = `${attributes.host}:${attributes.port_path_or_id}`
209
+ this.addAttribute('peer.address', address)
210
+ attributes.port_path_or_id = null
208
211
  }
212
+ attributes.host = null
209
213
  }
210
214
  }
211
215
 
@@ -222,4 +226,14 @@ function _truncate(val) {
222
226
  return truncated
223
227
  }
224
228
 
229
+ function _filterNulls(obj) {
230
+ const out = Object.create(null)
231
+ for (let key in obj) {
232
+ if (obj[key] != null) {
233
+ out[key] = obj[key]
234
+ }
235
+ }
236
+ return out
237
+ }
238
+
225
239
  module.exports = SpanEvent
package/lib/timer.js CHANGED
@@ -142,7 +142,9 @@ Timer.prototype.setDurationInMillis = function setDurationInMillis(duration, sta
142
142
  // anywhere except to calculate duration, and we are setting duration
143
143
  // this is fine.
144
144
  this.hrstart = [Math.floor(start / 1e3), start % 1e3 * 1e6]
145
- this.start = start
145
+ if (start != null) {
146
+ this.start = start
147
+ }
146
148
  }
147
149
 
148
150
  /**