newrelic 6.5.0 → 6.6.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/.eslintignore +1 -1
- package/NEWS.md +20 -0
- package/THIRD_PARTY_NOTICES.md +420 -0
- package/lib/agent.js +22 -8
- package/lib/collector/serverless.js +8 -0
- package/lib/config/default.js +23 -1
- package/lib/config/env.js +7 -1
- package/lib/config/index.js +7 -0
- package/lib/grpc/connection/states.js +9 -0
- package/lib/grpc/connection.js +344 -0
- package/lib/grpc/endpoints/infinite-tracing/v1.proto +29 -0
- package/lib/metrics/names.js +12 -1
- package/lib/proxy/grpc.js +15 -0
- package/lib/serverless/aws-lambda.js +2 -0
- package/lib/spans/create-span-event-aggregator.js +112 -0
- package/lib/spans/map-to-streaming-type.js +44 -0
- package/lib/spans/span-event-aggregator.js +5 -0
- package/lib/spans/span-streamer.js +109 -0
- package/lib/spans/streaming-span-attributes.js +59 -0
- package/lib/spans/streaming-span-event-aggregator.js +98 -0
- package/lib/spans/streaming-span-event.js +261 -0
- package/lib/transaction/trace/index.js +11 -8
- package/package.json +5 -3
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const logger = require('../logger').child({component: 'span-streamer'})
|
|
4
|
+
|
|
5
|
+
const NAMES = require('../metrics/names')
|
|
6
|
+
|
|
7
|
+
const NO_STREAM_WARNING =
|
|
8
|
+
'Attempting to stream spans before connection created. ' +
|
|
9
|
+
'This warning will not appear again this agent run.'
|
|
10
|
+
|
|
11
|
+
const BACK_PRESSURE_WARNING =
|
|
12
|
+
'Back pressure detected in SpanStreamer! Spans will be dropped until the current batch ' +
|
|
13
|
+
'has fully sent. Will not warn again for %s seconds.'
|
|
14
|
+
const BACK_PRESSURE_WARNING_INTERVAL = 60 // in seconds
|
|
15
|
+
|
|
16
|
+
const BACK_PRESSURE_STOP = 'Back pressure has ended, continuing to stream'
|
|
17
|
+
|
|
18
|
+
class SpanStreamer {
|
|
19
|
+
constructor(license_key, connection, metrics) {
|
|
20
|
+
this.stream = null
|
|
21
|
+
this.license_key = license_key
|
|
22
|
+
this.connection = connection
|
|
23
|
+
this._metrics = metrics
|
|
24
|
+
this._writable = false
|
|
25
|
+
|
|
26
|
+
// 'connected' indicates a safely writeable stream.
|
|
27
|
+
// May still be mid-connect to gRPC server.
|
|
28
|
+
this.connection.on('connected', (stream) =>{
|
|
29
|
+
this.stream = stream
|
|
30
|
+
this._writable = true
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
this.connection.on('disconnected', () =>{
|
|
34
|
+
this.stream = null
|
|
35
|
+
this._writable = false
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
write(span) {
|
|
40
|
+
if (!this.stream) {
|
|
41
|
+
logger.warnOnce(NO_STREAM_WARNING)
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!this._writable) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const formattedSpan = span.toStreamingFormat()
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// false indicates the stream has reached the highWaterMark
|
|
53
|
+
// and future writes should be avoided until drained. written items,
|
|
54
|
+
// including the one that returned false, will still be buffered.
|
|
55
|
+
this._writable = this.stream.write(formattedSpan)
|
|
56
|
+
|
|
57
|
+
if (!this._writable) {
|
|
58
|
+
logger.infoOncePer(
|
|
59
|
+
'BACK_PRESSURE_START',
|
|
60
|
+
BACK_PRESSURE_WARNING_INTERVAL * 1000,
|
|
61
|
+
BACK_PRESSURE_WARNING,
|
|
62
|
+
BACK_PRESSURE_WARNING_INTERVAL
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const waitDrainStart = Date.now()
|
|
66
|
+
|
|
67
|
+
this.stream.once('drain', () => {
|
|
68
|
+
const drainCompleted = Date.now()
|
|
69
|
+
const drainDurationMs = drainCompleted - waitDrainStart
|
|
70
|
+
|
|
71
|
+
// Metric can be used to see how frequently completing drains
|
|
72
|
+
// as well as average time to drain from when we first notice.
|
|
73
|
+
this._metrics.getOrCreateMetric(NAMES.INFINITE_TRACING.DRAIN_DURATION)
|
|
74
|
+
.recordValue(drainDurationMs / 1000)
|
|
75
|
+
|
|
76
|
+
logger.trace(BACK_PRESSURE_STOP)
|
|
77
|
+
this._writable = true
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// span was added to internal node stream buffer to be sent while draining
|
|
82
|
+
// so we return true even when we should not write anymore afterwards
|
|
83
|
+
return true
|
|
84
|
+
} catch (err) {
|
|
85
|
+
logger.trace('Could not stream span.', err)
|
|
86
|
+
// TODO: something has gone horribly wrong.
|
|
87
|
+
// We may want to log and turn off this aggregator
|
|
88
|
+
// to prevent sending further spans. Maybe even "disable" their creation?
|
|
89
|
+
// or is there a situation where we can recover?
|
|
90
|
+
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
connect(agent_run_id) {
|
|
96
|
+
this.connection.setConnectionDetails(
|
|
97
|
+
this.license_key,
|
|
98
|
+
agent_run_id
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
this.connection.connectSpans()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
disconnect() {
|
|
105
|
+
this.connection.disconnect()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = SpanStreamer
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const mapToStreamingType = require('./map-to-streaming-type')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Specialized attribute collection class for use with infinite streaming.
|
|
7
|
+
* Currently designed to be sent over grpc via the v1.proto definition.
|
|
8
|
+
*
|
|
9
|
+
* @private
|
|
10
|
+
* @class
|
|
11
|
+
*/
|
|
12
|
+
class StreamingSpanAttributes {
|
|
13
|
+
constructor(attributes) {
|
|
14
|
+
if (attributes) {
|
|
15
|
+
this.addAttributes(attributes)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Add a key/value pair to the attribute collection.
|
|
21
|
+
* null/undefined values will be dropped.
|
|
22
|
+
*
|
|
23
|
+
* Does not apply filtering/truncation.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} key Name of the attribute to be stored.
|
|
26
|
+
* @param {string|boolean|number} value Value of the attribute to be stored.
|
|
27
|
+
*/
|
|
28
|
+
addAttribute(key, value) {
|
|
29
|
+
const streamingValue = mapToStreamingType(value)
|
|
30
|
+
if (streamingValue) {
|
|
31
|
+
this[key] = streamingValue
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Adds all attributes in an object to the attribute collection.
|
|
40
|
+
* null/undefined values will be dropped.
|
|
41
|
+
*
|
|
42
|
+
* Does not apply filtering/truncation.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} [attributes]
|
|
45
|
+
* @param {string} [attributes.key] Name of the attribute to be stored.
|
|
46
|
+
* @param {string|boolean|number} [attributes.value] Value of the attribute to be stored.
|
|
47
|
+
*/
|
|
48
|
+
addAttributes(attributes) {
|
|
49
|
+
if (!attributes) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (let [key, value] of Object.entries(attributes)) {
|
|
54
|
+
this.addAttribute(key, value)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = StreamingSpanAttributes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Aggregator = require('../aggregators/base-aggregator')
|
|
4
|
+
const StreamingSpanEvent = require('./streaming-span-event')
|
|
5
|
+
const NAMES = require('../metrics/names')
|
|
6
|
+
const logger = require('../logger').child({component: 'streaming-span-event-aggregator'})
|
|
7
|
+
|
|
8
|
+
const SEND_WARNING =
|
|
9
|
+
'send() is not currently supported on streaming span event aggregator. ' +
|
|
10
|
+
'This warning will not appear again this agent run.'
|
|
11
|
+
|
|
12
|
+
// TODO: this doesn't "aggregate". Perhaps we need a different terminology
|
|
13
|
+
// for the base-class and then this implementation can avoid the misleading language.
|
|
14
|
+
class StreamingSpanEventAggregator extends Aggregator {
|
|
15
|
+
constructor(opts, collector, metrics) {
|
|
16
|
+
opts = opts || {}
|
|
17
|
+
opts.periodMs = opts.periodMs ? opts.periodMs : 1000
|
|
18
|
+
opts.limit = opts.limit ? opts.limit : 10000
|
|
19
|
+
opts.method = opts.method || 'span_event_data'
|
|
20
|
+
|
|
21
|
+
super(opts, collector)
|
|
22
|
+
|
|
23
|
+
this.metricNames = opts.metricNames || NAMES.INFINITE_TRACING
|
|
24
|
+
this.stream = opts.span_streamer
|
|
25
|
+
this.metrics = metrics
|
|
26
|
+
this.started = false
|
|
27
|
+
this.isStream = true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
start() {
|
|
31
|
+
if (this.started) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
logger.trace('StreamingSpanEventAggregator starting up')
|
|
36
|
+
this.stream.connect(this.runId)
|
|
37
|
+
this.started = true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
stop() {
|
|
41
|
+
if (!this.started) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
logger.trace('StreamingSpanEventAggregator stopping')
|
|
46
|
+
this.stream.disconnect()
|
|
47
|
+
this.started = false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
send() {
|
|
51
|
+
// Only log once started. This will get invoked on initial harvest
|
|
52
|
+
// prior to start which we'll just ignore.
|
|
53
|
+
if (this.started) {
|
|
54
|
+
logger.warnOnce(SEND_WARNING)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.emit(`finished ${this.method} data send.`)
|
|
58
|
+
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Not a payload based aggregator
|
|
64
|
+
*
|
|
65
|
+
* This is here to implement the implicit interface
|
|
66
|
+
*/
|
|
67
|
+
_toPayloadSync() {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Attempts to add the given segment to the collection.
|
|
73
|
+
*
|
|
74
|
+
* @param {TraceSegment} segment - The segment to add.
|
|
75
|
+
* @param {string} [parentId=null] - The GUID of the parent span.
|
|
76
|
+
*
|
|
77
|
+
* @return {bool} True if the segment was added, or false if it was discarded.
|
|
78
|
+
*/
|
|
79
|
+
addSegment(segment, parentId, isRoot) {
|
|
80
|
+
if (!this.started) {
|
|
81
|
+
logger.trace('Aggregator has not yet started, dropping span (%s).', segment.name)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// SEEN/SENT are to understand where we've had to drop spans due back-pressure, errors,
|
|
86
|
+
// reconnects, etc. so moving after start check to avoid logging for a
|
|
87
|
+
// currently unsupported case.
|
|
88
|
+
this.metrics.getOrCreateMetric(this.metricNames.SEEN).incrementCallCount()
|
|
89
|
+
|
|
90
|
+
const span = StreamingSpanEvent.fromSegment(segment, parentId, isRoot)
|
|
91
|
+
|
|
92
|
+
if (this.stream.write(span)) {
|
|
93
|
+
this.metrics.getOrCreateMetric(this.metricNames.SENT).incrementCallCount()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = StreamingSpanEventAggregator
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const StreamingSpanAttributes = require('./streaming-span-attributes')
|
|
4
|
+
const {truncate} = require('../util/byte-limit')
|
|
5
|
+
const Config = require('../config')
|
|
6
|
+
|
|
7
|
+
const {DESTINATIONS} = require('../config/attribute-filter')
|
|
8
|
+
const NAMES = require('../metrics/names')
|
|
9
|
+
const HTTP_LIBRARY = 'http'
|
|
10
|
+
const CLIENT_KIND = 'client'
|
|
11
|
+
const CATEGORIES = {
|
|
12
|
+
HTTP: 'http',
|
|
13
|
+
DATASTORE: 'datastore',
|
|
14
|
+
GENERIC: 'generic'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Specialized span event class for use with infinite streaming.
|
|
19
|
+
* Currently designed to be sent over grpc via the v1.proto definition.
|
|
20
|
+
*
|
|
21
|
+
* @private
|
|
22
|
+
* @class
|
|
23
|
+
*/
|
|
24
|
+
class StreamingSpanEvent {
|
|
25
|
+
/**
|
|
26
|
+
* @param {*} traceId TraceId for the Span.
|
|
27
|
+
* @param {object} agentAttributes Initial set of agent attributes.
|
|
28
|
+
* Must be pre-filtered and truncated.
|
|
29
|
+
* @param {object} customAttributes Initial set of custom attributes.
|
|
30
|
+
* Must be pre-filtered and truncated.
|
|
31
|
+
*/
|
|
32
|
+
constructor(traceId, agentAttributes, customAttributes) {
|
|
33
|
+
this._traceId = traceId
|
|
34
|
+
|
|
35
|
+
this._intrinsicAttributes = new StreamingSpanAttributes()
|
|
36
|
+
this._intrinsicAttributes.addAttribute('traceId', traceId)
|
|
37
|
+
this._intrinsicAttributes.addAttribute('type', 'Span')
|
|
38
|
+
this._intrinsicAttributes.addAttribute('category', CATEGORIES.GENERIC)
|
|
39
|
+
|
|
40
|
+
this._customAttributes = new StreamingSpanAttributes(customAttributes)
|
|
41
|
+
this._agentAttributes = new StreamingSpanAttributes(agentAttributes)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Add a key/value pair to the Span's instrinisics collection.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} key Name of the attribute to be stored.
|
|
48
|
+
* @param {string|boolean|number} value Value of the attribute to be stored.
|
|
49
|
+
*/
|
|
50
|
+
addIntrinsicAttribute(key, value) {
|
|
51
|
+
this._intrinsicAttributes.addAttribute(key, value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Add a key/value pair to the Span's custom/user attributes collection.
|
|
56
|
+
* @param {string} key Name of the attribute to be stored.
|
|
57
|
+
* @param {string|boolean|number} value Value of the attribute to be stored.
|
|
58
|
+
* @param {boolean} [truncateExempt=false] Set to true if attribute should not be truncated.
|
|
59
|
+
*/
|
|
60
|
+
addCustomAttribute(key, value, truncateExempt = false) {
|
|
61
|
+
const shouldKeep = this._checkFilter(key)
|
|
62
|
+
if (shouldKeep) {
|
|
63
|
+
const processedValue = truncateExempt ? value : _truncate(value)
|
|
64
|
+
this._customAttributes.addAttribute(key, processedValue)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Add a key/value pair to the Span's agent attributes collection.
|
|
70
|
+
* @param {string} key Name of the attribute to be stored.
|
|
71
|
+
* @param {string|boolean|number} value Value of the attribute to be stored.
|
|
72
|
+
* @param {boolean} [truncateExempt=false] Set to true if attribute should not be truncated.
|
|
73
|
+
*/
|
|
74
|
+
addAgentAttribute(key, value, truncateExempt = false) {
|
|
75
|
+
const shouldKeep = this._checkFilter(key)
|
|
76
|
+
if (shouldKeep) {
|
|
77
|
+
const processedValue = truncateExempt ? value : _truncate(value)
|
|
78
|
+
this._agentAttributes.addAttribute(key, processedValue)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_checkFilter(key) {
|
|
83
|
+
const {attributeFilter} = Config.getInstance()
|
|
84
|
+
const dest = attributeFilter.filterSegment(DESTINATIONS.SPAN_EVENT, key)
|
|
85
|
+
return dest & DESTINATIONS.SPAN_EVENT
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
toStreamingFormat() {
|
|
89
|
+
// Attributes are pre-formatted.
|
|
90
|
+
const formatted = {
|
|
91
|
+
trace_id: this._traceId,
|
|
92
|
+
intrinsics: this._intrinsicAttributes,
|
|
93
|
+
user_attributes: this._customAttributes,
|
|
94
|
+
agent_attributes: this._agentAttributes
|
|
95
|
+
}
|
|
96
|
+
return formatted
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static fromSegment(segment, parentId = null, isRoot = false) {
|
|
100
|
+
const agentAttributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
|
|
101
|
+
const customAttributes = segment.customAttributes.get(DESTINATIONS.SPAN_EVENT)
|
|
102
|
+
|
|
103
|
+
const transaction = segment.transaction
|
|
104
|
+
const traceId = transaction.traceId
|
|
105
|
+
|
|
106
|
+
let span = null
|
|
107
|
+
if (StreamingHttpSpanEvent.isHttpSegment(segment)) {
|
|
108
|
+
span = new StreamingHttpSpanEvent(traceId, agentAttributes, customAttributes)
|
|
109
|
+
} else if (StreamingDatastoreSpanEvent.isDatastoreSegment(segment)) {
|
|
110
|
+
span = new StreamingDatastoreSpanEvent(traceId, agentAttributes, customAttributes)
|
|
111
|
+
} else {
|
|
112
|
+
span = new StreamingSpanEvent(traceId, agentAttributes, customAttributes)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
span.addIntrinsicAttribute('guid', segment.id)
|
|
116
|
+
span.addIntrinsicAttribute('parentId', parentId)
|
|
117
|
+
span.addIntrinsicAttribute('transactionId', transaction.id)
|
|
118
|
+
span.addIntrinsicAttribute('sampled', transaction.sampled)
|
|
119
|
+
span.addIntrinsicAttribute('priority', transaction.priority)
|
|
120
|
+
span.addIntrinsicAttribute('name', segment.name)
|
|
121
|
+
|
|
122
|
+
if (isRoot) {
|
|
123
|
+
span.addIntrinsicAttribute('trustedParentId', transaction.traceContext.trustedParentId)
|
|
124
|
+
if (transaction.traceContext.tracingVendors) {
|
|
125
|
+
span.addIntrinsicAttribute('tracingVendors', transaction.traceContext.tracingVendors)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Only set this if it will be `true`. Must be `null` otherwise.
|
|
130
|
+
if (transaction.baseSegment === segment) {
|
|
131
|
+
span.addIntrinsicAttribute('nr.entryPoint', true)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Timestamp in milliseconds, duration in seconds. Yay consistency!
|
|
135
|
+
span.addIntrinsicAttribute('timestamp', segment.timer.start)
|
|
136
|
+
span.addIntrinsicAttribute('duration', segment.timer.getDurationInMillis() / 1000)
|
|
137
|
+
|
|
138
|
+
return span
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Specialized span event class for external requests for use with infinite streaming.
|
|
144
|
+
* Currently designed to be sent over grpc via the v1.proto definition.
|
|
145
|
+
*
|
|
146
|
+
* @private
|
|
147
|
+
* @class
|
|
148
|
+
*/
|
|
149
|
+
class StreamingHttpSpanEvent extends StreamingSpanEvent {
|
|
150
|
+
/**
|
|
151
|
+
* @param {*} traceId TraceId for the Span.
|
|
152
|
+
* @param {object} agentAttributes Initial set of agent attributes.
|
|
153
|
+
* Must be pre-filtered and truncated.
|
|
154
|
+
* @param {object} customAttributes Initial set of custom attributes.
|
|
155
|
+
* Must be pre-filtered and truncated.
|
|
156
|
+
*/
|
|
157
|
+
constructor(traceId, agentAttributes, customAttributes) {
|
|
158
|
+
super(traceId, agentAttributes, customAttributes)
|
|
159
|
+
|
|
160
|
+
this.addIntrinsicAttribute('category', CATEGORIES.HTTP)
|
|
161
|
+
this.addIntrinsicAttribute('component', agentAttributes.library || HTTP_LIBRARY)
|
|
162
|
+
this.addIntrinsicAttribute('span.kind', CLIENT_KIND)
|
|
163
|
+
|
|
164
|
+
if (agentAttributes.library) {
|
|
165
|
+
agentAttributes.library = null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (agentAttributes.url) {
|
|
169
|
+
this.addAgentAttribute('http.url', agentAttributes.url)
|
|
170
|
+
agentAttributes.url = null
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (agentAttributes.procedure) {
|
|
174
|
+
this.addAgentAttribute('http.method', agentAttributes.procedure)
|
|
175
|
+
agentAttributes.procedure = null
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static isHttpSegment(segment) {
|
|
180
|
+
return segment.name.startsWith(NAMES.EXTERNAL.PREFIX)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Specialized span event class for datastore operations and queries for use with
|
|
186
|
+
* infinite streaming.
|
|
187
|
+
* Currently designed to be sent over grpc via the v1.proto definition.
|
|
188
|
+
*
|
|
189
|
+
* @private
|
|
190
|
+
* @class.
|
|
191
|
+
*/
|
|
192
|
+
class StreamingDatastoreSpanEvent extends StreamingSpanEvent {
|
|
193
|
+
/**
|
|
194
|
+
* @param {*} traceId TraceId for the Span.
|
|
195
|
+
* @param {object} agentAttributes Initial set of agent attributes.
|
|
196
|
+
* Must be pre-filtered and truncated.
|
|
197
|
+
* @param {object} customAttributes Initial set of custom attributes.
|
|
198
|
+
* Must be pre-filtered and truncated.
|
|
199
|
+
*/
|
|
200
|
+
constructor(traceId, agentAttributes, customAttributes) {
|
|
201
|
+
super(traceId, agentAttributes, customAttributes)
|
|
202
|
+
|
|
203
|
+
this.addIntrinsicAttribute('category', CATEGORIES.DATASTORE)
|
|
204
|
+
this.addIntrinsicAttribute('span.kind', CLIENT_KIND)
|
|
205
|
+
|
|
206
|
+
if (agentAttributes.product) {
|
|
207
|
+
this.addIntrinsicAttribute('component', agentAttributes.product)
|
|
208
|
+
agentAttributes.product = null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (agentAttributes.collection) {
|
|
212
|
+
this.addAgentAttribute('db.collection', agentAttributes.collection)
|
|
213
|
+
agentAttributes.collection = null
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (agentAttributes.sql || agentAttributes.sql_obfuscated) {
|
|
217
|
+
let sql = null
|
|
218
|
+
if (agentAttributes.sql_obfuscated) {
|
|
219
|
+
sql = _truncate(agentAttributes.sql_obfuscated)
|
|
220
|
+
agentAttributes.sql_obfuscated = null
|
|
221
|
+
} else if (agentAttributes.sql) {
|
|
222
|
+
sql = _truncate(agentAttributes.sql)
|
|
223
|
+
agentAttributes.sql = null
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Flag as exempt from normal attribute truncation
|
|
227
|
+
this.addAgentAttribute('db.statement', sql, true)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (agentAttributes.database_name) {
|
|
231
|
+
this.addAgentAttribute('db.instance', agentAttributes.database_name)
|
|
232
|
+
agentAttributes.database_name = null
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (agentAttributes.host) {
|
|
236
|
+
this.addAgentAttribute('peer.hostname', agentAttributes.host)
|
|
237
|
+
|
|
238
|
+
if (agentAttributes.port_path_or_id) {
|
|
239
|
+
const address = `${agentAttributes.host}:${agentAttributes.port_path_or_id}`
|
|
240
|
+
this.addAgentAttribute('peer.address', address)
|
|
241
|
+
agentAttributes.port_path_or_id = null
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
agentAttributes.host = null
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
static isDatastoreSegment(segment) {
|
|
249
|
+
return segment.name.startsWith(NAMES.DB.PREFIX)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function _truncate(val) {
|
|
254
|
+
let truncated = truncate(val, 1997)
|
|
255
|
+
if (truncated !== val) {
|
|
256
|
+
truncated += '...'
|
|
257
|
+
}
|
|
258
|
+
return truncated
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = StreamingSpanEvent
|
|
@@ -67,28 +67,31 @@ Trace.prototype.end = function end() {
|
|
|
67
67
|
* Iterates over the trace tree and generates a span event for each segment.
|
|
68
68
|
*/
|
|
69
69
|
Trace.prototype.generateSpanEvents = function generateSpanEvents() {
|
|
70
|
-
|
|
70
|
+
const config = this.transaction.agent.config
|
|
71
|
+
|
|
72
|
+
const infiniteTracingConfigured = Boolean(config.infinite_tracing.trace_observer.host)
|
|
71
73
|
|
|
72
74
|
if (
|
|
73
|
-
this.transaction.sampled &&
|
|
75
|
+
(infiniteTracingConfigured || this.transaction.sampled) &&
|
|
74
76
|
config.span_events.enabled &&
|
|
75
77
|
config.distributed_tracing.enabled
|
|
76
78
|
) {
|
|
77
|
-
|
|
79
|
+
let toProcess = []
|
|
80
|
+
|
|
78
81
|
// Root segment does not become a span, so we need to process it separately.
|
|
79
82
|
const spanAggregator = this.transaction.agent.spanEventAggregator
|
|
83
|
+
|
|
80
84
|
const children = this.root.getChildren()
|
|
81
85
|
for (let i = 0; i < children.length; ++i) {
|
|
82
86
|
toProcess.push(new DTTraceNode(children[i], this.transaction.parentSpanId, true))
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
while (toProcess.length) {
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
let segmentInfo = toProcess.pop()
|
|
91
|
+
let segment = segmentInfo.segment
|
|
88
92
|
|
|
89
|
-
// Even though at some point we might want to stop adding events
|
|
90
|
-
//
|
|
91
|
-
// the spans as seen.
|
|
93
|
+
// Even though at some point we might want to stop adding events because all the priorities
|
|
94
|
+
// should be the same, we need to count the spans as seen.
|
|
92
95
|
spanAggregator.addSegment(segment, segmentInfo.parentId, segmentInfo.isRoot)
|
|
93
96
|
|
|
94
97
|
const nodes = segment.getChildren()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"contributors": [
|
|
@@ -127,7 +127,9 @@
|
|
|
127
127
|
"newrelic-naming-rules": "./bin/test-naming-rules.js"
|
|
128
128
|
},
|
|
129
129
|
"dependencies": {
|
|
130
|
-
"@
|
|
130
|
+
"@grpc/grpc-js": "^0.6.15",
|
|
131
|
+
"@grpc/proto-loader": "^0.5.3",
|
|
132
|
+
"@newrelic/aws-sdk": "^1.1.1",
|
|
131
133
|
"@newrelic/koa": "^3.0.0",
|
|
132
134
|
"@newrelic/superagent": "^2.0.1",
|
|
133
135
|
"@tyriar/fibonacci-heap": "^2.0.7",
|
|
@@ -171,7 +173,7 @@
|
|
|
171
173
|
"rimraf": "^2.6.3",
|
|
172
174
|
"should": "*",
|
|
173
175
|
"sinon": "^4.5.0",
|
|
174
|
-
"tap": "^14.10.
|
|
176
|
+
"tap": "^14.10.6",
|
|
175
177
|
"temp": "^0.8.1",
|
|
176
178
|
"through": "^2.3.6",
|
|
177
179
|
"when": "*"
|