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.
- package/CONTRIBUTING.md +3 -4
- package/NEWS.md +91 -0
- package/api.js +27 -3
- package/index.js +24 -24
- package/lib/adaptive-sampler.js +8 -9
- package/lib/attributes.js +39 -20
- package/lib/collector/facts.js +6 -1
- package/lib/collector/remote-method.js +1 -1
- package/lib/config/attribute-filter.js +43 -13
- package/lib/config/default.js +5 -1
- package/lib/config/env.js +1 -1
- package/lib/config/index.js +40 -5
- package/lib/errors/index.js +1 -2
- package/lib/header-attributes.js +0 -2
- package/lib/instrumentation/core/http-outbound.js +13 -1
- package/lib/instrumentation/core/http.js +0 -6
- package/lib/instrumentation/memcached.js +3 -2
- package/lib/serverless/aws-lambda.js +1 -6
- package/lib/shim/datastore-shim.js +10 -8
- package/lib/shim/message-shim.js +2 -4
- package/lib/shim/shim.js +12 -0
- package/lib/spans/span-event.js +107 -93
- package/lib/timer.js +3 -1
- package/lib/transaction/index.js +3 -5
- package/lib/transaction/trace/exclusive-time-calculator.js +145 -0
- package/lib/transaction/trace/index.js +18 -29
- package/lib/transaction/trace/segment.js +63 -74
- package/lib/util/attribute-types.js +21 -0
- package/lib/util/byte-limit.js +38 -9
- package/package.json +1 -1
- package/lib/util/sum-children.js +0 -52
package/lib/config/index.js
CHANGED
|
@@ -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', '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/lib/errors/index.js
CHANGED
|
@@ -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.
|
|
81
|
-
'transaction',
|
|
80
|
+
var dest = config.attributeFilter.filterTransaction(
|
|
82
81
|
DESTINATIONS.ERROR_EVENT,
|
|
83
82
|
key
|
|
84
83
|
)
|
package/lib/header-attributes.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
49
|
+
location = server
|
|
50
50
|
} else if (this.HashRing && this.HashRing.get && metacall.key) {
|
|
51
|
-
location = this.HashRing.get(metacall.key)
|
|
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
|
-
|
|
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
|
|
648
|
+
host,
|
|
649
649
|
port_path_or_id: port,
|
|
650
650
|
database_name: database
|
|
651
651
|
})
|
|
652
652
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
package/lib/shim/message-shim.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/lib/spans/span-event.js
CHANGED
|
@@ -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
|
|
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(
|
|
29
|
-
|
|
30
|
-
|
|
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(
|
|
82
|
+
span = new HttpSpanEvent(attributes)
|
|
76
83
|
} else if (DatastoreSpanEvent.testSegment(segment)) {
|
|
77
|
-
span = new DatastoreSpanEvent(
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
115
|
+
_filterNulls(this.attributes)
|
|
113
116
|
]
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
addAttribute(key, value, truncateExempt = false) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
key
|
|
121
|
-
|
|
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(
|
|
139
|
-
super(
|
|
135
|
+
constructor(attributes) {
|
|
136
|
+
super(attributes)
|
|
140
137
|
|
|
141
|
-
|
|
138
|
+
this.intrinsics.category = CATEGORIES.HTTP
|
|
139
|
+
this.intrinsics.component = attributes.library || HTTP_LIBRARY
|
|
140
|
+
this.intrinsics['span.kind'] = CLIENT_KIND
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
'span.kind': SPAN_KIND_ATTRIBUTE,
|
|
147
|
-
})
|
|
142
|
+
if (attributes.library) {
|
|
143
|
+
attributes.library = null
|
|
144
|
+
}
|
|
148
145
|
|
|
149
|
-
if (
|
|
150
|
-
this.addAttribute('http.url',
|
|
146
|
+
if (attributes.url) {
|
|
147
|
+
this.addAttribute('http.url', attributes.url)
|
|
148
|
+
attributes.url = null
|
|
151
149
|
}
|
|
152
150
|
|
|
153
|
-
if (
|
|
154
|
-
this.addAttribute('http.method',
|
|
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(
|
|
171
|
-
super(
|
|
169
|
+
constructor(attributes) {
|
|
170
|
+
super(attributes)
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
this.intrinsics.category = CATEGORIES.DATASTORE
|
|
173
|
+
this.intrinsics['span.kind'] = CLIENT_KIND
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
175
|
+
if (attributes.product) {
|
|
176
|
+
this.intrinsics.component = attributes.product
|
|
177
|
+
attributes.product = null
|
|
178
|
+
}
|
|
179
179
|
|
|
180
|
-
if (
|
|
181
|
-
this.addAttribute(
|
|
180
|
+
if (attributes.collection) {
|
|
181
|
+
this.addAttribute('db.collection', attributes.collection)
|
|
182
|
+
attributes.collection = null
|
|
182
183
|
}
|
|
183
184
|
|
|
184
|
-
if (
|
|
185
|
+
if (attributes.sql || attributes.sql_obfuscated) {
|
|
185
186
|
let sql = null
|
|
186
|
-
if (
|
|
187
|
-
sql = _truncate(
|
|
188
|
-
|
|
189
|
-
|
|
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 (
|
|
197
|
-
this.addAttribute('db.instance',
|
|
199
|
+
if (attributes.database_name) {
|
|
200
|
+
this.addAttribute('db.instance', attributes.database_name)
|
|
201
|
+
attributes.database_name = null
|
|
198
202
|
}
|
|
199
203
|
|
|
200
|
-
if (
|
|
201
|
-
this.addAttribute('peer.hostname',
|
|
204
|
+
if (attributes.host) {
|
|
205
|
+
this.addAttribute('peer.hostname', attributes.host)
|
|
202
206
|
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
145
|
+
if (start != null) {
|
|
146
|
+
this.start = start
|
|
147
|
+
}
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
/**
|