newrelic 4.4.0 → 4.7.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 +147 -1
- package/api.js +8 -1
- package/index.js +16 -8
- package/lib/agent.js +7 -1
- package/lib/config/default.js +17 -0
- package/lib/config/env.js +4 -0
- package/lib/config/index.js +21 -5
- package/lib/db/tracer.js +15 -6
- package/lib/environment.js +1 -1
- package/lib/errors/index.js +1 -1
- package/lib/feature_flags.js +0 -1
- package/lib/harvest.js +71 -22
- package/lib/instrumentation/core/crypto.js +2 -1
- package/lib/instrumentation/core/fs.js +7 -2
- package/lib/instrumentation/core/globals.js +17 -1
- package/lib/instrumentation/core/http-outbound.js +102 -3
- package/lib/instrumentation/core/http.js +6 -96
- package/lib/instrumentation/hapi/hapi-17.js +4 -0
- package/lib/metrics/recorders/distributed-trace.js +1 -1
- package/lib/metrics/recorders/http.js +1 -1
- package/lib/metrics/recorders/other.js +1 -1
- package/lib/priority-queue.js +5 -2
- package/lib/shim/message-shim.js +5 -4
- package/lib/shim/shim.js +14 -3
- package/lib/shim/specs/index.js +1 -0
- package/lib/shim/transaction-shim.js +3 -3
- package/lib/shimmer.js +3 -0
- package/lib/spans/aggregator.js +3 -4
- package/lib/spans/span-event.js +54 -30
- package/lib/transaction/dt-payload.js +16 -0
- package/lib/transaction/handle.js +1 -1
- package/lib/transaction/index.js +36 -9
- package/lib/transaction/trace/attributes.js +7 -27
- package/lib/transaction/trace/index.js +37 -46
- package/lib/util/byte-limit.js +40 -0
- package/package.json +2 -2
- package/.npmignore +0 -4
|
@@ -6,11 +6,19 @@ var urltils = require('../../util/urltils')
|
|
|
6
6
|
var hashes = require('../../util/hashes')
|
|
7
7
|
var logger = require('../../logger').child({component: 'outbound'})
|
|
8
8
|
var shimmer = require('../../shimmer')
|
|
9
|
+
var util = require('util')
|
|
10
|
+
var url = require('url')
|
|
11
|
+
var copy = require('../../util/copy')
|
|
9
12
|
|
|
10
13
|
const DEFAULT_HOST = 'localhost'
|
|
11
14
|
const DEFAULT_PORT = 80
|
|
12
15
|
const DEFAULT_SSL_PORT = 443
|
|
13
16
|
|
|
17
|
+
const NEWRELIC_ID_HEADER = 'x-newrelic-id'
|
|
18
|
+
const NEWRELIC_TRACE_HEADER = 'newrelic'
|
|
19
|
+
const NEWRELIC_TRANSACTION_HEADER = 'x-newrelic-transaction'
|
|
20
|
+
const NEWRELIC_SYNTHETICS_HEADER = 'x-newrelic-synthetics'
|
|
21
|
+
|
|
14
22
|
/**
|
|
15
23
|
* Instruments an outbound HTTP request.
|
|
16
24
|
*
|
|
@@ -21,6 +29,12 @@ const DEFAULT_SSL_PORT = 443
|
|
|
21
29
|
* @return {http.ClientRequest} The instrumented outbound request.
|
|
22
30
|
*/
|
|
23
31
|
module.exports = function instrumentOutbound(agent, opts, makeRequest) {
|
|
32
|
+
if (typeof opts === 'string') {
|
|
33
|
+
opts = url.parse(opts)
|
|
34
|
+
} else {
|
|
35
|
+
opts = copy.shallow(opts)
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
let hostname = opts.hostname || opts.host || DEFAULT_HOST
|
|
25
39
|
let port = opts.port || opts.defaultPort
|
|
26
40
|
if (!port) {
|
|
@@ -32,7 +46,7 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
|
|
|
32
46
|
'Invalid host name (%s) or port (%s) for outbound request.',
|
|
33
47
|
hostname, port
|
|
34
48
|
)
|
|
35
|
-
return makeRequest()
|
|
49
|
+
return makeRequest(opts)
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
// Technically we shouldn't append the port if this is an https request on 443
|
|
@@ -54,8 +68,48 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
|
|
|
54
68
|
)
|
|
55
69
|
|
|
56
70
|
function instrumentRequest(segment) {
|
|
71
|
+
const transaction = segment.transaction
|
|
72
|
+
const outboundHeaders = Object.create(null)
|
|
73
|
+
|
|
74
|
+
// FLAG: synthetics
|
|
75
|
+
if (
|
|
76
|
+
agent.config.feature_flag.synthetics &&
|
|
77
|
+
agent.config.encoding_key &&
|
|
78
|
+
transaction.syntheticsHeader
|
|
79
|
+
) {
|
|
80
|
+
outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// If CAT is enabled, inject the transaction header.
|
|
84
|
+
// TODO: abstract header logic shared with TransactionShim#insertCATRequestHeaders
|
|
85
|
+
if (agent.config.cross_application_tracer.enabled) {
|
|
86
|
+
if (agent.config.distributed_tracing.enabled) {
|
|
87
|
+
_addDistributedHeaders(transaction, outboundHeaders)
|
|
88
|
+
} else if (agent.config.encoding_key) {
|
|
89
|
+
_addCATHeaders(agent, transaction, outboundHeaders)
|
|
90
|
+
} else {
|
|
91
|
+
logger.trace('No encoding key found, not adding CAT headers')
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (util.isArray(opts.headers)) {
|
|
96
|
+
opts.headers = opts.headers.slice()
|
|
97
|
+
Array.prototype.push.apply(
|
|
98
|
+
opts.headers,
|
|
99
|
+
Object.keys(outboundHeaders).map(function getHeaderTuples(key) {
|
|
100
|
+
return [key, outboundHeaders[key]]
|
|
101
|
+
})
|
|
102
|
+
)
|
|
103
|
+
} else {
|
|
104
|
+
opts.headers = Object.assign(
|
|
105
|
+
Object.create(null),
|
|
106
|
+
opts.headers,
|
|
107
|
+
outboundHeaders
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
57
111
|
segment.start()
|
|
58
|
-
const request = makeRequest()
|
|
112
|
+
const request = makeRequest(opts)
|
|
59
113
|
const parsed = urltils.scrubAndParseParameters(request.path)
|
|
60
114
|
const proto = parsed.protocol || opts.protocol || 'http:'
|
|
61
115
|
segment.name += parsed.path
|
|
@@ -129,7 +183,7 @@ function handleResponse(segment, hostname, req, res) {
|
|
|
129
183
|
const agent = segment.transaction.agent
|
|
130
184
|
if (
|
|
131
185
|
agent.config.cross_application_tracer.enabled &&
|
|
132
|
-
!agent.config.
|
|
186
|
+
!agent.config.distributed_tracing.enabled
|
|
133
187
|
) {
|
|
134
188
|
pullCatHeaders(agent.config, segment, hostname, res.headers['x-newrelic-app-data'])
|
|
135
189
|
}
|
|
@@ -202,3 +256,48 @@ function _makeNonEnumerable(obj, prop) {
|
|
|
202
256
|
logger.debug(e, 'Failed to make %s non enumerable.', prop)
|
|
203
257
|
}
|
|
204
258
|
}
|
|
259
|
+
|
|
260
|
+
function _addDistributedHeaders(tx, outboundHeaders) {
|
|
261
|
+
try {
|
|
262
|
+
const txData = tx.createDistributedTracePayload().httpSafe()
|
|
263
|
+
outboundHeaders[NEWRELIC_TRACE_HEADER] = txData
|
|
264
|
+
|
|
265
|
+
logger.trace(
|
|
266
|
+
'Added outbound request distributed tracing headers in transaction %s',
|
|
267
|
+
tx.id
|
|
268
|
+
)
|
|
269
|
+
} catch (err) {
|
|
270
|
+
logger.trace(err, 'Failed to create distributed trace payload')
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function _addCATHeaders(agent, tx, outboundHeaders) {
|
|
275
|
+
if (agent.config.obfuscatedId) {
|
|
276
|
+
outboundHeaders[NEWRELIC_ID_HEADER] = agent.config.obfuscatedId
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
var pathHash = hashes.calculatePathHash(
|
|
280
|
+
agent.config.applications()[0],
|
|
281
|
+
tx.getFullName() || '',
|
|
282
|
+
tx.referringPathHash
|
|
283
|
+
)
|
|
284
|
+
tx.pushPathHash(pathHash)
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
let txData = JSON.stringify([
|
|
288
|
+
tx.id,
|
|
289
|
+
false,
|
|
290
|
+
tx.tripId || tx.id,
|
|
291
|
+
pathHash
|
|
292
|
+
])
|
|
293
|
+
txData = hashes.obfuscateNameUsingKey(txData, agent.config.encoding_key)
|
|
294
|
+
outboundHeaders[NEWRELIC_TRANSACTION_HEADER] = txData
|
|
295
|
+
|
|
296
|
+
logger.trace(
|
|
297
|
+
'Added outbound request CAT headers in transaction %s',
|
|
298
|
+
tx.id
|
|
299
|
+
)
|
|
300
|
+
} catch (err) {
|
|
301
|
+
logger.trace(err, 'Failed to create CAT payload')
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -11,7 +11,6 @@ var url = require('url')
|
|
|
11
11
|
var urltils = require('../../util/urltils')
|
|
12
12
|
var properties = require('../../util/properties')
|
|
13
13
|
var psemver = require('../../util/process-version')
|
|
14
|
-
var copy = require('../../util/copy')
|
|
15
14
|
|
|
16
15
|
const NAMES = require('../../metrics/names')
|
|
17
16
|
const DESTS = require('../../config/attribute-filter').DESTINATIONS
|
|
@@ -206,7 +205,7 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
if (agent.config.cross_application_tracer.enabled) {
|
|
209
|
-
if (agent.config.
|
|
208
|
+
if (agent.config.distributed_tracing.enabled) {
|
|
210
209
|
const payload = request.headers[NEWRELIC_TRACE_HEADER]
|
|
211
210
|
if (payload) {
|
|
212
211
|
logger.trace(
|
|
@@ -499,54 +498,10 @@ function wrapRequest(agent, request) {
|
|
|
499
498
|
|
|
500
499
|
const args = agent.tracer.slice(arguments)
|
|
501
500
|
const context = this
|
|
502
|
-
const outboundHeaders = Object.create(null)
|
|
503
|
-
|
|
504
|
-
// FLAG: synthetics
|
|
505
|
-
if (
|
|
506
|
-
agent.config.feature_flag.synthetics &&
|
|
507
|
-
agent.config.encoding_key &&
|
|
508
|
-
transaction.syntheticsHeader
|
|
509
|
-
) {
|
|
510
|
-
outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// If CAT is enabled, inject the transaction header.
|
|
514
|
-
// TODO: abstract header logic shared with TransactionShim#insertCATRequestHeaders
|
|
515
|
-
if (agent.config.cross_application_tracer.enabled) {
|
|
516
|
-
if (agent.config.feature_flag.distributed_tracing) {
|
|
517
|
-
_addDistributedHeaders(transaction, outboundHeaders)
|
|
518
|
-
} else if (agent.config.encoding_key) {
|
|
519
|
-
_addCATHeaders(agent, transaction, outboundHeaders)
|
|
520
|
-
} else {
|
|
521
|
-
logger.trace('No encoding key found, not adding CAT headers')
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
if (typeof options === 'string') {
|
|
526
|
-
options = url.parse(options)
|
|
527
|
-
} else {
|
|
528
|
-
options = copy.shallow(options)
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (util.isArray(options.headers)) {
|
|
532
|
-
options.headers = options.headers.slice()
|
|
533
|
-
Array.prototype.push.apply(
|
|
534
|
-
options.headers,
|
|
535
|
-
Object.keys(outboundHeaders).map(function getHeaderTuples(key) {
|
|
536
|
-
return [key, outboundHeaders[key]]
|
|
537
|
-
})
|
|
538
|
-
)
|
|
539
|
-
} else {
|
|
540
|
-
options.headers = Object.assign(
|
|
541
|
-
Object.create(null),
|
|
542
|
-
options.headers,
|
|
543
|
-
outboundHeaders
|
|
544
|
-
)
|
|
545
|
-
}
|
|
546
|
-
args[0] = options
|
|
547
501
|
|
|
548
502
|
// hostname & port logic pulled directly from node's 0.10 lib/http.js
|
|
549
|
-
return instrumentOutbound(agent, options, function makeRequest() {
|
|
503
|
+
return instrumentOutbound(agent, options, function makeRequest(opts) {
|
|
504
|
+
args[0] = opts
|
|
550
505
|
return request.apply(context, args)
|
|
551
506
|
})
|
|
552
507
|
}
|
|
@@ -738,7 +693,7 @@ module.exports = function initialize(agent, http, moduleName) {
|
|
|
738
693
|
*
|
|
739
694
|
* @param {string} header - The raw X-NewRelic-Synthetics header
|
|
740
695
|
* @param {string} encKey - Encoding key handed down from the server
|
|
741
|
-
* @param {
|
|
696
|
+
* @param {Array.<number>} trustedIds - Array of accounts to trust the header from.
|
|
742
697
|
* @param {Transaction} transaction - Where the synthetics data is attached to.
|
|
743
698
|
*/
|
|
744
699
|
function handleSyntheticsHeader(header, encKey, trustedIds, transaction) {
|
|
@@ -756,8 +711,8 @@ function handleSyntheticsHeader(header, encKey, trustedIds, transaction) {
|
|
|
756
711
|
*
|
|
757
712
|
* @param {string} header - The raw X-NewRelic-Synthetics header
|
|
758
713
|
* @param {string} encKey - Encoding key handed down from the server
|
|
759
|
-
* @param {
|
|
760
|
-
* @return {Object
|
|
714
|
+
* @param {Array.<number>} trustedIds - Array of accounts to trust the header from.
|
|
715
|
+
* @return {Object|null} - On successful parse and verification an object of
|
|
761
716
|
* synthetics data is returned, otherwise null is
|
|
762
717
|
* returned.
|
|
763
718
|
*/
|
|
@@ -877,48 +832,3 @@ function _collectHeaders(headers, nameMap, prefix, tx) {
|
|
|
877
832
|
}
|
|
878
833
|
}
|
|
879
834
|
}
|
|
880
|
-
|
|
881
|
-
function _addDistributedHeaders(tx, outboundHeaders) {
|
|
882
|
-
try {
|
|
883
|
-
const txData = tx.createDistributedTracePayload().httpSafe()
|
|
884
|
-
outboundHeaders[NEWRELIC_TRACE_HEADER] = txData
|
|
885
|
-
|
|
886
|
-
logger.trace(
|
|
887
|
-
'Added outbound request distributed tracing headers in transaction %s',
|
|
888
|
-
tx.id
|
|
889
|
-
)
|
|
890
|
-
} catch (err) {
|
|
891
|
-
logger.trace(err, 'Failed to create distributed trace payload')
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
function _addCATHeaders(agent, tx, outboundHeaders) {
|
|
896
|
-
if (agent.config.obfuscatedId) {
|
|
897
|
-
outboundHeaders[NEWRELIC_ID_HEADER] = agent.config.obfuscatedId
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
var pathHash = hashes.calculatePathHash(
|
|
901
|
-
agent.config.applications()[0],
|
|
902
|
-
tx.getFullName() || '',
|
|
903
|
-
tx.referringPathHash
|
|
904
|
-
)
|
|
905
|
-
tx.pushPathHash(pathHash)
|
|
906
|
-
|
|
907
|
-
try {
|
|
908
|
-
let txData = JSON.stringify([
|
|
909
|
-
tx.id,
|
|
910
|
-
false,
|
|
911
|
-
tx.tripId || tx.id,
|
|
912
|
-
pathHash
|
|
913
|
-
])
|
|
914
|
-
txData = hashes.obfuscateNameUsingKey(txData, agent.config.encoding_key)
|
|
915
|
-
outboundHeaders[NEWRELIC_TRANSACTION_HEADER] = txData
|
|
916
|
-
|
|
917
|
-
logger.trace(
|
|
918
|
-
'Added outbound request CAT headers in transaction %s',
|
|
919
|
-
tx.id
|
|
920
|
-
)
|
|
921
|
-
} catch (err) {
|
|
922
|
-
logger.trace(err, 'Failed to create CAT payload')
|
|
923
|
-
}
|
|
924
|
-
}
|
|
@@ -10,6 +10,10 @@ module.exports = function initialize(agent, hapi, moduleName, shim) {
|
|
|
10
10
|
return !(result instanceof Error) && (result !== args[1].continue)
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
+
// 'Server' and 'server' both point to the same export,
|
|
14
|
+
// but we can't make any assumption about which will be used.
|
|
15
|
+
// Since we wrap the prototype, the second wrap should exit early.
|
|
16
|
+
shim.wrapReturn(hapi, 'server', serverFactoryWrapper)
|
|
13
17
|
shim.wrapReturn(hapi, 'Server', serverFactoryWrapper)
|
|
14
18
|
}
|
|
15
19
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const NAMES = require('../names')
|
|
4
4
|
|
|
5
5
|
function recordDistributedTrace(tx, suffix, duration, exclusive) {
|
|
6
|
-
const distTraceReceived = !!tx.
|
|
6
|
+
const distTraceReceived = !!tx.acceptedDistributedTrace
|
|
7
7
|
const tag = [
|
|
8
8
|
tx.parentType || 'Unknown',
|
|
9
9
|
tx.parentAcct || 'Unknown',
|
|
@@ -33,7 +33,7 @@ function recordWeb(segment, scope) {
|
|
|
33
33
|
tx.measure(NAMES.QUEUETIME, null, tx.queueTime)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
if (config.
|
|
36
|
+
if (config.distributed_tracing.enabled) {
|
|
37
37
|
recordDistributedTrace(tx, 'Web', duration, exclusive)
|
|
38
38
|
} else if (tx.incomingCatId) {
|
|
39
39
|
tx.measure(
|
|
@@ -34,7 +34,7 @@ function recordBackground(segment, scope) {
|
|
|
34
34
|
)
|
|
35
35
|
tx.measure(NAMES.OTHER_TRANSACTION.TOTAL_TIME, null, totalTime, exclusive)
|
|
36
36
|
|
|
37
|
-
if (tx.agent.config.
|
|
37
|
+
if (tx.agent.config.distributed_tracing.enabled) {
|
|
38
38
|
recordDistributedTrace(tx, 'Other', duration, exclusive)
|
|
39
39
|
}
|
|
40
40
|
}
|
package/lib/priority-queue.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var Heap = require('@tyriar/fibonacci-heap').FibonacciHeap
|
|
4
4
|
|
|
5
5
|
function PriorityQueue(limit) {
|
|
6
|
-
this.limit = limit
|
|
6
|
+
this.limit = limit == null ? 10 : limit
|
|
7
7
|
this.seen = 0
|
|
8
8
|
this._data = new Heap()
|
|
9
9
|
|
|
@@ -24,8 +24,11 @@ PriorityQueue.prototype.getMinimumPriority = function getMinimumPriority() {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
PriorityQueue.prototype.add = function add(value, priority) {
|
|
27
|
-
priority = priority || Math.random()
|
|
28
27
|
this.seen++
|
|
28
|
+
if (this.limit <= 0) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
priority = priority || Math.random()
|
|
29
32
|
if (this.length === this.limit) {
|
|
30
33
|
return this._replace(value, priority)
|
|
31
34
|
}
|
package/lib/shim/message-shim.js
CHANGED
|
@@ -333,10 +333,6 @@ function recordProduce(nodule, properties, recordNamer) {
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
var name = _nameMessageSegment(shim, msgDesc, shim._metrics.PRODUCE)
|
|
336
|
-
if (msgDesc.headers) {
|
|
337
|
-
shim.insertCATRequestHeaders(msgDesc.headers, true)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
336
|
if (!shim.agent.config.message_tracer.segment_parameters.enabled) {
|
|
341
337
|
delete msgDesc.parameters
|
|
342
338
|
} else if (msgDesc.routingKey) {
|
|
@@ -350,6 +346,11 @@ function recordProduce(nodule, properties, recordNamer) {
|
|
|
350
346
|
promise: msgDesc.promise || false,
|
|
351
347
|
callback: msgDesc.callback || null,
|
|
352
348
|
recorder: genericRecorder,
|
|
349
|
+
inContext: function generateCATHeaders() {
|
|
350
|
+
if (msgDesc.headers) {
|
|
351
|
+
shim.insertCATRequestHeaders(msgDesc.headers, true)
|
|
352
|
+
}
|
|
353
|
+
},
|
|
353
354
|
parameters: msgDesc.parameters || null
|
|
354
355
|
}
|
|
355
356
|
})
|
package/lib/shim/shim.js
CHANGED
|
@@ -946,7 +946,7 @@ function record(nodule, properties, recordNamer) {
|
|
|
946
946
|
var promised = false
|
|
947
947
|
var ret
|
|
948
948
|
try {
|
|
949
|
-
ret = shim.applySegment(fn, segment, true, ctx, args)
|
|
949
|
+
ret = shim.applySegment(fn, segment, true, ctx, args, segDesc.inContext)
|
|
950
950
|
if (segDesc.after && segDesc.promise && shim.isPromise(ret)) {
|
|
951
951
|
promised = true
|
|
952
952
|
return ret.then(function onThen(val) {
|
|
@@ -1286,7 +1286,7 @@ function storeSegment(obj, segment) {
|
|
|
1286
1286
|
* Sets the given segment as the active one for the duration of the function's
|
|
1287
1287
|
* execution.
|
|
1288
1288
|
*
|
|
1289
|
-
* - `applySegment(func, segment, full, context, args)`
|
|
1289
|
+
* - `applySegment(func, segment, full, context, args[, inContextCB])`
|
|
1290
1290
|
*
|
|
1291
1291
|
* @memberof Shim.prototype
|
|
1292
1292
|
*
|
|
@@ -1305,9 +1305,15 @@ function storeSegment(obj, segment) {
|
|
|
1305
1305
|
* @param {Array.<*>} args
|
|
1306
1306
|
* The arguments to be passed into the function.
|
|
1307
1307
|
*
|
|
1308
|
+
* @param {Function} [inContextCB]
|
|
1309
|
+
* The function used to do more instrumentation work. This function is
|
|
1310
|
+
* guaranteed to be executed with the segment associated with.
|
|
1311
|
+
*
|
|
1312
|
+
*
|
|
1308
1313
|
* @return {*} Whatever value `func` returned.
|
|
1309
1314
|
*/
|
|
1310
|
-
|
|
1315
|
+
/* eslint-disable max-params */
|
|
1316
|
+
function applySegment(func, segment, full, context, args, inContextCB) {
|
|
1311
1317
|
// Exist fast for bad arguments.
|
|
1312
1318
|
if (!this.isFunction(func)) {
|
|
1313
1319
|
return
|
|
@@ -1327,6 +1333,10 @@ function applySegment(func, segment, full, context, args) {
|
|
|
1327
1333
|
segment.start()
|
|
1328
1334
|
}
|
|
1329
1335
|
|
|
1336
|
+
if (typeof inContextCB === 'function') {
|
|
1337
|
+
inContextCB()
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1330
1340
|
// Execute the function and then return the tracer segment to the old one.
|
|
1331
1341
|
try {
|
|
1332
1342
|
return func.apply(context, args)
|
|
@@ -1337,6 +1347,7 @@ function applySegment(func, segment, full, context, args) {
|
|
|
1337
1347
|
tracer.segment = prevSegment
|
|
1338
1348
|
}
|
|
1339
1349
|
}
|
|
1350
|
+
/* eslint-enable max-params */
|
|
1340
1351
|
|
|
1341
1352
|
/**
|
|
1342
1353
|
* Creates a new segment.
|
package/lib/shim/specs/index.js
CHANGED
|
@@ -45,6 +45,7 @@ function WrapSpec(spec) {
|
|
|
45
45
|
function SegmentSpec(spec) {
|
|
46
46
|
this.name = hasOwnProperty(spec, 'name') ? spec.name : null
|
|
47
47
|
this.recorder = hasOwnProperty(spec, 'recorder') ? spec.recorder : null
|
|
48
|
+
this.inContext = hasOwnProperty(spec, 'inContext') ? spec.inContext : null
|
|
48
49
|
this.parent = hasOwnProperty(spec, 'parent') ? spec.parent : null
|
|
49
50
|
this.parameters = hasOwnProperty(spec, 'parameters') ? spec.parameters : null
|
|
50
51
|
this.internal = hasOwnProperty(spec, 'internal') ? spec.internal : false
|
|
@@ -287,7 +287,7 @@ function handleCATHeaders(headers, segment, transportType) {
|
|
|
287
287
|
transportType = Transaction.TRANSPORT_TYPES.UNKNOWN
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
if (this.agent.config.
|
|
290
|
+
if (this.agent.config.distributed_tracing.enabled) {
|
|
291
291
|
const payload = headers[DISTRIBUTED_TRACE_HEADER]
|
|
292
292
|
if (payload) {
|
|
293
293
|
tx.acceptDistributedTracePayload(payload, transportType)
|
|
@@ -351,7 +351,7 @@ function insertCATRequestHeaders(headers, useAlternateHeaderNames) {
|
|
|
351
351
|
this.logger.trace('CAT disabled, not adding headers.')
|
|
352
352
|
return
|
|
353
353
|
}
|
|
354
|
-
var usingDistributedTracing = this.agent.config.
|
|
354
|
+
var usingDistributedTracing = this.agent.config.distributed_tracing.enabled
|
|
355
355
|
if (!this.agent.config.encoding_key && !usingDistributedTracing) {
|
|
356
356
|
this.logger.warn('Missing encoding key, not adding CAT headers!')
|
|
357
357
|
return
|
|
@@ -433,7 +433,7 @@ function insertCATReplyHeader(headers, useAlternateHeaderNames) {
|
|
|
433
433
|
if (!config.cross_application_tracer.enabled) {
|
|
434
434
|
this.logger.trace('CAT disabled, not adding reply header.')
|
|
435
435
|
return
|
|
436
|
-
} else if (config.
|
|
436
|
+
} else if (config.distributed_tracing.enabled) {
|
|
437
437
|
this.logger.warn('CAT disabled, distributed tracing is enabled')
|
|
438
438
|
return
|
|
439
439
|
} else if (!config.encoding_key) {
|
package/lib/shimmer.js
CHANGED
|
@@ -205,6 +205,9 @@ var shimmer = module.exports = {
|
|
|
205
205
|
if (original.__NR_unwrap) return logger.debug("%s already wrapped by agent.", fqmn)
|
|
206
206
|
|
|
207
207
|
var wrapped = wrapper(original, method)
|
|
208
|
+
Object.keys(original).forEach((key) => {
|
|
209
|
+
wrapped[key] = original[key]
|
|
210
|
+
})
|
|
208
211
|
wrapped.__NR_original = original
|
|
209
212
|
wrapped.__NR_unwrap = function __NR_unwrap() {
|
|
210
213
|
nodule[method] = original
|
package/lib/spans/aggregator.js
CHANGED
|
@@ -20,19 +20,18 @@ class SpanAggregator extends EventAggregator {
|
|
|
20
20
|
* Attempts to add the given segment to the collection.
|
|
21
21
|
*
|
|
22
22
|
* @param {TraceSegment} segment - The segment to add.
|
|
23
|
-
* @param {string} [parentId]
|
|
24
|
-
* @param {string} [grandparentId] - The GUID of the grandparent span.
|
|
23
|
+
* @param {string} [parentId=null] - The GUID of the parent span.
|
|
25
24
|
*
|
|
26
25
|
* @return {bool} True if the segment was added, or false if it was discarded.
|
|
27
26
|
*/
|
|
28
|
-
addSegment(segment, parentId
|
|
27
|
+
addSegment(segment, parentId) {
|
|
29
28
|
// Check if the priority would be accepted before creating the event object.
|
|
30
29
|
const tx = segment.transaction
|
|
31
30
|
if (tx.priority < this._events.getMinimumPriority()) {
|
|
32
31
|
++this._events.seen
|
|
33
32
|
return false
|
|
34
33
|
}
|
|
35
|
-
const span = SpanEvent.fromSegment(segment, parentId || null
|
|
34
|
+
const span = SpanEvent.fromSegment(segment, parentId || null)
|
|
36
35
|
return this.addEvent(span, tx.priority)
|
|
37
36
|
}
|
|
38
37
|
}
|
package/lib/spans/span-event.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const truncate = require('../util/byte-limit').truncate
|
|
4
|
+
|
|
3
5
|
const NAMES = require('../metrics/names')
|
|
4
|
-
const
|
|
6
|
+
const HTTP_LIBRARY = 'http'
|
|
7
|
+
const SPAN_KIND_ATTRIBUTE = 'client'
|
|
5
8
|
const CATEGORIES = {
|
|
6
|
-
|
|
9
|
+
HTTP: 'http',
|
|
7
10
|
DATASTORE: 'datastore',
|
|
8
11
|
GENERIC: 'generic'
|
|
9
12
|
}
|
|
@@ -25,8 +28,7 @@ class SpanEvent {
|
|
|
25
28
|
this.traceId = null
|
|
26
29
|
this.guid = null
|
|
27
30
|
this.parentId = null
|
|
28
|
-
this.
|
|
29
|
-
this.appLocalRootId = null
|
|
31
|
+
this.transactionId = null
|
|
30
32
|
this.sampled = null
|
|
31
33
|
this.priority = null
|
|
32
34
|
this.name = null
|
|
@@ -45,8 +47,8 @@ class SpanEvent {
|
|
|
45
47
|
return DatastoreSpanEvent
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
static get
|
|
49
|
-
return
|
|
50
|
+
static get HttpSpanEvent() {
|
|
51
|
+
return HttpSpanEvent
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
/**
|
|
@@ -55,14 +57,15 @@ class SpanEvent {
|
|
|
55
57
|
* The constructed span event will contain extra data depending on the
|
|
56
58
|
* category of the segment.
|
|
57
59
|
*
|
|
58
|
-
* @param {TraceSegment}
|
|
60
|
+
* @param {TraceSegment} segment - The segment to turn into a span event.
|
|
61
|
+
* @param {?string} [parentId=null] - The ID of the segment's parent.
|
|
59
62
|
*
|
|
60
63
|
* @return {SpanEvent} The constructed event.
|
|
61
64
|
*/
|
|
62
|
-
static fromSegment(segment, parentId
|
|
65
|
+
static fromSegment(segment, parentId) {
|
|
63
66
|
let span = null
|
|
64
|
-
if (
|
|
65
|
-
span = new
|
|
67
|
+
if (HttpSpanEvent.testSegment(segment)) {
|
|
68
|
+
span = new HttpSpanEvent(segment.parameters)
|
|
66
69
|
} else if (DatastoreSpanEvent.testSegment(segment)) {
|
|
67
70
|
span = new DatastoreSpanEvent(segment.parameters)
|
|
68
71
|
} else {
|
|
@@ -74,12 +77,16 @@ class SpanEvent {
|
|
|
74
77
|
span.traceId = tx.traceId || tx.id
|
|
75
78
|
span.guid = segment.id
|
|
76
79
|
span.parentId = parentId || null
|
|
77
|
-
|
|
78
|
-
span.
|
|
80
|
+
|
|
81
|
+
span.transactionId = tx.id
|
|
79
82
|
span.sampled = tx.sampled
|
|
80
83
|
span.priority = tx.priority
|
|
81
84
|
span.name = segment.name
|
|
82
85
|
|
|
86
|
+
if (tx.baseSegment === segment) {
|
|
87
|
+
span['nr.entryPoint'] = true
|
|
88
|
+
}
|
|
89
|
+
|
|
83
90
|
// Timestamp in milliseconds, duration in seconds. Yay consistency!
|
|
84
91
|
span.timestamp = segment.timer.start
|
|
85
92
|
span.duration = segment.timer.getDurationInMillis() / 1000
|
|
@@ -88,8 +95,15 @@ class SpanEvent {
|
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
toJSON() {
|
|
98
|
+
const attrs = Object.create(null)
|
|
99
|
+
for (let key in this) {
|
|
100
|
+
if (this[key] != null) {
|
|
101
|
+
attrs[key] = this[key]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
91
105
|
return [
|
|
92
|
-
|
|
106
|
+
attrs,
|
|
93
107
|
EMPTY_USER_ATTRS,
|
|
94
108
|
EMPTY_AGENT_ATTRS
|
|
95
109
|
]
|
|
@@ -102,17 +116,18 @@ class SpanEvent {
|
|
|
102
116
|
* @private
|
|
103
117
|
* @class
|
|
104
118
|
*/
|
|
105
|
-
class
|
|
119
|
+
class HttpSpanEvent extends SpanEvent {
|
|
106
120
|
constructor(parameters) {
|
|
107
121
|
super()
|
|
108
122
|
|
|
109
|
-
this.category = CATEGORIES.
|
|
110
|
-
this.
|
|
123
|
+
this.category = CATEGORIES.HTTP
|
|
124
|
+
this.component = parameters.library || HTTP_LIBRARY
|
|
125
|
+
this['span.kind'] = SPAN_KIND_ATTRIBUTE
|
|
111
126
|
if (parameters.url) {
|
|
112
|
-
this.
|
|
127
|
+
this['http.url'] = parameters.url
|
|
113
128
|
}
|
|
114
129
|
if (parameters.procedure) {
|
|
115
|
-
this.
|
|
130
|
+
this['http.method'] = parameters.procedure
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
|
|
@@ -132,23 +147,24 @@ class DatastoreSpanEvent extends SpanEvent {
|
|
|
132
147
|
super()
|
|
133
148
|
|
|
134
149
|
this.category = CATEGORIES.DATASTORE
|
|
150
|
+
this['span.kind'] = SPAN_KIND_ATTRIBUTE
|
|
135
151
|
if (parameters.product) {
|
|
136
|
-
this.
|
|
152
|
+
this.component = parameters.product
|
|
137
153
|
}
|
|
138
|
-
if (parameters.
|
|
139
|
-
this.
|
|
154
|
+
if (parameters.sql_obfuscated) {
|
|
155
|
+
this['db.statement'] = _truncate(parameters.sql_obfuscated)
|
|
156
|
+
} else if (parameters.sql) {
|
|
157
|
+
this['db.statement'] = _truncate(parameters.sql)
|
|
140
158
|
}
|
|
141
|
-
if (parameters.
|
|
142
|
-
this.
|
|
159
|
+
if (parameters.database_name) {
|
|
160
|
+
this['db.instance'] = parameters.database_name
|
|
143
161
|
}
|
|
144
162
|
if (parameters.host) {
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (parameters.database_name) {
|
|
151
|
-
this.datastoreName = parameters.database_name
|
|
163
|
+
this['peer.hostname'] = parameters.host
|
|
164
|
+
|
|
165
|
+
if (parameters.port_path_or_id) {
|
|
166
|
+
this['peer.address'] = `${parameters.host}:${parameters.port_path_or_id}`
|
|
167
|
+
}
|
|
152
168
|
}
|
|
153
169
|
}
|
|
154
170
|
|
|
@@ -157,4 +173,12 @@ class DatastoreSpanEvent extends SpanEvent {
|
|
|
157
173
|
}
|
|
158
174
|
}
|
|
159
175
|
|
|
176
|
+
function _truncate(val) {
|
|
177
|
+
let truncated = truncate(val, 1997)
|
|
178
|
+
if (truncated !== val) {
|
|
179
|
+
truncated += '...'
|
|
180
|
+
}
|
|
181
|
+
return truncated
|
|
182
|
+
}
|
|
183
|
+
|
|
160
184
|
module.exports = SpanEvent
|