newrelic 8.2.0 → 8.3.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 +4989 -4963
- package/THIRD_PARTY_NOTICES.md +0 -33
- package/lib/collector/facts.js +1 -2
- package/lib/config/default.js +20 -5
- package/lib/config/env.js +14 -4
- package/lib/config/index.js +8 -3
- package/lib/instrumentation/fastify.js +30 -8
- package/lib/instrumentation/mongodb/common.js +63 -24
- package/lib/instrumentation/mongodb/v3-mongo.js +20 -1
- package/lib/instrumentation/mongodb/v4-mongo.js +6 -7
- package/lib/metrics/names.js +6 -3
- package/lib/spans/span-event-aggregator.js +62 -10
- package/package.json +1 -2
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -46,7 +46,6 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
|
|
|
46
46
|
* [eslint-plugin-prettier](#eslint-plugin-prettier)
|
|
47
47
|
* [eslint](#eslint)
|
|
48
48
|
* [express](#express)
|
|
49
|
-
* [fastify](#fastify)
|
|
50
49
|
* [generic-pool](#generic-pool)
|
|
51
50
|
* [glob](#glob)
|
|
52
51
|
* [got](#got)
|
|
@@ -2348,38 +2347,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
2348
2347
|
|
|
2349
2348
|
```
|
|
2350
2349
|
|
|
2351
|
-
### fastify
|
|
2352
|
-
|
|
2353
|
-
This product includes source derived from [fastify](https://github.com/fastify/fastify) ([v2.15.3](https://github.com/fastify/fastify/tree/v2.15.3)), distributed under the [MIT License](https://github.com/fastify/fastify/blob/v2.15.3/LICENSE):
|
|
2354
|
-
|
|
2355
|
-
```
|
|
2356
|
-
MIT License
|
|
2357
|
-
|
|
2358
|
-
Copyright (c) 2016-2020 The Fastify Team
|
|
2359
|
-
|
|
2360
|
-
The Fastify team members are listed at https://github.com/fastify/fastify#team
|
|
2361
|
-
and in the README file.
|
|
2362
|
-
|
|
2363
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2364
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
2365
|
-
in the Software without restriction, including without limitation the rights
|
|
2366
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
2367
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
2368
|
-
furnished to do so, subject to the following conditions:
|
|
2369
|
-
|
|
2370
|
-
The above copyright notice and this permission notice shall be included in all
|
|
2371
|
-
copies or substantial portions of the Software.
|
|
2372
|
-
|
|
2373
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
2374
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
2375
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
2376
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
2377
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
2378
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
2379
|
-
SOFTWARE.
|
|
2380
|
-
|
|
2381
|
-
```
|
|
2382
|
-
|
|
2383
2350
|
### generic-pool
|
|
2384
2351
|
|
|
2385
2352
|
This product includes source derived from [generic-pool](https://github.com/coopernurse/node-pool) ([v3.8.2](https://github.com/coopernurse/node-pool/tree/v3.8.2)), distributed under the [MIT License](https://github.com/coopernurse/node-pool/blob/v3.8.2/README.md):
|
package/lib/collector/facts.js
CHANGED
|
@@ -10,7 +10,6 @@ const fetchSystemInfo = require('../system-info')
|
|
|
10
10
|
const logger = require('../logger').child({ component: 'facts' })
|
|
11
11
|
const os = require('os')
|
|
12
12
|
const parseLabels = require('../util/label-parser')
|
|
13
|
-
const Config = require('../config/')
|
|
14
13
|
|
|
15
14
|
module.exports = facts
|
|
16
15
|
|
|
@@ -64,7 +63,7 @@ function facts(agent, callback) {
|
|
|
64
63
|
analytic_event_data: agent.config.transaction_events.max_samples_stored,
|
|
65
64
|
custom_event_data: agent.config.custom_insights_events.max_samples_stored,
|
|
66
65
|
error_event_data: agent.config.error_collector.max_event_samples_stored,
|
|
67
|
-
span_event_data:
|
|
66
|
+
span_event_data: agent.config.span_events.max_samples_stored
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
package/lib/config/default.js
CHANGED
|
@@ -292,7 +292,7 @@ exports.config = () => ({
|
|
|
292
292
|
*/
|
|
293
293
|
capture_events: true,
|
|
294
294
|
/**
|
|
295
|
-
* The agent will collect all error events up to this number
|
|
295
|
+
* The agent will collect all error events up to this number every 5 seconds.
|
|
296
296
|
* If there are more than that, a statistical sampling will be collected.
|
|
297
297
|
* Currently this uses a priority sampling algorithm.
|
|
298
298
|
*
|
|
@@ -301,6 +301,9 @@ exports.config = () => ({
|
|
|
301
301
|
* Relic servers. The memory concerns are something you should consider for
|
|
302
302
|
* your own server's sake. The payload of events is compressed, but if it
|
|
303
303
|
* grows too large the New Relic servers may reject it.
|
|
304
|
+
*
|
|
305
|
+
* @env NEW_RELIC_ERROR_COLLECTOR_MAX_EVENT_SAMPLES_STORED
|
|
306
|
+
*
|
|
304
307
|
*/
|
|
305
308
|
max_event_samples_stored: 100,
|
|
306
309
|
|
|
@@ -648,8 +651,10 @@ exports.config = () => ({
|
|
|
648
651
|
*/
|
|
649
652
|
enabled: true,
|
|
650
653
|
/**
|
|
651
|
-
* The agent will collect all events up to this number
|
|
654
|
+
* The agent will collect all events up to this number every 5 seconds. If
|
|
652
655
|
* there are more than that, a statistical sampling will be collected.
|
|
656
|
+
*
|
|
657
|
+
* @env NEW_RELIC_TRANSACTION_EVENTS_MAX_SAMPLES_STORED
|
|
653
658
|
*/
|
|
654
659
|
max_samples_stored: 10000
|
|
655
660
|
},
|
|
@@ -671,7 +676,7 @@ exports.config = () => ({
|
|
|
671
676
|
*/
|
|
672
677
|
enabled: true,
|
|
673
678
|
/**
|
|
674
|
-
* The agent will collect all events up to this number
|
|
679
|
+
* The agent will collect all events up to this number every 5 seconds. If there
|
|
675
680
|
* are more than that, a statistical sampling will be collected. Currently
|
|
676
681
|
* this uses a priority sampling algorithm.
|
|
677
682
|
*
|
|
@@ -680,6 +685,9 @@ exports.config = () => ({
|
|
|
680
685
|
* Relic servers. The memory concerns are something you should consider for
|
|
681
686
|
* your own server's sake. The payload of events is compressed, but if it
|
|
682
687
|
* grows too large the New Relic servers may reject it.
|
|
688
|
+
*
|
|
689
|
+
* @env NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED
|
|
690
|
+
*
|
|
683
691
|
*/
|
|
684
692
|
max_samples_stored: 1000
|
|
685
693
|
},
|
|
@@ -795,7 +803,14 @@ exports.config = () => ({
|
|
|
795
803
|
* @env NEW_RELIC_SPAN_EVENTS_ATTRIBUTES_INCLUDE
|
|
796
804
|
*/
|
|
797
805
|
include: []
|
|
798
|
-
}
|
|
806
|
+
},
|
|
807
|
+
/**
|
|
808
|
+
* The agent will collect all events up to this number per minute. If
|
|
809
|
+
* there are more than that, a statistical sampling will be collected.
|
|
810
|
+
*
|
|
811
|
+
* @env NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED
|
|
812
|
+
*/
|
|
813
|
+
max_samples_stored: 2000
|
|
799
814
|
},
|
|
800
815
|
|
|
801
816
|
/**
|
|
@@ -840,7 +855,7 @@ exports.config = () => ({
|
|
|
840
855
|
*
|
|
841
856
|
* @env NEW_RELIC_DISTRIBUTED_TRACING_ENABLED
|
|
842
857
|
*/
|
|
843
|
-
enabled:
|
|
858
|
+
enabled: true,
|
|
844
859
|
|
|
845
860
|
/**
|
|
846
861
|
* Excludes New Relic format distributed tracing header (`newrelic`) on
|
package/lib/config/env.js
CHANGED
|
@@ -44,7 +44,8 @@ const ENV_MAPPING = {
|
|
|
44
44
|
ignore_status_codes: 'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERROR_CODES',
|
|
45
45
|
ignore_classes: 'NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERRORS',
|
|
46
46
|
expected_status_codes: 'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERROR_CODES',
|
|
47
|
-
expected_classes: 'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERRORS'
|
|
47
|
+
expected_classes: 'NEW_RELIC_ERROR_COLLECTOR_EXPECTED_ERRORS',
|
|
48
|
+
max_event_samples_stored: 'NEW_RELIC_ERROR_COLLECTOR_MAX_EVENT_SAMPLES_STORED'
|
|
48
49
|
},
|
|
49
50
|
strip_exception_messages: {
|
|
50
51
|
enabled: 'NEW_RELIC_STRIP_EXCEPTION_MESSAGES_ENABLED'
|
|
@@ -54,7 +55,11 @@ const ENV_MAPPING = {
|
|
|
54
55
|
enabled: 'NEW_RELIC_TRANSACTION_EVENTS_ATTRIBUTES_ENABLED',
|
|
55
56
|
exclude: 'NEW_RELIC_TRANSACTION_EVENTS_ATTRIBUTES_EXCLUDE',
|
|
56
57
|
include: 'NEW_RELIC_TRANSACTION_EVENTS_ATTRIBUTES_INCLUDE'
|
|
57
|
-
}
|
|
58
|
+
},
|
|
59
|
+
max_samples_stored: 'NEW_RELIC_TRANSACTION_EVENTS_MAX_SAMPLES_STORED'
|
|
60
|
+
},
|
|
61
|
+
custom_insights_events: {
|
|
62
|
+
max_samples_stored: 'NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED'
|
|
58
63
|
},
|
|
59
64
|
transaction_tracer: {
|
|
60
65
|
attributes: {
|
|
@@ -123,7 +128,8 @@ const ENV_MAPPING = {
|
|
|
123
128
|
enabled: 'NEW_RELIC_SPAN_EVENTS_ATTRIBUTES_ENABLED',
|
|
124
129
|
exclude: 'NEW_RELIC_SPAN_EVENTS_ATTRIBUTES_EXCLUDE',
|
|
125
130
|
include: 'NEW_RELIC_SPAN_EVENTS_ATTRIBUTES_INCLUDE'
|
|
126
|
-
}
|
|
131
|
+
},
|
|
132
|
+
max_samples_stored: 'NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED'
|
|
127
133
|
},
|
|
128
134
|
transaction_segments: {
|
|
129
135
|
attributes: {
|
|
@@ -242,7 +248,11 @@ const FLOAT_VARS = new Set(['NEW_RELIC_APDEX_T', 'NEW_RELIC_TRACER_THRESHOLD'])
|
|
|
242
248
|
const INT_VARS = new Set([
|
|
243
249
|
'NEW_RELIC_EXPLAIN_THRESHOLD',
|
|
244
250
|
'NEW_RELIC_MAX_SQL_SAMPLES',
|
|
245
|
-
'NEW_RELIC_INFINITE_TRACING_SPAN_EVENTS_QUEUE_SIZE'
|
|
251
|
+
'NEW_RELIC_INFINITE_TRACING_SPAN_EVENTS_QUEUE_SIZE',
|
|
252
|
+
'NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED',
|
|
253
|
+
'NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED',
|
|
254
|
+
'NEW_RELIC_TRANSACTION_EVENTS_MAX_SAMPLES_STORED',
|
|
255
|
+
'NEW_RELIC_ERROR_COLLECTOR_MAX_EVENT_SAMPLES_STORED'
|
|
246
256
|
])
|
|
247
257
|
|
|
248
258
|
exports.ENV_MAPPING = ENV_MAPPING
|
package/lib/config/index.js
CHANGED
|
@@ -29,7 +29,6 @@ const mergeServerConfig = new MergeServerConfig()
|
|
|
29
29
|
const AZURE_APP_NAME = 'APP_POOL_ID'
|
|
30
30
|
const DEFAULT_MAX_PAYLOAD_SIZE_IN_BYTES = 1000000
|
|
31
31
|
const DEFAULT_CONFIG_PATH = require.resolve('./default')
|
|
32
|
-
const SPAN_EVENT_LIMIT = 1000
|
|
33
32
|
const BASE_CONFIG_PATH = require.resolve('../../newrelic')
|
|
34
33
|
const HAS_ARBITRARY_KEYS = new Set(['ignore_messages', 'expected_messages', 'labels'])
|
|
35
34
|
|
|
@@ -183,7 +182,7 @@ function Config(config) {
|
|
|
183
182
|
analytic_event_data: this.transaction_events.max_samples_stored,
|
|
184
183
|
custom_event_data: this.custom_insights_events.max_samples_stored,
|
|
185
184
|
error_event_data: this.error_collector.max_event_samples_stored,
|
|
186
|
-
span_event_data:
|
|
185
|
+
span_event_data: this.span_events.max_samples_stored
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
188
|
}
|
|
@@ -544,6 +543,13 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
544
543
|
this.logUnsupported(params, key)
|
|
545
544
|
break
|
|
546
545
|
|
|
546
|
+
// DT span event harvest config limits
|
|
547
|
+
case 'span_event_harvest_config':
|
|
548
|
+
this.span_event_harvest_config = {
|
|
549
|
+
...params[key]
|
|
550
|
+
}
|
|
551
|
+
break
|
|
552
|
+
|
|
547
553
|
// These settings are not allowed from the server.
|
|
548
554
|
case 'attributes.enabled':
|
|
549
555
|
case 'attributes.exclude':
|
|
@@ -1711,6 +1717,5 @@ Config.initialize = initialize
|
|
|
1711
1717
|
Config.getOrCreateInstance = getOrCreateInstance
|
|
1712
1718
|
Config.getInstance = getInstance
|
|
1713
1719
|
Config.createInstance = createInstance
|
|
1714
|
-
Config.SPAN_EVENT_LIMIT = SPAN_EVENT_LIMIT
|
|
1715
1720
|
|
|
1716
1721
|
module.exports = Config
|
|
@@ -43,12 +43,6 @@ const setupRouteHandler = (shim, fastify) => {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const setupMiddlewareHandlers = (shim, fastify) => {
|
|
46
|
-
// Don't wrap use() in fastify v3+
|
|
47
|
-
const fastifyVersion = shim.require('./package.json').version
|
|
48
|
-
if (semver.satisfies(fastifyVersion, '>=3.0.0')) {
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
46
|
shim.wrap(fastify, 'use', function wrapFastifyUse(shim, fn) {
|
|
53
47
|
return function wrappedFastifyUser() {
|
|
54
48
|
const args = shim.argsToArray.apply(shim, arguments)
|
|
@@ -70,10 +64,14 @@ module.exports = function initialize(agent, fastify, moduleName, shim) {
|
|
|
70
64
|
return
|
|
71
65
|
}
|
|
72
66
|
shim.setFramework(shim.FASTIFY)
|
|
67
|
+
|
|
68
|
+
const fastifyVersion = shim.require('./package.json').version
|
|
69
|
+
const isv3Plus = semver.satisfies(fastifyVersion, '>=3.0.0')
|
|
70
|
+
|
|
73
71
|
/**
|
|
74
72
|
* Fastify exports a function, so we need to use wrapExport
|
|
75
73
|
*/
|
|
76
|
-
shim.wrapExport(fastify, function wrapFastifyModule(shim, fn) {
|
|
74
|
+
const wrappedExport = shim.wrapExport(fastify, function wrapFastifyModule(shim, fn) {
|
|
77
75
|
return function wrappedFastifyModule() {
|
|
78
76
|
// normalize arguments
|
|
79
77
|
const args = shim.argsToArray.apply(shim, arguments)
|
|
@@ -83,9 +81,33 @@ module.exports = function initialize(agent, fastify, moduleName, shim) {
|
|
|
83
81
|
|
|
84
82
|
setupRouteHandler(shim, fastifyForWrapping)
|
|
85
83
|
|
|
86
|
-
|
|
84
|
+
// Don't wrap use() in fastify v3+
|
|
85
|
+
if (!isv3Plus) {
|
|
86
|
+
setupMiddlewareHandlers(shim, fastifyForWrapping)
|
|
87
|
+
}
|
|
87
88
|
|
|
88
89
|
return fastifyForWrapping
|
|
89
90
|
}
|
|
90
91
|
})
|
|
92
|
+
|
|
93
|
+
if (isv3Plus) {
|
|
94
|
+
setupExports(fastify, wrappedExport)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* module.exports = fastify
|
|
100
|
+
* module.exports.fastify = fastify
|
|
101
|
+
* module.exports.default = fastify
|
|
102
|
+
*/
|
|
103
|
+
function setupExports(original, wrappedExport) {
|
|
104
|
+
wrappedExport.fastify = original.fastify
|
|
105
|
+
|
|
106
|
+
if (original.fastify) {
|
|
107
|
+
wrappedExport.fastify = wrappedExport
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (original.default) {
|
|
111
|
+
wrappedExport.default = wrappedExport
|
|
112
|
+
}
|
|
91
113
|
}
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
const { CURSOR_OPS, COLLECTION_OPS, DB_OPS } = require('./constants')
|
|
8
|
-
const { URL } = require('url')
|
|
9
|
-
|
|
10
8
|
const common = module.exports
|
|
9
|
+
common.NR_ATTRS = Symbol('NR_ATTRS')
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Instruments all methods from constants.CURSOR_OPS on a given
|
|
@@ -24,7 +23,7 @@ common.instrumentCursor = function instrumentCursor(shim, Cursor) {
|
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
shim.recordQuery(proto, 'each', common.makeQueryDescFunc(shim, 'each'))
|
|
27
|
-
shim.recordOperation(proto, 'pipe')
|
|
26
|
+
shim.recordOperation(proto, 'pipe', { opaque: true })
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -97,18 +96,16 @@ common.captureAttributesOnStarted = function captureAttributesOnStarted(shim, in
|
|
|
97
96
|
// out of an excess of caution.
|
|
98
97
|
const connId = evnt.connectionId
|
|
99
98
|
if (connId) {
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} else {
|
|
108
|
-
shim.captureInstanceAttributes(parts[0], parts[1], evnt.databaseName)
|
|
109
|
-
}
|
|
99
|
+
// used in v3 when connection is a cluster pool
|
|
100
|
+
if (typeof connId === 'number') {
|
|
101
|
+
setHostPort(shim, evnt.address, evnt.databaseName, this.$MongoClient)
|
|
102
|
+
// used in v3 when connection is to 1 host
|
|
103
|
+
} else if (typeof connId === 'string') {
|
|
104
|
+
setHostPort(shim, connId, evnt.databaseName)
|
|
105
|
+
// v2 contains `domainSocket`, get socket connection from `host`
|
|
110
106
|
} else if (connId.domainSocket) {
|
|
111
107
|
shim.captureInstanceAttributes('localhost', connId.host, evnt.databaseName)
|
|
108
|
+
// v2 remote connection get `host` `port` from respective properties
|
|
112
109
|
} else {
|
|
113
110
|
shim.captureInstanceAttributes(connId.host, connId.port, evnt.databaseName)
|
|
114
111
|
}
|
|
@@ -116,6 +113,41 @@ common.captureAttributesOnStarted = function captureAttributesOnStarted(shim, in
|
|
|
116
113
|
})
|
|
117
114
|
}
|
|
118
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Extracts the host and port from a connection string
|
|
118
|
+
* This also handles if connection string is a domain socket
|
|
119
|
+
* Mongo sticks the path to the domain socket in the "host" slot, but we
|
|
120
|
+
* want it in the "port", so if we have a domain socket we need to change
|
|
121
|
+
* the order of our parameters.
|
|
122
|
+
*
|
|
123
|
+
* @param {Shim} shim
|
|
124
|
+
* @param {string} connStr
|
|
125
|
+
* @param {string} db database name
|
|
126
|
+
* @param {Object} client mongo client instance
|
|
127
|
+
*/
|
|
128
|
+
function setHostPort(shim, connStr, db, client) {
|
|
129
|
+
const parts = connStr.split(':')
|
|
130
|
+
// in v3 when running with a cluster of socket connections
|
|
131
|
+
// the address is `undefined:undefined`. we will instead attempt
|
|
132
|
+
// to get connection details from the client symbol NR_ATTRS
|
|
133
|
+
// added in `lib/instrumentation/mongodb/v3-mongo` when a client connects
|
|
134
|
+
// with a URL string
|
|
135
|
+
if (parts.includes('undefined')) {
|
|
136
|
+
try {
|
|
137
|
+
const attrs = client[common.NR_ATTRS]
|
|
138
|
+
const socket = decodeURIComponent(attrs.split(',')[0].split('mongodb://')[1])
|
|
139
|
+
shim.captureInstanceAttributes('localhost', socket, db)
|
|
140
|
+
} catch (err) {
|
|
141
|
+
shim.logger.debug(err, 'Could not extract host/port from mongo command')
|
|
142
|
+
}
|
|
143
|
+
// connected using domain socket but the "host"(e.g: /path/to/mongo-socket-port.sock)
|
|
144
|
+
} else if (parts.length && parts[0][0] === '/') {
|
|
145
|
+
shim.captureInstanceAttributes('localhost', parts[0], db)
|
|
146
|
+
} else {
|
|
147
|
+
shim.captureInstanceAttributes(parts[0], parts[1], db)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
119
151
|
/**
|
|
120
152
|
* Get the database_name, host, port_path_or_id
|
|
121
153
|
* for the query segment. v4 refactored where the toplogy is stored.
|
|
@@ -136,13 +168,15 @@ function getInstanceAttributeParameters(shim, obj) {
|
|
|
136
168
|
obj.s.db.s &&
|
|
137
169
|
obj.s.db.s.client &&
|
|
138
170
|
obj.s.db.s.client.s &&
|
|
139
|
-
obj.s.db.s.client.s.
|
|
171
|
+
obj.s.db.s.client.s.options &&
|
|
172
|
+
obj.s.db.s.client.s.options.hosts &&
|
|
173
|
+
obj.s.db.s.client.s.options.hosts.length
|
|
140
174
|
) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
port =
|
|
175
|
+
// hosts is an array but we will always pull the first for consistency
|
|
176
|
+
let [{ host, port, socketPath }] = obj.s.db.s.client.s.options.hosts
|
|
177
|
+
|
|
178
|
+
if (socketPath) {
|
|
179
|
+
port = socketPath
|
|
146
180
|
host = 'localhost'
|
|
147
181
|
}
|
|
148
182
|
return {
|
|
@@ -160,12 +194,17 @@ function getInstanceAttributeParameters(shim, obj) {
|
|
|
160
194
|
}
|
|
161
195
|
|
|
162
196
|
function doCapture(conf, database) {
|
|
163
|
-
let host =
|
|
164
|
-
let port =
|
|
197
|
+
let host = null
|
|
198
|
+
let port = null
|
|
199
|
+
|
|
200
|
+
// servers is an array but we will always pull the first for consistency
|
|
201
|
+
if (conf.servers && conf.servers.length) {
|
|
202
|
+
;[{ host, port }] = conf.servers
|
|
203
|
+
}
|
|
165
204
|
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
if (
|
|
205
|
+
// host is a domain socket. set host as localhost and use the domain
|
|
206
|
+
// socket host as the port
|
|
207
|
+
if (host && host.endsWith('.sock')) {
|
|
169
208
|
port = host
|
|
170
209
|
host = 'localhost'
|
|
171
210
|
}
|
|
@@ -9,7 +9,8 @@ const {
|
|
|
9
9
|
captureAttributesOnStarted,
|
|
10
10
|
instrumentCollection,
|
|
11
11
|
instrumentCursor,
|
|
12
|
-
instrumentDb
|
|
12
|
+
instrumentDb,
|
|
13
|
+
NR_ATTRS
|
|
13
14
|
} = require('./common')
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -30,6 +31,23 @@ function queryParser(operation) {
|
|
|
30
31
|
return { operation, collection }
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Records the `mongo.MongoClient.connect` operations. It also adds the first arg of connect(url)
|
|
36
|
+
* to a Symbol on the MongoClient to be used later to extract the host/port in cases where the topology
|
|
37
|
+
* is a cluster of domain sockets
|
|
38
|
+
*
|
|
39
|
+
* @param {Shim} shim
|
|
40
|
+
* @param {Object} mongodb resolved package
|
|
41
|
+
*/
|
|
42
|
+
function instrumentClient(shim, mongodb) {
|
|
43
|
+
shim.recordOperation(mongodb.MongoClient, 'connect', function wrappedConnect(shim, _, __, args) {
|
|
44
|
+
// Add the connection url to the MongoClient to retrieve later in the `lib/instrumentation/mongo/common`
|
|
45
|
+
// captureAttributesOnStarted listener
|
|
46
|
+
this[NR_ATTRS] = args[0]
|
|
47
|
+
return { callback: shim.LAST }
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
33
51
|
/**
|
|
34
52
|
* Registers relevant instrumentation for mongo >= 3.0.6
|
|
35
53
|
* In 3.0.6 they refactored their "APM" module which removed
|
|
@@ -44,6 +62,7 @@ function queryParser(operation) {
|
|
|
44
62
|
*/
|
|
45
63
|
module.exports = function instrument(shim, mongodb) {
|
|
46
64
|
shim.setParser(queryParser)
|
|
65
|
+
instrumentClient(shim, mongodb)
|
|
47
66
|
const instrumenter = mongodb.instrument(Object.create(null), () => {})
|
|
48
67
|
captureAttributesOnStarted(shim, instrumenter)
|
|
49
68
|
instrumentCursor(shim, mongodb.Cursor)
|
|
@@ -36,15 +36,14 @@ function cmdStartedHandler(shim, evnt) {
|
|
|
36
36
|
if (evnt.connectionId) {
|
|
37
37
|
let [host, port] = evnt.address.split(':')
|
|
38
38
|
|
|
39
|
-
// connection via socket get port from
|
|
40
|
-
//
|
|
39
|
+
// connection via socket get port from 1st host
|
|
40
|
+
// socketPath property
|
|
41
|
+
// which looks like mongodb:///tmp/mongodb-27017.sock"
|
|
41
42
|
if (['undefined'].includes(host, port)) {
|
|
42
43
|
host = 'localhost'
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (socketPath.length && socketPath[1].startsWith('//')) {
|
|
47
|
-
port = decodeURIComponent(socketPath[1].substr(2))
|
|
44
|
+
const hosts = this.s.options.hosts
|
|
45
|
+
if (hosts && hosts.length && hosts[0].socketPath) {
|
|
46
|
+
port = hosts[0].socketPath
|
|
48
47
|
}
|
|
49
48
|
} else if (host === '127.0.0.1') {
|
|
50
49
|
host = 'localhost'
|
package/lib/metrics/names.js
CHANGED
|
@@ -230,10 +230,13 @@ const DISTRIBUTED_TRACE = {
|
|
|
230
230
|
TRANSPORT: 'TransportDuration'
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
const SPAN_EVENT_PREFIX = 'SpanEvent/'
|
|
234
|
+
|
|
233
235
|
const SPAN_EVENTS = {
|
|
234
|
-
SEEN: SUPPORTABILITY.PREFIX + '
|
|
235
|
-
SENT: SUPPORTABILITY.PREFIX + '
|
|
236
|
-
DROPPED: SUPPORTABILITY.PREFIX + '
|
|
236
|
+
SEEN: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'TotalEventsSeen',
|
|
237
|
+
SENT: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'TotalEventsSent',
|
|
238
|
+
DROPPED: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'Discarded',
|
|
239
|
+
LIMIT: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'Limit'
|
|
237
240
|
}
|
|
238
241
|
|
|
239
242
|
const INFINITE_TRACING = {
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
|
-
const logger = require('../logger')
|
|
9
|
-
let spanLogger = null
|
|
8
|
+
const logger = require('../logger').child({ component: 'span_aggregator' })
|
|
10
9
|
const EventAggregator = require('../aggregators/event-aggregator')
|
|
11
10
|
const SpanEvent = require('./span-event')
|
|
12
11
|
const NAMES = require('../metrics/names')
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
const DEFAULT_SPAN_EVENT_LIMIT = 2000
|
|
14
|
+
// Used only when server value missing
|
|
15
|
+
const SPAN_EVENT_FALLBACK_MAX_LIMIT = 10000
|
|
14
16
|
|
|
15
17
|
class SpanEventAggregator extends EventAggregator {
|
|
16
18
|
constructor(opts, collector, metrics) {
|
|
@@ -18,8 +20,6 @@ class SpanEventAggregator extends EventAggregator {
|
|
|
18
20
|
opts.method = opts.method || 'span_event_data'
|
|
19
21
|
opts.metricNames = opts.metricNames || NAMES.SPAN_EVENTS
|
|
20
22
|
|
|
21
|
-
spanLogger = logger.child({ component: 'span_aggregator' })
|
|
22
|
-
|
|
23
23
|
super(opts, collector, metrics)
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ class SpanEventAggregator extends EventAggregator {
|
|
|
27
27
|
const events = this.events
|
|
28
28
|
|
|
29
29
|
if (events.length === 0) {
|
|
30
|
-
|
|
30
|
+
logger.debug('No span events to send.')
|
|
31
31
|
return
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -46,8 +46,8 @@ class SpanEventAggregator extends EventAggregator {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
send() {
|
|
49
|
-
if (
|
|
50
|
-
|
|
49
|
+
if (logger.traceEnabled()) {
|
|
50
|
+
logger.trace(
|
|
51
51
|
{
|
|
52
52
|
spansCollected: this.length,
|
|
53
53
|
spansSeen: this.seen
|
|
@@ -64,7 +64,7 @@ class SpanEventAggregator extends EventAggregator {
|
|
|
64
64
|
* @param {TraceSegment} segment - The segment to add.
|
|
65
65
|
* @param {string} [parentId=null] - The GUID of the parent span.
|
|
66
66
|
*
|
|
67
|
-
* @return {
|
|
67
|
+
* @return {boolean} True if the segment was added, or false if it was discarded.
|
|
68
68
|
*/
|
|
69
69
|
addSegment(segment, parentId, isRoot) {
|
|
70
70
|
// Check if the priority would be accepted before creating the event object.
|
|
@@ -79,7 +79,59 @@ class SpanEventAggregator extends EventAggregator {
|
|
|
79
79
|
const span = SpanEvent.fromSegment(segment, parentId || null, isRoot)
|
|
80
80
|
return this.add(span, tx.priority)
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Reconfigure the `SpanEventAggregator` based on values from server
|
|
85
|
+
*
|
|
86
|
+
* @param {Config} config
|
|
87
|
+
*/
|
|
88
|
+
reconfigure(config) {
|
|
89
|
+
super.reconfigure(config)
|
|
90
|
+
|
|
91
|
+
const { periodMs, limit } = this._getValidSpanConfiguration(config)
|
|
92
|
+
|
|
93
|
+
this.periodMs = periodMs
|
|
94
|
+
this.limit = limit
|
|
95
|
+
this._metrics.getOrCreateMetric(this._metricNames.LIMIT).recordValue(this.limit)
|
|
96
|
+
this._items.setLimit(this.limit)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retrieves report period and harvest limits defined in `span_event_harvest_config`.
|
|
101
|
+
* When no `span_event_harvest_config` has been received from the server, applies an
|
|
102
|
+
* agent-defined fallback maximum to protect against collecting and sending too many spans.
|
|
103
|
+
*
|
|
104
|
+
* @param {Config} config
|
|
105
|
+
*/
|
|
106
|
+
_getValidSpanConfiguration(config) {
|
|
107
|
+
const spanHarvestConfig = config.span_event_harvest_config
|
|
108
|
+
if (spanHarvestConfig) {
|
|
109
|
+
logger.trace('Using span_event_harvest_config values.')
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
periodMs: spanHarvestConfig.report_period_ms,
|
|
113
|
+
limit: spanHarvestConfig.harvest_limit
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const configuredLimit = config.span_events.max_samples_stored || DEFAULT_SPAN_EVENT_LIMIT
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
periodMs: this.defaultPeriod,
|
|
121
|
+
limit: _enforceMaxLimit(configuredLimit, SPAN_EVENT_FALLBACK_MAX_LIMIT)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function _enforceMaxLimit(currentLimit, maxLimit) {
|
|
127
|
+
let spanLimit = currentLimit
|
|
128
|
+
if (spanLimit > maxLimit) {
|
|
129
|
+
spanLimit = maxLimit
|
|
130
|
+
|
|
131
|
+
logger.debug('Using maximum allowed span event limit of %s', maxLimit)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return spanLimit
|
|
82
135
|
}
|
|
83
136
|
|
|
84
137
|
module.exports = SpanEventAggregator
|
|
85
|
-
module.exports.LIMIT = LIMIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -186,7 +186,6 @@
|
|
|
186
186
|
"eslint-plugin-node": "^11.1.0",
|
|
187
187
|
"eslint-plugin-prettier": "^3.4.0",
|
|
188
188
|
"express": "*",
|
|
189
|
-
"fastify": "^2.15.3",
|
|
190
189
|
"generic-pool": "^3.6.1",
|
|
191
190
|
"glob": "^7.1.2",
|
|
192
191
|
"got": "^8.0.1",
|