newrelic 6.13.2 → 6.14.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 +28 -2
- package/README.md +14 -5
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/lib/agent.js +4 -0
- package/lib/collector/remote-method.js +17 -11
- package/lib/errors/error-collector.js +6 -1
- package/lib/errors/index.js +14 -1
- package/lib/grpc/connection.js +26 -2
- package/lib/metrics/names.js +4 -1
- package/lib/spans/span-context.js +18 -0
- package/lib/spans/span-event.js +13 -2
- package/lib/spans/span-streamer.js +23 -12
- package/lib/spans/streaming-span-event-aggregator.js +4 -0
- package/lib/spans/streaming-span-event.js +13 -1
- package/lib/transaction/index.js +15 -14
- package/newrelic.js +1 -1
- package/package.json +3 -3
- package/third_party_manifest.json +11 -11
package/NEWS.md
CHANGED
|
@@ -1,4 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
### 6.14.0 (2020-10-28):
|
|
2
|
+
|
|
3
|
+
* Updated README for consistency.
|
|
4
|
+
|
|
5
|
+
* Fixed issue where gRPC connection used for infinite tracing could throw if the server
|
|
6
|
+
shutdown during disconnect of an existing connection.
|
|
7
|
+
|
|
8
|
+
* Bumped @grpc/grpc-js to 1.1.7.
|
|
9
|
+
|
|
10
|
+
* Bumped @grpc/proto-loader to ^0.5.5.
|
|
11
|
+
|
|
12
|
+
* Infinite tracing logging and support metric improvements.
|
|
13
|
+
|
|
14
|
+
* Increased logging level of certain infinite tracing / gRPC errors.
|
|
15
|
+
* Decreased logging interval of dropped span warning for infinite tracing.
|
|
16
|
+
* Added additional support metrics and logging for infinite tracing.
|
|
17
|
+
|
|
18
|
+
* Fixed bug where errors would still be collected for transactions with ignored error
|
|
19
|
+
status codes in certain situations.
|
|
20
|
+
|
|
21
|
+
* Converted errors ignore unit tests to tap API.
|
|
22
|
+
|
|
23
|
+
* Added Node 14 to CI test coverage.
|
|
24
|
+
|
|
25
|
+
Many thanks to @jgeurts for the contribution.
|
|
26
|
+
|
|
27
|
+
### 6.13.2 (2020-10-13):
|
|
2
28
|
|
|
3
29
|
* Removed lodash as a development dependency
|
|
4
30
|
|
|
@@ -10,7 +36,7 @@
|
|
|
10
36
|
|
|
11
37
|
Thank you to @ronen-e for the contribution!
|
|
12
38
|
|
|
13
|
-
|
|
39
|
+
### 6.13.1 (2020-09-24):
|
|
14
40
|
|
|
15
41
|
* Fixed named-pipe check for lambda invocations to avoid race-condition.
|
|
16
42
|
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ In order to take full advantage of this package, make sure you have a [New Relic
|
|
|
10
10
|
|
|
11
11
|
As with any instrumentation tool, please test before using in production.
|
|
12
12
|
|
|
13
|
-
## Installation
|
|
13
|
+
## Installation
|
|
14
14
|
|
|
15
15
|
To use New Relic's Node.js agent entails these three steps, which are described in detail below:
|
|
16
16
|
|
|
@@ -57,9 +57,13 @@ If you're compiling your JavaScript and can't control the final `require` order,
|
|
|
57
57
|
$ node -r newrelic your-program.js
|
|
58
58
|
$ node --require newrelic your-program.js
|
|
59
59
|
|
|
60
|
+
## Getting Started
|
|
61
|
+
|
|
60
62
|
For more information on getting started, [check the Node.js docs](https://docs.newrelic.com/docs/agents/nodejs-agent/getting-started/introduction-new-relic-nodejs).
|
|
61
63
|
|
|
62
|
-
##
|
|
64
|
+
## Usage
|
|
65
|
+
|
|
66
|
+
### Using the API
|
|
63
67
|
|
|
64
68
|
The `newrelic` module returns an object with the Node agent's API methods attached.
|
|
65
69
|
|
|
@@ -72,7 +76,7 @@ The `newrelic` module returns an object with the Node agent's API methods attach
|
|
|
72
76
|
|
|
73
77
|
You can read more about using the API over on the [New Relic documentation](https://docs.newrelic.com/docs/agents/nodejs-agent/api-guides/guide-using-nodejs-agent-api) site.
|
|
74
78
|
|
|
75
|
-
##
|
|
79
|
+
## Testing
|
|
76
80
|
|
|
77
81
|
These are the steps to work on core agent features, with more detail below:
|
|
78
82
|
|
|
@@ -149,17 +153,22 @@ Please review [New Relic’s General Data Privacy Notice](https://newrelic.com/t
|
|
|
149
153
|
|
|
150
154
|
See our [roadmap](./ROADMAP_Node.md), to learn more about our product vision, understand our plans, and provide us valuable feedback.
|
|
151
155
|
|
|
152
|
-
##
|
|
156
|
+
## Contribute
|
|
153
157
|
|
|
154
158
|
We encourage your contributions to improve the Node.js agent! Keep in mind when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project.
|
|
159
|
+
|
|
155
160
|
If you have any questions, or to execute our corporate CLA, required if your contribution is on behalf of a company, please drop us an email at opensource@newrelic.com.
|
|
156
161
|
|
|
157
162
|
**A note about vulnerabilities**
|
|
158
163
|
|
|
159
|
-
As noted in our [security policy](
|
|
164
|
+
As noted in our [security policy](../../security/policy), New Relic is committed to the privacy and security of our customers and their data. We believe that providing coordinated disclosure by security researchers and engaging with the security community are important means to achieve our security goals.
|
|
160
165
|
|
|
161
166
|
If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through [HackerOne](https://hackerone.com/newrelic).
|
|
162
167
|
|
|
168
|
+
If you would like to contribute to this project, review [these guidelines](./CONTRIBUTING.md).
|
|
169
|
+
|
|
170
|
+
To [all contributors](https://github.com/newrelic/node-newrelic/graphs/contributors), we thank you! Without your contribution, this project would not be what it is today. We also host a community project page dedicated to [New Relic Node Agent](https://opensource.newrelic.com/projects/newrelic/node-newrelic).
|
|
171
|
+
|
|
163
172
|
## License
|
|
164
173
|
|
|
165
174
|
The Node.js agent is licensed under the [Apache 2.0](http://apache.org/licenses/LICENSE-2.0.txt) License.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -76,7 +76,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
|
|
|
76
76
|
|
|
77
77
|
### @grpc/grpc-js
|
|
78
78
|
|
|
79
|
-
This product includes source derived from [@grpc/grpc-js](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js) ([v1.
|
|
79
|
+
This product includes source derived from [@grpc/grpc-js](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js) ([v1.1.7](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.1.7)), distributed under the [Apache-2.0 License](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.1.7/LICENSE):
|
|
80
80
|
|
|
81
81
|
```
|
|
82
82
|
Apache License
|
|
@@ -285,7 +285,7 @@ This product includes source derived from [@grpc/grpc-js](https://github.com/grp
|
|
|
285
285
|
|
|
286
286
|
### @grpc/proto-loader
|
|
287
287
|
|
|
288
|
-
This product includes source derived from [@grpc/proto-loader](https://github.com/grpc/grpc-node) ([v0.5.
|
|
288
|
+
This product includes source derived from [@grpc/proto-loader](https://github.com/grpc/grpc-node) ([v0.5.5](https://github.com/grpc/grpc-node/tree/v0.5.5)), distributed under the [Apache-2.0 License](https://github.com/grpc/grpc-node/blob/v0.5.5/LICENSE):
|
|
289
289
|
|
|
290
290
|
```
|
|
291
291
|
Apache License
|
package/lib/agent.js
CHANGED
|
@@ -581,6 +581,10 @@ Agent.prototype._beforeMetricDataSend = function _beforeMetricDataSend() {
|
|
|
581
581
|
|
|
582
582
|
// Send uninstrumented supportability metrics every metric harvest cycle
|
|
583
583
|
uninstrumented.createMetrics(this.metrics)
|
|
584
|
+
|
|
585
|
+
if (this.spanEventAggregator.isStream) {
|
|
586
|
+
this.spanEventAggregator.createMetrics()
|
|
587
|
+
}
|
|
584
588
|
}
|
|
585
589
|
|
|
586
590
|
Agent.prototype._generateEntityStatsAndClear = function _generateHarvestMetrics() {
|
|
@@ -107,17 +107,23 @@ RemoteMethod.prototype._post = function _post(data, nrHeaders, callback) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
if (options.compressed) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
110
|
+
// NOTE: gzip and deflate throw immediately in Node 14+ with an invalid argument
|
|
111
|
+
try {
|
|
112
|
+
var useGzip = this._config.compressed_content_encoding === 'gzip'
|
|
113
|
+
var compressor = useGzip ? zlib.gzip : zlib.deflate
|
|
114
|
+
compressor(data, function onCompress(err, compressed) {
|
|
115
|
+
if (err) {
|
|
116
|
+
logger.warn(err, 'Error compressing JSON for delivery. Not sending.')
|
|
117
|
+
return callback(err)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
options.body = compressed
|
|
121
|
+
makeRequest()
|
|
122
|
+
})
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logger.warn(err, 'Error compressing JSON for delivery. Not sending.')
|
|
125
|
+
return callback(err)
|
|
126
|
+
}
|
|
121
127
|
} else {
|
|
122
128
|
options.body = data
|
|
123
129
|
makeRequest()
|
|
@@ -142,13 +142,18 @@ class ErrorCollector {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
const isErroredTransaction = urltils.isError(this.config, transaction.statusCode)
|
|
145
|
+
const isIgnoredErrorStatusCode = urltils.isIgnoredError(
|
|
146
|
+
this.config,
|
|
147
|
+
transaction.statusCode
|
|
148
|
+
)
|
|
149
|
+
|
|
145
150
|
const isExpectedErrorStatusCode = urltils.isExpectedError(
|
|
146
151
|
this.config,
|
|
147
152
|
transaction.statusCode
|
|
148
153
|
)
|
|
149
154
|
|
|
150
155
|
// collect other exceptions only if status code is not ignored
|
|
151
|
-
if (transaction.exceptions.length) {
|
|
156
|
+
if (transaction.exceptions.length && !isIgnoredErrorStatusCode) {
|
|
152
157
|
for (let i = 0; i < transaction.exceptions.length; i++) {
|
|
153
158
|
const exception = transaction.exceptions[i]
|
|
154
159
|
if (this.collect(transaction, exception)) {
|
package/lib/errors/index.js
CHANGED
|
@@ -20,7 +20,20 @@ class Exception {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
getErrorDetails(config) {
|
|
23
|
-
|
|
23
|
+
const errorDetails = errorHelper.extractErrorInformation(null, this.error, config)
|
|
24
|
+
errorDetails.expected = this.isExpected(config, errorDetails)
|
|
25
|
+
|
|
26
|
+
return errorDetails
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
isExpected(config, {type, message}) {
|
|
30
|
+
if (!this._expected) {
|
|
31
|
+
this._expected =
|
|
32
|
+
errorHelper.isExpectedErrorClass(config, type) ||
|
|
33
|
+
errorHelper.isExpectedErrorMessage(config, type, message)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this._expected
|
|
24
37
|
}
|
|
25
38
|
}
|
|
26
39
|
|
package/lib/grpc/connection.js
CHANGED
|
@@ -121,7 +121,7 @@ class GrpcConnection extends EventEmitter {
|
|
|
121
121
|
// immediately.
|
|
122
122
|
this._setState(connectionStates.connected, this.stream)
|
|
123
123
|
} catch (err) {
|
|
124
|
-
logger.
|
|
124
|
+
logger.warn(
|
|
125
125
|
err,
|
|
126
126
|
'Unexpected error establishing gRPC stream, will not attempt reconnect.'
|
|
127
127
|
)
|
|
@@ -198,9 +198,26 @@ class GrpcConnection extends EventEmitter {
|
|
|
198
198
|
if (this.stream) {
|
|
199
199
|
this.stream.removeAllListeners()
|
|
200
200
|
|
|
201
|
+
const oldStream = this.stream
|
|
202
|
+
this.stream.on('status', function endStreamStatusHandler(grpcStatus) {
|
|
203
|
+
logger.trace('End stream status received [%s]: %s', grpcStatus.code, grpcStatus.details)
|
|
204
|
+
|
|
205
|
+
// Cleanup the final end stream listeners.
|
|
206
|
+
oldStream.removeAllListeners()
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// Listen to any final errors to prevent throwing.
|
|
210
|
+
// This is unlikely but if the server closes post
|
|
211
|
+
// removing listeners and prior to response it could
|
|
212
|
+
// happen. We noticed this via tests on Node 14.
|
|
213
|
+
this.stream.on('error', function endStreamErrorHandler(err) {
|
|
214
|
+
logger.trace('End stream error received. Code: [%s]: %s', err.code, err.details)
|
|
215
|
+
})
|
|
216
|
+
|
|
201
217
|
// Indicates to server we are done.
|
|
202
218
|
// Server officially closes the stream.
|
|
203
219
|
this.stream.end()
|
|
220
|
+
|
|
204
221
|
this.stream = null
|
|
205
222
|
}
|
|
206
223
|
|
|
@@ -238,6 +255,9 @@ class GrpcConnection extends EventEmitter {
|
|
|
238
255
|
// per the spec, An UNIMPLEMENTED status code from gRPC indicates
|
|
239
256
|
// that the versioned Trace Observer is no longer available. Agents
|
|
240
257
|
// MUST NOT attempt to reconnect in this case
|
|
258
|
+
logger.info(
|
|
259
|
+
'[UNIMPLEMENTED]: Trace Obserserver is no longer available. Shutting down connection.'
|
|
260
|
+
)
|
|
241
261
|
this._disconnect()
|
|
242
262
|
} else if (grpc.status[grpc.status.OK] === grpcStatusName) {
|
|
243
263
|
this._reconnect()
|
|
@@ -256,7 +276,11 @@ class GrpcConnection extends EventEmitter {
|
|
|
256
276
|
this._metrics.getOrCreateMetric(NAMES.INFINITE_TRACING.SPAN_RESPONSE_ERROR)
|
|
257
277
|
.incrementCallCount()
|
|
258
278
|
|
|
259
|
-
|
|
279
|
+
// For errors, the status will either result in a disconnect or a reconnect
|
|
280
|
+
// delay that should prevent too frequent spamming. Unless the app is idle
|
|
281
|
+
// and regularly getting Status 13 reconnects from the server, in which case
|
|
282
|
+
// this will be almost the only logging.
|
|
283
|
+
logger.warn('Span stream error. Code: [%s]: %s', err.code, err.details)
|
|
260
284
|
})
|
|
261
285
|
}
|
|
262
286
|
|
package/lib/metrics/names.js
CHANGED
|
@@ -240,10 +240,13 @@ const SPAN_EVENTS = {
|
|
|
240
240
|
const INFINITE_TRACING = {
|
|
241
241
|
SEEN: SUPPORTABILITY.INFINITE_TRACING + '/Span/Seen',
|
|
242
242
|
SENT: SUPPORTABILITY.INFINITE_TRACING + '/Span/Sent',
|
|
243
|
+
DROPPED: SUPPORTABILITY.INFINITE_TRACING + '/Span/Dropped',
|
|
243
244
|
SPAN_RESPONSE_ERROR: SUPPORTABILITY.INFINITE_TRACING + '/Span/Response/Error',
|
|
244
245
|
SPAN_RESPONSE_GRPC_UNIMPLEMENTED: SUPPORTABILITY.INFINITE_TRACING + '/Span/gRPC/UNIMPLEMENTED',
|
|
245
246
|
SPAN_RESPONSE_GRPC_STATUS: SUPPORTABILITY.INFINITE_TRACING + '/Span/gRPC/%s',
|
|
246
|
-
|
|
247
|
+
QUEUE_CAPACITY: SUPPORTABILITY.INFINITE_TRACING + '/Span/QueueCapacity',
|
|
248
|
+
QUEUE_SIZE: SUPPORTABILITY.INFINITE_TRACING + '/Span/QueueSize',
|
|
249
|
+
DRAIN_DURATION: SUPPORTABILITY.INFINITE_TRACING + '/Drain/Duration',
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
module.exports = {
|
|
@@ -21,6 +21,9 @@ class SpanContext {
|
|
|
21
21
|
customAttributes || new PrioritizedAttributes(ATTRIBUTE_SCOPE, MAXIMUM_CUSTOM_ATTRIBUTES)
|
|
22
22
|
|
|
23
23
|
this.ATTRIBUTE_PRIORITY = ATTRIBUTE_PRIORITY
|
|
24
|
+
|
|
25
|
+
this.hasError = false
|
|
26
|
+
this.errorDetails = null
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
addIntrinsicAttribute(key, value) {
|
|
@@ -36,6 +39,21 @@ class SpanContext {
|
|
|
36
39
|
priority
|
|
37
40
|
)
|
|
38
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Set error details to be potentially be used to create span
|
|
45
|
+
* attributes. Attributes will be created unless the transaction
|
|
46
|
+
* ends with an ignored error status code.
|
|
47
|
+
*
|
|
48
|
+
* Last error wins.
|
|
49
|
+
*/
|
|
50
|
+
setError(details) {
|
|
51
|
+
this.hasError = true
|
|
52
|
+
|
|
53
|
+
// Error details will be used to create attributes unless the transaction ends
|
|
54
|
+
// with an ignored status code.
|
|
55
|
+
this.errorDetails = details
|
|
56
|
+
}
|
|
39
57
|
}
|
|
40
58
|
|
|
41
59
|
module.exports = SpanContext
|
package/lib/spans/span-event.js
CHANGED
|
@@ -84,9 +84,21 @@ class SpanEvent {
|
|
|
84
84
|
* @return {SpanEvent} The constructed event.
|
|
85
85
|
*/
|
|
86
86
|
static fromSegment(segment, parentId = null, isRoot = false) {
|
|
87
|
+
const spanContext = segment.getSpanContext()
|
|
88
|
+
|
|
89
|
+
// Since segments already hold span agent attributes and we want to leverage
|
|
90
|
+
// filtering, we add to the segment attributes prior to processing.
|
|
91
|
+
if (spanContext.hasError && !segment.transaction.hasIgnoredErrorStatusCode()) {
|
|
92
|
+
const details = spanContext.errorDetails
|
|
93
|
+
segment.addSpanAttribute('error.message', details.message)
|
|
94
|
+
segment.addSpanAttribute('error.class', details.type)
|
|
95
|
+
if (details.expected) {
|
|
96
|
+
segment.addSpanAttribute('error.expected', details.expected)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
const attributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
|
|
88
101
|
|
|
89
|
-
const spanContext = segment.getSpanContext()
|
|
90
102
|
const customAttributes = spanContext.customAttributes.get(DESTINATIONS.SPAN_EVENT)
|
|
91
103
|
|
|
92
104
|
let span = null
|
|
@@ -98,7 +110,6 @@ class SpanEvent {
|
|
|
98
110
|
span = new SpanEvent(attributes, customAttributes)
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
|
|
102
113
|
for (const [key, value] of Object.entries(spanContext.intrinsicAttributes)) {
|
|
103
114
|
span.intrinsics[key] = value
|
|
104
115
|
}
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
const logger = require('../logger').child({component: 'span-streamer'})
|
|
9
9
|
const NAMES = require('../metrics/names').INFINITE_TRACING
|
|
10
10
|
|
|
11
|
+
const SPAN_DROP_MSG_INTERVAL_MS = 30000
|
|
11
12
|
const SPAN_DROP_MSG =
|
|
12
|
-
'Queue full, dropping spans.'
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
'Queue full, dropping spans. ' +
|
|
14
|
+
`Will not warn again for ${SPAN_DROP_MSG_INTERVAL_MS / 1000} seconds.`
|
|
15
15
|
|
|
16
16
|
class SpanStreamer {
|
|
17
17
|
constructor(license_key, connection, metrics, queue_size) {
|
|
@@ -47,21 +47,20 @@ class SpanStreamer {
|
|
|
47
47
|
|
|
48
48
|
// If not writeable (because of backpressure) queue the span
|
|
49
49
|
if (!this._writable) {
|
|
50
|
-
if (this.spans.length
|
|
50
|
+
if (this.spans.length < this.queue_size) {
|
|
51
51
|
this.spans.push(span)
|
|
52
52
|
return
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// While this can be directionally calculated between seen/sent the
|
|
56
|
+
// queue makes that a bit more disconnected. This will be a bit more specific.
|
|
57
|
+
this._metrics.getOrCreateMetric(NAMES.DROPPED).incrementCallCount()
|
|
58
|
+
|
|
55
59
|
// If the queue is full drop the span
|
|
56
60
|
logger.infoOncePer(
|
|
57
|
-
// key for the OncePer
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
SPAN_DROP_INTERVAL * 1000,
|
|
61
|
-
// message
|
|
62
|
-
SPAN_DROP_MSG + ' Will not warn again for %s seconds.',
|
|
63
|
-
// variables to put in log message
|
|
64
|
-
SPAN_DROP_INTERVAL
|
|
61
|
+
'SPAN_DROP_MSG', // key for the OncePer
|
|
62
|
+
SPAN_DROP_MSG_INTERVAL_MS,
|
|
63
|
+
SPAN_DROP_MSG
|
|
65
64
|
)
|
|
66
65
|
|
|
67
66
|
return
|
|
@@ -121,6 +120,8 @@ class SpanStreamer {
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
sendQueue() {
|
|
123
|
+
logger.trace('Sending spans from queue.')
|
|
124
|
+
|
|
124
125
|
// Continue sending the spans that were in the queue. _writable is checked
|
|
125
126
|
// so that if a send fails while clearing the queue, this drain handler can
|
|
126
127
|
// finish, and the drain handler setup on the failed send will then attempt
|
|
@@ -129,6 +130,11 @@ class SpanStreamer {
|
|
|
129
130
|
const nextObject = this.spans.shift()
|
|
130
131
|
this.send(nextObject)
|
|
131
132
|
}
|
|
133
|
+
|
|
134
|
+
logger.trace(
|
|
135
|
+
'Finished sending spans from queue. Items left in queue: %s',
|
|
136
|
+
this.spans.length
|
|
137
|
+
)
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
connect(agent_run_id, requestHeadersMap) {
|
|
@@ -144,6 +150,11 @@ class SpanStreamer {
|
|
|
144
150
|
disconnect() {
|
|
145
151
|
this.connection.disconnect()
|
|
146
152
|
}
|
|
153
|
+
|
|
154
|
+
createMetrics() {
|
|
155
|
+
this._metrics.getOrCreateMetric(NAMES.QUEUE_CAPACITY).recordValue(this.queue_size)
|
|
156
|
+
this._metrics.getOrCreateMetric(NAMES.QUEUE_SIZE).recordValue(this.spans.length)
|
|
157
|
+
}
|
|
147
158
|
}
|
|
148
159
|
|
|
149
160
|
module.exports = SpanStreamer
|
|
@@ -102,9 +102,21 @@ class StreamingSpanEvent {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
static fromSegment(segment, parentId = null, isRoot = false) {
|
|
105
|
+
const spanContext = segment.getSpanContext()
|
|
106
|
+
|
|
107
|
+
// Since segments already hold span agent attributes and we want to leverage
|
|
108
|
+
// filtering, we add to the segment attributes prior to processing.
|
|
109
|
+
if (spanContext.hasError && !segment.transaction.hasIgnoredErrorStatusCode()) {
|
|
110
|
+
const details = spanContext.errorDetails
|
|
111
|
+
segment.addSpanAttribute('error.message', details.message)
|
|
112
|
+
segment.addSpanAttribute('error.class', details.type)
|
|
113
|
+
if (details.expected) {
|
|
114
|
+
segment.addSpanAttribute('error.expected', details.expected)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
105
118
|
const agentAttributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
|
|
106
119
|
|
|
107
|
-
const spanContext = segment.getSpanContext()
|
|
108
120
|
const customAttributes = spanContext.customAttributes.get(DESTINATIONS.SPAN_EVENT)
|
|
109
121
|
|
|
110
122
|
const transaction = segment.transaction
|
package/lib/transaction/index.js
CHANGED
|
@@ -679,7 +679,6 @@ Transaction.prototype.measure = function measure(name, scope, duration, exclusiv
|
|
|
679
679
|
Transaction.prototype._setApdex = function _setApdex(name, duration, keyApdexInMillis) {
|
|
680
680
|
var apdexStats = this.metrics.getOrCreateApdexMetric(name, null, keyApdexInMillis)
|
|
681
681
|
|
|
682
|
-
|
|
683
682
|
// if we have an error-like status code, and all the errors are
|
|
684
683
|
// expected, we know the status code was caused by an expected
|
|
685
684
|
// error, so we will not report "frustrating". Otherwise, we
|
|
@@ -747,19 +746,13 @@ function _linkExceptionToSegment(exception) {
|
|
|
747
746
|
return
|
|
748
747
|
}
|
|
749
748
|
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
const isExpected =
|
|
758
|
-
errorHelper.isExpectedErrorClass(config, details.type) ||
|
|
759
|
-
errorHelper.isExpectedErrorMessage(config, details.type, details.message)
|
|
760
|
-
|
|
761
|
-
if (isExpected) {
|
|
762
|
-
segment.addSpanAttribute('error.expected', isExpected)
|
|
749
|
+
const spanContext = segment.getSpanContext()
|
|
750
|
+
if (spanContext) {
|
|
751
|
+
// Exception attributes will be added to span unless transaction
|
|
752
|
+
// status code has been ignored. Last error wins.
|
|
753
|
+
const config = this.agent.config
|
|
754
|
+
const details = exception.getErrorDetails(config)
|
|
755
|
+
spanContext.setError(details)
|
|
763
756
|
}
|
|
764
757
|
|
|
765
758
|
// Add the span/segment ID to the exception as agent attributes
|
|
@@ -793,6 +786,14 @@ function _addUserError(exception) {
|
|
|
793
786
|
this.userErrors.push(exception)
|
|
794
787
|
}
|
|
795
788
|
|
|
789
|
+
/**
|
|
790
|
+
* Returns if the transaction's current status code is errored
|
|
791
|
+
* but considered ignored via the config.
|
|
792
|
+
*/
|
|
793
|
+
Transaction.prototype.hasIgnoredErrorStatusCode = function _hasIgnoredErrorStatusCode() {
|
|
794
|
+
return urltils.isIgnoredError(this.agent.config, this.statusCode)
|
|
795
|
+
}
|
|
796
|
+
|
|
796
797
|
/**
|
|
797
798
|
* Returns true if an error happened during the transaction or if the transaction itself is
|
|
798
799
|
* considered to be an error.
|
package/newrelic.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.14.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -142,8 +142,8 @@
|
|
|
142
142
|
"newrelic-naming-rules": "./bin/test-naming-rules.js"
|
|
143
143
|
},
|
|
144
144
|
"dependencies": {
|
|
145
|
-
"@grpc/grpc-js": "1.
|
|
146
|
-
"@grpc/proto-loader": "^0.5.
|
|
145
|
+
"@grpc/grpc-js": "1.1.7",
|
|
146
|
+
"@grpc/proto-loader": "^0.5.5",
|
|
147
147
|
"@newrelic/aws-sdk": "^2.0.0",
|
|
148
148
|
"@newrelic/koa": "^4.0.0",
|
|
149
149
|
"@newrelic/superagent": "^3.0.0",
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
{
|
|
2
|
-
"lastUpdated": "
|
|
2
|
+
"lastUpdated": "Wed Oct 28 2020 11:06:05 GMT-0700 (Pacific Daylight Time)",
|
|
3
3
|
"projectName": "New Relic Node Agent",
|
|
4
4
|
"projectUrl": "https://github.com/newrelic/node-newrelic",
|
|
5
5
|
"includeDev": true,
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@grpc/grpc-js@1.
|
|
7
|
+
"@grpc/grpc-js@1.1.7": {
|
|
8
8
|
"name": "@grpc/grpc-js",
|
|
9
|
-
"version": "1.
|
|
10
|
-
"range": "1.
|
|
9
|
+
"version": "1.1.7",
|
|
10
|
+
"range": "1.1.7",
|
|
11
11
|
"licenses": "Apache-2.0",
|
|
12
12
|
"repoUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
|
13
|
-
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.
|
|
13
|
+
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/tree/v1.1.7",
|
|
14
14
|
"licenseFile": "node_modules/@grpc/grpc-js/LICENSE",
|
|
15
|
-
"licenseUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.
|
|
15
|
+
"licenseUrl": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js/blob/v1.1.7/LICENSE",
|
|
16
16
|
"licenseTextSource": "file",
|
|
17
17
|
"publisher": "Google Inc."
|
|
18
18
|
},
|
|
19
|
-
"@grpc/proto-loader@0.5.
|
|
19
|
+
"@grpc/proto-loader@0.5.5": {
|
|
20
20
|
"name": "@grpc/proto-loader",
|
|
21
|
-
"version": "0.5.
|
|
22
|
-
"range": "^0.5.
|
|
21
|
+
"version": "0.5.5",
|
|
22
|
+
"range": "^0.5.5",
|
|
23
23
|
"licenses": "Apache-2.0",
|
|
24
24
|
"repoUrl": "https://github.com/grpc/grpc-node",
|
|
25
|
-
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/v0.5.
|
|
25
|
+
"versionedRepoUrl": "https://github.com/grpc/grpc-node/tree/v0.5.5",
|
|
26
26
|
"licenseFile": "node_modules/@grpc/proto-loader/LICENSE",
|
|
27
|
-
"licenseUrl": "https://github.com/grpc/grpc-node/blob/v0.5.
|
|
27
|
+
"licenseUrl": "https://github.com/grpc/grpc-node/blob/v0.5.5/LICENSE",
|
|
28
28
|
"licenseTextSource": "file",
|
|
29
29
|
"publisher": "Google Inc."
|
|
30
30
|
},
|