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
|
@@ -7,6 +7,12 @@ const DT_VERSION_MAJOR = 0
|
|
|
7
7
|
const DT_VERSION_MINOR = 1
|
|
8
8
|
|
|
9
9
|
module.exports = class DistributedTracePayload {
|
|
10
|
+
/**
|
|
11
|
+
* The class reponsible for producing distributed trace payloads.
|
|
12
|
+
* Created by calling {@link TransactionHandle#createDistributedTracePayload}.
|
|
13
|
+
*
|
|
14
|
+
* @constructor
|
|
15
|
+
*/
|
|
10
16
|
constructor(payload) {
|
|
11
17
|
logger.trace('DistributedTracePayload created with %s', payload)
|
|
12
18
|
this.plainTextPayload = JSON.stringify({
|
|
@@ -16,11 +22,21 @@ module.exports = class DistributedTracePayload {
|
|
|
16
22
|
this.base64Payload = null
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @returns {String} The base64 encoded JSON representation of the
|
|
27
|
+
* distributed trace payload.
|
|
28
|
+
*/
|
|
19
29
|
text() {
|
|
20
30
|
logger.trace('DistributedTracePayload text: %s', this.plainTextPayload)
|
|
21
31
|
return this.plainTextPayload
|
|
22
32
|
}
|
|
23
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Construct a payload suitable for HTTP transport.
|
|
36
|
+
*
|
|
37
|
+
* @returns {String} The base64 encoded JSON representation of the
|
|
38
|
+
* distributed trace payload.
|
|
39
|
+
*/
|
|
24
40
|
httpSafe() {
|
|
25
41
|
if (!this.base64Payload) {
|
|
26
42
|
this.base64Payload = makeBuffer(this.plainTextPayload, 'utf-8').toString('base64')
|
|
@@ -33,7 +33,7 @@ module.exports = class TransactionHandle {
|
|
|
33
33
|
*
|
|
34
34
|
* Proxy method for Transaction#createDistrubtedTracePayload.
|
|
35
35
|
*
|
|
36
|
-
* @returns {DistributedTracePayload} The created payload.
|
|
36
|
+
* @returns {DistributedTracePayload} The created payload object.
|
|
37
37
|
*
|
|
38
38
|
*/
|
|
39
39
|
createDistributedTracePayload() {
|
package/lib/transaction/index.js
CHANGED
|
@@ -122,6 +122,7 @@ function Transaction(agent) {
|
|
|
122
122
|
this.traceId = null
|
|
123
123
|
this.parentSpanId = null
|
|
124
124
|
this.isDistributedTrace = null
|
|
125
|
+
this.acceptedDistributedTrace = null
|
|
125
126
|
|
|
126
127
|
// Lazy evaluate the priority and sampling in case we end up accepting a payload.
|
|
127
128
|
this.priority = null
|
|
@@ -192,6 +193,10 @@ Transaction.prototype.end = function end(done) {
|
|
|
192
193
|
transaction.record()
|
|
193
194
|
}
|
|
194
195
|
|
|
196
|
+
// This is function currently must be called after all recorders have been fired due
|
|
197
|
+
// to some of the recorders (namely the db recorders) add parameters to the segments.
|
|
198
|
+
transaction.trace.generateSpanEvents()
|
|
199
|
+
|
|
195
200
|
transaction.agent.emit('transactionFinished', transaction)
|
|
196
201
|
if (typeof done === 'function') {
|
|
197
202
|
done(transaction)
|
|
@@ -687,7 +692,7 @@ Transaction.prototype.getIntrinsicAttributes = function getIntrinsicAttributes()
|
|
|
687
692
|
|
|
688
693
|
// If CAT is enabled, extract the path hashes and referring transaction info.
|
|
689
694
|
if (config.cross_application_tracer.enabled) {
|
|
690
|
-
if (config.
|
|
695
|
+
if (config.distributed_tracing.enabled) {
|
|
691
696
|
this.addDistributedTraceIntrinsics(this._intrinsicAttributes)
|
|
692
697
|
} else {
|
|
693
698
|
this._intrinsicAttributes.path_hash = hashes.calculatePathHash(
|
|
@@ -749,7 +754,7 @@ function acceptDistributedTracePayload(payload, transport) {
|
|
|
749
754
|
}
|
|
750
755
|
|
|
751
756
|
const config = this.agent.config
|
|
752
|
-
const distTraceEnabled = config.
|
|
757
|
+
const distTraceEnabled = config.distributed_tracing.enabled
|
|
753
758
|
const catEnabled = config.cross_application_tracer.enabled
|
|
754
759
|
const trustedAccount = config.trusted_account_key || config.account_id
|
|
755
760
|
|
|
@@ -765,6 +770,23 @@ function acceptDistributedTracePayload(payload, transport) {
|
|
|
765
770
|
return
|
|
766
771
|
}
|
|
767
772
|
|
|
773
|
+
if (!parsed.v || !parsed.d) {
|
|
774
|
+
if (!parsed.v) {
|
|
775
|
+
logger.warn(
|
|
776
|
+
'Received a distributed trace payload with no version field',
|
|
777
|
+
this.id
|
|
778
|
+
)
|
|
779
|
+
}
|
|
780
|
+
if (!parsed.d) {
|
|
781
|
+
logger.warn(
|
|
782
|
+
'Received a distributed trace payload with no data field',
|
|
783
|
+
this.id
|
|
784
|
+
)
|
|
785
|
+
}
|
|
786
|
+
this.agent.recordSupportability('DistributedTrace/AcceptPayload/ParseException')
|
|
787
|
+
return
|
|
788
|
+
}
|
|
789
|
+
|
|
768
790
|
const majorVersion = parsed.v && typeof parsed.v[0] === 'number' && parsed.v[0]
|
|
769
791
|
if (majorVersion == null) {
|
|
770
792
|
logger.warn('Invalid distributed trace payload, not accepting')
|
|
@@ -792,10 +814,10 @@ function acceptDistributedTracePayload(payload, transport) {
|
|
|
792
814
|
return
|
|
793
815
|
}
|
|
794
816
|
|
|
795
|
-
const
|
|
796
|
-
if (
|
|
817
|
+
const trustedAccountKey = data.tk || data.ac
|
|
818
|
+
if (trustedAccountKey !== trustedAccount) {
|
|
797
819
|
this.agent.recordSupportability(
|
|
798
|
-
`DistributedTrace/AcceptPayload/UntrustedAccount
|
|
820
|
+
`DistributedTrace/AcceptPayload/Ignored/UntrustedAccount`
|
|
799
821
|
)
|
|
800
822
|
return
|
|
801
823
|
}
|
|
@@ -804,7 +826,7 @@ function acceptDistributedTracePayload(payload, transport) {
|
|
|
804
826
|
|
|
805
827
|
this.parentType = data.ty
|
|
806
828
|
this.parentApp = data.ap
|
|
807
|
-
this.parentAcct =
|
|
829
|
+
this.parentAcct = data.ac
|
|
808
830
|
this.parentTransportType = transport
|
|
809
831
|
this.parentTransportDuration = Math.max(0, (Date.now() - data.ti) / 1000)
|
|
810
832
|
this.traceId = data.tr
|
|
@@ -823,6 +845,10 @@ function acceptDistributedTracePayload(payload, transport) {
|
|
|
823
845
|
}
|
|
824
846
|
|
|
825
847
|
this.isDistributedTrace = true
|
|
848
|
+
// Track if the distributed trace was created through accepting, since
|
|
849
|
+
// there is potentially no data difference between creation from
|
|
850
|
+
// Mobile or Browser trace payloads and creation.
|
|
851
|
+
this.acceptedDistributedTrace = true
|
|
826
852
|
|
|
827
853
|
this.agent.recordSupportability('DistributedTrace/AcceptPayload/Success')
|
|
828
854
|
}
|
|
@@ -873,7 +899,7 @@ function createDistributedTracePayload() {
|
|
|
873
899
|
const config = this.agent.config
|
|
874
900
|
const accountId = config.account_id
|
|
875
901
|
const appId = config.application_id
|
|
876
|
-
const distTraceEnabled = config.
|
|
902
|
+
const distTraceEnabled = config.distributed_tracing.enabled
|
|
877
903
|
const catEnabled = config.cross_application_tracer.enabled
|
|
878
904
|
|
|
879
905
|
if (!accountId || !appId || !distTraceEnabled || !catEnabled) {
|
|
@@ -898,7 +924,7 @@ function createDistributedTracePayload() {
|
|
|
898
924
|
ti: Date.now()
|
|
899
925
|
}
|
|
900
926
|
|
|
901
|
-
if (this.agent.config.span_events.enabled && currSegment) {
|
|
927
|
+
if (this.agent.config.span_events.enabled && this.sampled && currSegment) {
|
|
902
928
|
data.id = currSegment.id
|
|
903
929
|
}
|
|
904
930
|
|
|
@@ -918,7 +944,8 @@ function createDistributedTracePayload() {
|
|
|
918
944
|
Transaction.prototype.addDistributedTraceIntrinsics = addDistributedTraceIntrinsics
|
|
919
945
|
function addDistributedTraceIntrinsics(attrs) {
|
|
920
946
|
this._calculatePriority()
|
|
921
|
-
|
|
947
|
+
|
|
948
|
+
// *always* add these if DT flag is enabled.
|
|
922
949
|
attrs.traceId = this.traceId || this.id
|
|
923
950
|
attrs.guid = this.id
|
|
924
951
|
attrs.priority = this.priority
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const byteUtils = require('../../util/byte-limit')
|
|
4
|
+
|
|
3
5
|
module.exports = TraceAttributes
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -20,7 +22,7 @@ function TraceAttributes(opts) {
|
|
|
20
22
|
*/
|
|
21
23
|
TraceAttributes.prototype.isValidLength = isValidLength
|
|
22
24
|
function isValidLength(str) {
|
|
23
|
-
return
|
|
25
|
+
return byteUtils.isValidLength(str, 256)
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
@@ -55,7 +57,10 @@ TraceAttributes.prototype.get = function get(dest) {
|
|
|
55
57
|
continue
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
attrs[key] = typeof attr.value === 'string'
|
|
60
|
+
attrs[key] = typeof attr.value === 'string'
|
|
61
|
+
? byteUtils.truncate(attr.value, 255)
|
|
62
|
+
: attr.value
|
|
63
|
+
|
|
59
64
|
if (++attrCount >= this.limit) {
|
|
60
65
|
break
|
|
61
66
|
}
|
|
@@ -83,31 +88,6 @@ TraceAttributes.prototype.reset = function reset() {
|
|
|
83
88
|
this.attributes = Object.create(null)
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
/**
|
|
87
|
-
* Trims a string value to 255 bytes, if necessary.
|
|
88
|
-
*
|
|
89
|
-
* @private
|
|
90
|
-
*
|
|
91
|
-
* @param {string} val - The value to truncate to 255 bytes.
|
|
92
|
-
*
|
|
93
|
-
* @return {string} The truncated value.
|
|
94
|
-
*/
|
|
95
|
-
function truncate(val) {
|
|
96
|
-
// First truncation handles the simple case of only one-byte characters.
|
|
97
|
-
if (!isValidLength(val)) {
|
|
98
|
-
val = val.substring(0, 255)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Our limitation is on byte length, and the string could contain multi-byte
|
|
102
|
-
// characters. Doing a byte-substring could chop a character in half. We need
|
|
103
|
-
// to pop the remaining characters off one by one until we have a good length.
|
|
104
|
-
var l = val.length
|
|
105
|
-
while (!isValidLength(val)) {
|
|
106
|
-
val = val.substring(0, --l)
|
|
107
|
-
}
|
|
108
|
-
return val
|
|
109
|
-
}
|
|
110
|
-
|
|
111
91
|
/**
|
|
112
92
|
* Checks incoming attribute value against valid types:
|
|
113
93
|
* string, number, boolean, & null.
|
|
@@ -49,72 +49,63 @@ function Trace(transaction) {
|
|
|
49
49
|
* segments that support recording.
|
|
50
50
|
*/
|
|
51
51
|
Trace.prototype.end = function end() {
|
|
52
|
-
var
|
|
53
|
-
var segments = []
|
|
54
|
-
var processFn
|
|
55
|
-
|
|
56
|
-
if (config.span_events.enabled && config.feature_flag.distributed_tracing) {
|
|
57
|
-
segments.push(new DTTraceNode(
|
|
58
|
-
this.root,
|
|
59
|
-
this.transaction.id,
|
|
60
|
-
this.transaction.parentId || null
|
|
61
|
-
))
|
|
62
|
-
processFn = makeDTNodeProcessor(this.transaction.agent.spans)
|
|
63
|
-
} else {
|
|
64
|
-
segments.push(this.root)
|
|
65
|
-
processFn = processNode
|
|
66
|
-
}
|
|
52
|
+
var segments = [this.root]
|
|
67
53
|
|
|
68
54
|
while (segments.length) {
|
|
69
55
|
var segment = segments.pop()
|
|
70
|
-
|
|
56
|
+
_softEndSegment(segment)
|
|
57
|
+
Array.prototype.push.apply(segments, segment.getChildren())
|
|
71
58
|
}
|
|
72
59
|
}
|
|
73
60
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
function makeDTNodeProcessor(spanAggregator) {
|
|
81
|
-
var addMoreSpans = true
|
|
82
|
-
return function processNodeDT(segmentInfo, segments) {
|
|
83
|
-
var segment = segmentInfo.segment
|
|
61
|
+
/**
|
|
62
|
+
* Iterates over the trace tree and generates a span event for each segment.
|
|
63
|
+
*/
|
|
64
|
+
Trace.prototype.generateSpanEvents = function generateSpanEvents() {
|
|
65
|
+
var config = this.transaction.agent.config
|
|
84
66
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
67
|
+
if (
|
|
68
|
+
this.transaction.sampled &&
|
|
69
|
+
config.span_events.enabled &&
|
|
70
|
+
config.distributed_tracing.enabled
|
|
71
|
+
) {
|
|
72
|
+
var toProcess = []
|
|
73
|
+
// Root segment does not become a span, so we need to process it separately.
|
|
74
|
+
const spanAggregator = this.transaction.agent.spans
|
|
75
|
+
const children = this.root.getChildren()
|
|
76
|
+
for (let i = 0; i < children.length; ++i) {
|
|
77
|
+
toProcess.push(new DTTraceNode(children[i], this.transaction.parentSpanId))
|
|
90
78
|
}
|
|
91
79
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
while (toProcess.length) {
|
|
81
|
+
var segmentInfo = toProcess.pop()
|
|
82
|
+
var segment = segmentInfo.segment
|
|
83
|
+
|
|
84
|
+
// Even though at some point we might want to stop adding events
|
|
85
|
+
// because all the priorities should be the same, we need to count
|
|
86
|
+
// the spans as seen.
|
|
87
|
+
spanAggregator.addSegment(segment, segmentInfo.parentId)
|
|
88
|
+
|
|
89
|
+
Array.prototype.push.apply(
|
|
90
|
+
toProcess,
|
|
91
|
+
segment.getChildren().map(child => new DTTraceNode(child, segment.id))
|
|
99
92
|
)
|
|
100
93
|
}
|
|
101
|
-
|
|
102
|
-
var children = segment.getChildren()
|
|
103
|
-
Array.prototype.push.apply(
|
|
104
|
-
segments,
|
|
105
|
-
children.map(child => new DTTraceNode(child, segment.id, segmentInfo.parentId))
|
|
106
|
-
)
|
|
107
94
|
}
|
|
108
95
|
}
|
|
109
96
|
|
|
110
|
-
function
|
|
97
|
+
function DTTraceNode(segment, parentId) {
|
|
98
|
+
this.segment = segment
|
|
99
|
+
this.parentId = parentId
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function _softEndSegment(segment) {
|
|
111
103
|
if (segment.timer.softEnd()) {
|
|
112
104
|
segment._updateRootTimer()
|
|
113
105
|
// timer.softEnd() returns true if the timer was ended prematurely, so
|
|
114
106
|
// in that case we can name the segment as truncated
|
|
115
107
|
segment.name = NAMES.TRUNCATED.PREFIX + segment.name
|
|
116
108
|
}
|
|
117
|
-
Array.prototype.push.apply(segments, segment.getChildren())
|
|
118
109
|
}
|
|
119
110
|
|
|
120
111
|
/**
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a given string is within agent attribute limits.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} str - Object key name or value
|
|
7
|
+
* @param {number} limit - String byte limit
|
|
8
|
+
*/
|
|
9
|
+
function isValidLength(str, limit) {
|
|
10
|
+
return Buffer.byteLength(str, 'utf8') < limit
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Trims a string value to given byte limit, if necessary.
|
|
15
|
+
*
|
|
16
|
+
* @private
|
|
17
|
+
*
|
|
18
|
+
* @param {string} val - The value to truncate to given byte limit.
|
|
19
|
+
* @param {number} limit - The byte limit
|
|
20
|
+
*
|
|
21
|
+
* @return {string} The truncated value.
|
|
22
|
+
*/
|
|
23
|
+
function truncate(val, limit) {
|
|
24
|
+
// First truncation handles the simple case of only one-byte characters.
|
|
25
|
+
if (!isValidLength(val + 1)) {
|
|
26
|
+
val = val.substring(0, limit)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Our limitation is on byte length, and the string could contain multi-byte
|
|
30
|
+
// characters. Doing a byte-substring could chop a character in half. We need
|
|
31
|
+
// to pop the remaining characters off one by one until we have a good length.
|
|
32
|
+
var l = val.length
|
|
33
|
+
while (!isValidLength(val, limit + 1)) {
|
|
34
|
+
val = val.substring(0, --l)
|
|
35
|
+
}
|
|
36
|
+
return val
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports.isValidLength = isValidLength
|
|
40
|
+
module.exports.truncate = truncate
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"licenses": [
|
|
6
6
|
{
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
],
|
|
97
97
|
"homepage": "http://github.com/newrelic/node-newrelic",
|
|
98
98
|
"engines": {
|
|
99
|
-
"node": ">=4.0.0",
|
|
99
|
+
"node": ">=4.0.0 <11.0.0",
|
|
100
100
|
"npm": ">=2.0.0"
|
|
101
101
|
},
|
|
102
102
|
"directories": {
|
package/.npmignore
DELETED