newrelic 9.8.0 → 9.9.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 +37 -2
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +32 -3
- package/lib/collector/facts.js +0 -28
- package/lib/config/default.js +49 -1
- package/lib/config/formatters.js +16 -0
- package/lib/config/index.js +17 -4
- package/lib/instrumentation/core/http-outbound.js +1 -0
- package/lib/instrumentation/core/http.js +1 -1
- package/lib/instrumentation/winston.js +46 -12
- package/lib/instrumentations.js +16 -1
- package/lib/metrics/names.js +0 -1
- package/lib/shim/constants.js +7 -1
- package/lib/shimmer.js +7 -2
- package/lib/transaction/trace/index.js +4 -1
- package/lib/util/urltils.js +24 -0
- package/lib/utilization/gcp-info.js +5 -2
- package/package.json +4 -3
package/NEWS.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
### v9.9.0 (2023-02-06)
|
|
2
|
+
* Added support for url obfuscation using regex (Thanks for your contribution @matewilk)
|
|
3
|
+
* For Distributed Tracing, this means that:
|
|
4
|
+
* Incoming and outgoing requests' `path` will be obfuscated
|
|
5
|
+
* Transaction's `request.url` attribute will be obfuscated
|
|
6
|
+
* Span's `http.uri` path will be obfuscated
|
|
7
|
+
* For transactions, this means that:
|
|
8
|
+
* Transaction trace details `url` will be obfuscated
|
|
9
|
+
* With the following example configuration, url obfuscation will turn `/api/v1/users/12345456/edit` to `/api/v1/users/**/edit`.
|
|
10
|
+
```js
|
|
11
|
+
url_obfuscation: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
regex: {
|
|
14
|
+
pattern: /(\/api\/v1\/users\/)([\d]+)(\/.*$)/,
|
|
15
|
+
flags: "i",
|
|
16
|
+
replacement: '$1**$3'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
* You can also use environment variables to configure url obfuscation:
|
|
21
|
+
```yml
|
|
22
|
+
NEW_RELIC_URL_OBFUSCATION_ENABLED: "true",
|
|
23
|
+
NEW_RELIC_URL_OBFUSCATION_REGEX_PATTERN: '/(\/api\/v1\/users\/)([\d]+)(\/.*$)/',
|
|
24
|
+
NEW_RELIC_URL_OBFUSCATION_REGEX_FLAGS: 'i',
|
|
25
|
+
NEW_RELIC_URL_OBFUSCATION_REGEX_REPLACEMENT: '$1**$3'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
* Add a new tracking type of instrumentation. This will be responsible for logging `Supportability/Features/Instrumentation/OnResolved/<pkg>` and `Supportability/Features/Instrumentation/OnResolved/<pkg>/Version/<version>` metrics when packages are required.
|
|
29
|
+
|
|
30
|
+
### v9.8.1 (2023-01-25)
|
|
31
|
+
|
|
32
|
+
* Changed GCP metadata parsing to use `json-bigint` to avoid loss of precision from numerical instance ID.
|
|
33
|
+
|
|
34
|
+
* Instrumented `winston.loggers.add` so it works like `winston.createLogger`.
|
|
35
|
+
|
|
1
36
|
### v9.8.0 (2023-01-17)
|
|
2
37
|
|
|
3
38
|
* Updated `getBrowserTimingHeader` to allow Browser Agent to be generated even when not in a Transaction by adding `allowTransactionlessInjection` to function options. `allowTransactionlessInjection` is a boolean option, and when set to `true`, will allow injection of the Browser Agent when not in a transaction. This is intended to be used in frameworks that build Static Site Generation(SSG). Note that if you are using this option, you may need to wait until the Node agent has established a connection before calling `getBrowserTimingHeader`. To wait until the agent is connected, you can add the following check to your code:
|
|
@@ -3168,7 +3203,7 @@ Special thanks to Ryan Copley (@RyanCopley) for the contribution.
|
|
|
3168
3203
|
used for instrumenting web frameworks like `restify` or `express`.
|
|
3169
3204
|
|
|
3170
3205
|
Documentation and tutorials for the new API can be found on our GitHub
|
|
3171
|
-
documentation page: http://newrelic.github.io/node-newrelic/
|
|
3206
|
+
documentation page: http://newrelic.github.io/node-newrelic/
|
|
3172
3207
|
|
|
3173
3208
|
* Rewrote built-in instrumentation using the new `Shim` classes.
|
|
3174
3209
|
|
|
@@ -3257,7 +3292,7 @@ Special thanks to Ryan Copley (@RyanCopley) for the contribution.
|
|
|
3257
3292
|
meaning every instrumentation will create Middleware metrics for your server.
|
|
3258
3293
|
|
|
3259
3294
|
Tutorials on using the new instrumentation shim can be found on our API docs:
|
|
3260
|
-
http://newrelic.github.io/node-newrelic
|
|
3295
|
+
http://newrelic.github.io/node-newrelic/.
|
|
3261
3296
|
|
|
3262
3297
|
* Removed `express_segments` feature flag.
|
|
3263
3298
|
|
package/README.md
CHANGED
|
@@ -199,7 +199,7 @@ Here are some resources for learning more about the agent:
|
|
|
199
199
|
|
|
200
200
|
- [New Relic's official Node.js agent documentation](https://docs.newrelic.com/docs/agents/nodejs-agent)
|
|
201
201
|
|
|
202
|
-
- [Developer docs](http://newrelic.github.io/node-newrelic/
|
|
202
|
+
- [Developer docs](http://newrelic.github.io/node-newrelic/)
|
|
203
203
|
|
|
204
204
|
- [Configuring the agent using `newrelic.js` or environment variables](https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration)
|
|
205
205
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -22,6 +22,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
|
|
|
22
22
|
* [@tyriar/fibonacci-heap](#tyriarfibonacci-heap)
|
|
23
23
|
* [concat-stream](#concat-stream)
|
|
24
24
|
* [https-proxy-agent](#https-proxy-agent)
|
|
25
|
+
* [json-bigint](#json-bigint)
|
|
25
26
|
* [json-stringify-safe](#json-stringify-safe)
|
|
26
27
|
* [readable-stream](#readable-stream)
|
|
27
28
|
* [semver](#semver)
|
|
@@ -1202,6 +1203,34 @@ The above copyright notice and this permission notice shall be included in all c
|
|
|
1202
1203
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1203
1204
|
```
|
|
1204
1205
|
|
|
1206
|
+
### json-bigint
|
|
1207
|
+
|
|
1208
|
+
This product includes source derived from [json-bigint](https://github.com/sidorares/json-bigint) ([v1.0.0](https://github.com/sidorares/json-bigint/tree/v1.0.0)), distributed under the [MIT License](https://github.com/sidorares/json-bigint/blob/v1.0.0/LICENSE):
|
|
1209
|
+
|
|
1210
|
+
```
|
|
1211
|
+
The MIT License (MIT)
|
|
1212
|
+
|
|
1213
|
+
Copyright (c) 2013 Andrey Sidorov
|
|
1214
|
+
|
|
1215
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
1216
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
1217
|
+
the Software without restriction, including without limitation the rights to
|
|
1218
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
1219
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
1220
|
+
subject to the following conditions:
|
|
1221
|
+
|
|
1222
|
+
The above copyright notice and this permission notice shall be included in all
|
|
1223
|
+
copies or substantial portions of the Software.
|
|
1224
|
+
|
|
1225
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1226
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
1227
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
1228
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
1229
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
1230
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1231
|
+
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1205
1234
|
### json-stringify-safe
|
|
1206
1235
|
|
|
1207
1236
|
This product includes source derived from [json-stringify-safe](https://github.com/isaacs/json-stringify-safe) ([v5.0.1](https://github.com/isaacs/json-stringify-safe/tree/v5.0.1)), distributed under the [ISC License](https://github.com/isaacs/json-stringify-safe/blob/v5.0.1/LICENSE):
|
|
@@ -2651,7 +2680,7 @@ SOFTWARE.
|
|
|
2651
2680
|
|
|
2652
2681
|
### jsdoc
|
|
2653
2682
|
|
|
2654
|
-
This product includes source derived from [jsdoc](https://github.com/jsdoc/jsdoc) ([
|
|
2683
|
+
This product includes source derived from [jsdoc](https://github.com/jsdoc/jsdoc) ([v4.0.0](https://github.com/jsdoc/jsdoc/tree/v4.0.0)), distributed under the [Apache-2.0 License](https://github.com/jsdoc/jsdoc/blob/v4.0.0/LICENSE.md):
|
|
2655
2684
|
|
|
2656
2685
|
```
|
|
2657
2686
|
# License
|
|
@@ -9326,12 +9355,12 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
9326
9355
|
|
|
9327
9356
|
### tap
|
|
9328
9357
|
|
|
9329
|
-
This product includes source derived from [tap](https://github.com/tapjs/node-tap) ([v16.3.
|
|
9358
|
+
This product includes source derived from [tap](https://github.com/tapjs/node-tap) ([v16.3.4](https://github.com/tapjs/node-tap/tree/v16.3.4)), distributed under the [ISC License](https://github.com/tapjs/node-tap/blob/v16.3.4/LICENSE):
|
|
9330
9359
|
|
|
9331
9360
|
```
|
|
9332
9361
|
The ISC License
|
|
9333
9362
|
|
|
9334
|
-
Copyright (c) 2011-
|
|
9363
|
+
Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors
|
|
9335
9364
|
|
|
9336
9365
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
9337
9366
|
purpose with or without fee is hereby granted, provided that the above
|
package/lib/collector/facts.js
CHANGED
|
@@ -9,12 +9,6 @@ const fetchSystemInfo = require('../system-info')
|
|
|
9
9
|
const logger = require('../logger').child({ component: 'facts' })
|
|
10
10
|
const os = require('os')
|
|
11
11
|
const parseLabels = require('../util/label-parser')
|
|
12
|
-
const NAMES = require('../metrics/names')
|
|
13
|
-
|
|
14
|
-
// For now static and tentative list of which logging libraries we
|
|
15
|
-
// want to track. Later might come up with a better way of populating
|
|
16
|
-
// this list.
|
|
17
|
-
const LOG_LIBRARIES = ['winston', 'bunyan', 'pino', 'loglevel', 'npmlog', 'fancy-log']
|
|
18
12
|
|
|
19
13
|
module.exports = facts
|
|
20
14
|
|
|
@@ -38,8 +32,6 @@ async function facts(agent, callback) {
|
|
|
38
32
|
systemInfo = systemInfo || Object.create(null)
|
|
39
33
|
environment = environment || []
|
|
40
34
|
|
|
41
|
-
countSupportabilityForLogLibraries(environment, agent)
|
|
42
|
-
|
|
43
35
|
const hostname = agent.config.getHostnameSafe()
|
|
44
36
|
const results = {
|
|
45
37
|
utilization: {
|
|
@@ -148,23 +140,3 @@ function getIdentifierOverride(appNames) {
|
|
|
148
140
|
appNames.concat([]).sort().join(',')
|
|
149
141
|
].join(':')
|
|
150
142
|
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* This function searches the computed environment for the presence of
|
|
154
|
-
* some popular logging libraries. If present, it increments the
|
|
155
|
-
* corresponding supportability metrics.
|
|
156
|
-
*
|
|
157
|
-
* @param {Array} environment the environment computed by
|
|
158
|
-
* @param {object} agent the New Relic agent
|
|
159
|
-
*/
|
|
160
|
-
function countSupportabilityForLogLibraries(environment, agent) {
|
|
161
|
-
const packages = environment.find((elt) => elt[0] === 'Packages')[1]
|
|
162
|
-
packages.forEach((pkg) => {
|
|
163
|
-
pkg = JSON.parse(pkg)[0]
|
|
164
|
-
if (LOG_LIBRARIES.includes(pkg)) {
|
|
165
|
-
agent.metrics
|
|
166
|
-
.getOrCreateMetric(`${NAMES.SUPPORTABILITY.NODEJS_DEPENDENCIES}/${pkg}`)
|
|
167
|
-
.incrementCallCount()
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
}
|
package/lib/config/default.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const defaultConfig = module.exports
|
|
9
|
-
const { array, int, float, boolean, object, objectList, allowList } = require('./formatters')
|
|
9
|
+
const { array, int, float, boolean, object, objectList, allowList, regex } = require('./formatters')
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* A function that returns the definition of the agent configuration
|
|
@@ -52,6 +52,13 @@ defaultConfig.definition = () => ({
|
|
|
52
52
|
* You shouldn't need to change this.
|
|
53
53
|
*/
|
|
54
54
|
host: '',
|
|
55
|
+
/**
|
|
56
|
+
* Endpoint to send OpenTelemetry spans to.
|
|
57
|
+
*
|
|
58
|
+
* This should be automatically deduced from your region and other
|
|
59
|
+
* settings, but if desired, you can override it.
|
|
60
|
+
*/
|
|
61
|
+
otlp_endpoint: '',
|
|
55
62
|
/**
|
|
56
63
|
* The port on which the collector proxy will be listening.
|
|
57
64
|
*
|
|
@@ -1208,6 +1215,47 @@ defaultConfig.definition = () => ({
|
|
|
1208
1215
|
formatter: boolean,
|
|
1209
1216
|
default: false
|
|
1210
1217
|
}
|
|
1218
|
+
},
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Obfuscates URL parameters
|
|
1222
|
+
* for outgoing and incoming requests
|
|
1223
|
+
* for distrubuted tracing attributes - both transaction and span attributes
|
|
1224
|
+
* for transaction trace transaction details
|
|
1225
|
+
*/
|
|
1226
|
+
url_obfuscation: {
|
|
1227
|
+
/**
|
|
1228
|
+
* Toggles whether to obfuscate URL parameters
|
|
1229
|
+
*/
|
|
1230
|
+
enabled: {
|
|
1231
|
+
formatter: boolean,
|
|
1232
|
+
default: false
|
|
1233
|
+
},
|
|
1234
|
+
|
|
1235
|
+
regex: {
|
|
1236
|
+
/**
|
|
1237
|
+
* A regular expression to match URL parameters to obfuscate
|
|
1238
|
+
*/
|
|
1239
|
+
pattern: {
|
|
1240
|
+
formatter: regex,
|
|
1241
|
+
default: null
|
|
1242
|
+
},
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* A string containing RegEx flags to use when matching URL parameters
|
|
1246
|
+
*/
|
|
1247
|
+
flags: {
|
|
1248
|
+
default: ''
|
|
1249
|
+
},
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* A string containing a replacement value for URL parameters
|
|
1253
|
+
* can contain refferences to capture groups in the pattern
|
|
1254
|
+
*/
|
|
1255
|
+
replacement: {
|
|
1256
|
+
default: ''
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1211
1259
|
}
|
|
1212
1260
|
})
|
|
1213
1261
|
|
package/lib/config/formatters.js
CHANGED
|
@@ -106,3 +106,19 @@ formatters.objectList = function objectList(val, logger) {
|
|
|
106
106
|
formatters.allowList = function allowList(list, val) {
|
|
107
107
|
return list.includes(val) ? val : list[0]
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Parse a config setting as a regex
|
|
112
|
+
*
|
|
113
|
+
* @param {string} val valid regex
|
|
114
|
+
* @param {logger} logger agent logger instance
|
|
115
|
+
* @returns {RegExp} regex
|
|
116
|
+
*/
|
|
117
|
+
formatters.regex = function regex(val, logger) {
|
|
118
|
+
try {
|
|
119
|
+
return new RegExp(val)
|
|
120
|
+
} catch (error) {
|
|
121
|
+
logger.error(`New Relic configurator could not validate regex: ${val}`)
|
|
122
|
+
logger.error(error.stack)
|
|
123
|
+
}
|
|
124
|
+
}
|
package/lib/config/index.js
CHANGED
|
@@ -993,7 +993,7 @@ Config.prototype._fromPassed = function _fromPassed(external, internal, arbitrar
|
|
|
993
993
|
return
|
|
994
994
|
}
|
|
995
995
|
|
|
996
|
-
if (typeof node === 'object' && !Array.isArray(node)) {
|
|
996
|
+
if (typeof node === 'object' && !Array.isArray(node) && !(node instanceof RegExp)) {
|
|
997
997
|
// is top level and can have arbitrary keys
|
|
998
998
|
const allowArbitrary = internal === this || HAS_ARBITRARY_KEYS.has(key)
|
|
999
999
|
this._fromPassed(node, internal[key], allowArbitrary)
|
|
@@ -1095,7 +1095,13 @@ Config.prototype._fromEnvironment = function _fromEnvironment(
|
|
|
1095
1095
|
setFromEnv({ config, key, envVar, paths })
|
|
1096
1096
|
} else if (type === 'object') {
|
|
1097
1097
|
if (value.hasOwnProperty('env')) {
|
|
1098
|
-
setFromEnv({
|
|
1098
|
+
setFromEnv({
|
|
1099
|
+
config,
|
|
1100
|
+
key,
|
|
1101
|
+
envVar: value.env,
|
|
1102
|
+
paths,
|
|
1103
|
+
formatter: value.formatter
|
|
1104
|
+
})
|
|
1099
1105
|
} else if (value.hasOwnProperty('default')) {
|
|
1100
1106
|
const envVar = deriveEnvVar(key, paths)
|
|
1101
1107
|
setFromEnv({ config, key, envVar, formatter: value.formatter, paths })
|
|
@@ -1298,14 +1304,21 @@ Config.prototype._canonicalize = function _canonicalize() {
|
|
|
1298
1304
|
const level = this.logging.level
|
|
1299
1305
|
this.logging.level = logAliases[level] || level
|
|
1300
1306
|
|
|
1307
|
+
const region = parseKey(this.license_key)
|
|
1301
1308
|
if (this.host === '') {
|
|
1302
|
-
const region = parseKey(this.license_key)
|
|
1303
1309
|
if (region) {
|
|
1304
|
-
this.host =
|
|
1310
|
+
this.host = `collector.${region}.nr-data.net`
|
|
1305
1311
|
} else {
|
|
1306
1312
|
this.host = 'collector.newrelic.com'
|
|
1307
1313
|
}
|
|
1308
1314
|
}
|
|
1315
|
+
if (this.otlp_endpoint === '') {
|
|
1316
|
+
if (region) {
|
|
1317
|
+
this.otlp_endpoint = `otlp.${region}.nr-data.net`
|
|
1318
|
+
} else {
|
|
1319
|
+
this.otlp_endpoint = 'otlp.nr-data.net'
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1309
1322
|
|
|
1310
1323
|
if (this.license_key) {
|
|
1311
1324
|
this.license_key = this.license_key.trim()
|
|
@@ -215,6 +215,7 @@ function applySegment(opts, makeRequest, hostname, segment) {
|
|
|
215
215
|
segment.start()
|
|
216
216
|
const request = makeRequest(opts)
|
|
217
217
|
const parsed = urltils.scrubAndParseParameters(request.path)
|
|
218
|
+
parsed.path = urltils.obfuscatePath(segment.transaction.agent.config, parsed.path)
|
|
218
219
|
const proto = parsed.protocol || opts.protocol || 'http:'
|
|
219
220
|
segment.name += parsed.path
|
|
220
221
|
request[symbols.segment] = segment
|
|
@@ -86,7 +86,7 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
86
86
|
|
|
87
87
|
// the error tracer needs a URL for tracing, even though naming overwrites
|
|
88
88
|
transaction.parsedUrl = url.parse(request.url, true)
|
|
89
|
-
transaction.url = transaction.parsedUrl.
|
|
89
|
+
transaction.url = urltils.obfuscatePath(agent.config, transaction.parsedUrl.path)
|
|
90
90
|
transaction.verb = request.method
|
|
91
91
|
|
|
92
92
|
// URL is sent as an agent attribute with transaction events
|
|
@@ -27,28 +27,62 @@ module.exports = function instrument(agent, winston, _, shim) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
shim.wrap(winston, 'createLogger', function wrapCreate(shim, createLogger) {
|
|
30
|
-
return function
|
|
30
|
+
return function wrappedCreateLogger() {
|
|
31
31
|
const args = shim.argsToArray.apply(shim, arguments)
|
|
32
32
|
const opts = args[0] || {}
|
|
33
33
|
if (isStream(opts)) {
|
|
34
34
|
return createLogger.apply(this, args)
|
|
35
35
|
}
|
|
36
|
+
return performInstrumentation({
|
|
37
|
+
obj: this,
|
|
38
|
+
args,
|
|
39
|
+
opts,
|
|
40
|
+
agent,
|
|
41
|
+
winston,
|
|
42
|
+
registerLogger: createLogger
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
})
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
shim.wrap(winston.loggers, 'add', function wrapAdd(shim, add) {
|
|
48
|
+
return function wrappedAdd() {
|
|
49
|
+
const args = shim.argsToArray.apply(shim, arguments)
|
|
50
|
+
const id = args[0]
|
|
51
|
+
const opts = args[1] || {}
|
|
52
|
+
// add does nothing if the logger has already been added, so we
|
|
53
|
+
// have to do the same nothingness here.
|
|
54
|
+
const alreadyAdded = this.loggers.has(id)
|
|
55
|
+
if (alreadyAdded || isStream(opts)) {
|
|
56
|
+
return add.apply(this, args)
|
|
41
57
|
}
|
|
58
|
+
return performInstrumentation({
|
|
59
|
+
obj: this,
|
|
60
|
+
args,
|
|
61
|
+
opts,
|
|
62
|
+
agent,
|
|
63
|
+
winston,
|
|
64
|
+
registerLogger: add
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function performInstrumentation({ obj, args, opts, agent, winston, registerLogger }) {
|
|
71
|
+
const config = agent.config
|
|
42
72
|
|
|
43
|
-
|
|
73
|
+
createModuleUsageMetric('winston', agent.metrics)
|
|
44
74
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
75
|
+
if (isLocalDecoratingEnabled(config) || isMetricsEnabled(config)) {
|
|
76
|
+
registerFormatter({ opts, agent, winston })
|
|
77
|
+
}
|
|
48
78
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
79
|
+
const winstonLogger = registerLogger.apply(obj, args)
|
|
80
|
+
|
|
81
|
+
if (isLogForwardingEnabled(config, agent)) {
|
|
82
|
+
winstonLogger.add(new NrTransport({ agent }))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return winstonLogger
|
|
52
86
|
}
|
|
53
87
|
|
|
54
88
|
/**
|
package/lib/instrumentations.js
CHANGED
|
@@ -38,6 +38,21 @@ module.exports = function instrumentations() {
|
|
|
38
38
|
'undici': { type: MODULE_TYPE.TRANSACTION },
|
|
39
39
|
'@hapi/vision': { type: MODULE_TYPE.WEB_FRAMEWORK },
|
|
40
40
|
'when': { module: './instrumentation/when' },
|
|
41
|
-
'winston': { type: MODULE_TYPE.GENERIC }
|
|
41
|
+
'winston': { type: MODULE_TYPE.GENERIC },
|
|
42
|
+
/**
|
|
43
|
+
* The modules below are listed here purely to take
|
|
44
|
+
* advantage of the Supportability/Features/onRequire/<module>
|
|
45
|
+
* metrics for libraries we want to track for some reason or another.
|
|
46
|
+
* The big uses cases are:
|
|
47
|
+
* Logging libraries we want to instrument in the future
|
|
48
|
+
* Libraries that have OpenTelemetry instrumentation we want to register
|
|
49
|
+
* or have already registered.
|
|
50
|
+
*/
|
|
51
|
+
'loglevel': { type: MODULE_TYPE.TRACKING },
|
|
52
|
+
'npmlog': { type: MODULE_TYPE.TRACKING },
|
|
53
|
+
'fancy-log': { type: MODULE_TYPE.TRACKING },
|
|
54
|
+
'@prisma/client': { type: MODULE_TYPE.TRACKING },
|
|
55
|
+
'@nestjs/core': { type: MODULE_TYPE.TRACKING },
|
|
56
|
+
'knex': { type: MODULE_TYPE.TRACKING }
|
|
42
57
|
}
|
|
43
58
|
}
|
package/lib/metrics/names.js
CHANGED
|
@@ -23,7 +23,6 @@ const SUPPORTABILITY = {
|
|
|
23
23
|
TRANSACTION_API: 'Supportability/API/Transaction',
|
|
24
24
|
UTILIZATION: 'Supportability/utilization',
|
|
25
25
|
DEPENDENCIES: 'Supportability/InstalledDependencies',
|
|
26
|
-
NODEJS_DEPENDENCIES: 'Supportability/InstalledDependencies/Nodejs',
|
|
27
26
|
NODEJS: 'Supportability/Nodejs',
|
|
28
27
|
REGISTRATION: 'Supportability/Registration',
|
|
29
28
|
EVENT_HARVEST: 'Supportability/EventHarvest',
|
package/lib/shim/constants.js
CHANGED
|
@@ -32,5 +32,11 @@ exports.MODULE_TYPE = {
|
|
|
32
32
|
TRANSACTION: 'transaction',
|
|
33
33
|
|
|
34
34
|
/** Web server framework module, such as Express or Restify. */
|
|
35
|
-
WEB_FRAMEWORK: 'web-framework'
|
|
35
|
+
WEB_FRAMEWORK: 'web-framework',
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Used to load supportability metrics on installed verisions of packages
|
|
39
|
+
* that the Node.js agent does not instrument(i.e. - otel instrumentation or top logging libraries)
|
|
40
|
+
*/
|
|
41
|
+
TRACKING: 'tracking'
|
|
36
42
|
}
|
package/lib/shimmer.js
CHANGED
|
@@ -530,6 +530,13 @@ function instrumentPostLoad(agent, nodule, moduleName, resolvedName) {
|
|
|
530
530
|
NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE
|
|
531
531
|
)
|
|
532
532
|
|
|
533
|
+
// Tracking instrumentation is only used to add the supportability metrics
|
|
534
|
+
// that occur directly above this. No reason to attempt to load instrumentation
|
|
535
|
+
// as it does not exist.
|
|
536
|
+
if (instrumentation.type === MODULE_TYPE.TRACKING) {
|
|
537
|
+
return nodule
|
|
538
|
+
}
|
|
539
|
+
|
|
533
540
|
try {
|
|
534
541
|
if (instrumentation.onRequire(shim, nodule, moduleName) !== false) {
|
|
535
542
|
nodule = shim.getExport(nodule)
|
|
@@ -580,7 +587,6 @@ function _firstPartyInstrumentation(agent, fileName, shim, nodule, moduleName) {
|
|
|
580
587
|
|
|
581
588
|
function _postLoad(agent, nodule, name, resolvedName) {
|
|
582
589
|
const instrumentation = shimmer.getInstrumentationNameFromModuleName(name)
|
|
583
|
-
|
|
584
590
|
const registeredInstrumentation = shimmer.registeredInstrumentations[instrumentation]
|
|
585
591
|
const hasPostLoadInstrumentation =
|
|
586
592
|
registeredInstrumentation && registeredInstrumentation.onRequire
|
|
@@ -596,7 +602,6 @@ function _postLoad(agent, nodule, name, resolvedName) {
|
|
|
596
602
|
|
|
597
603
|
function _onResolveFileName(agent, requiredNameOrPath, resolvedFilepath) {
|
|
598
604
|
const instrumentation = shimmer.getInstrumentationNameFromModuleName(requiredNameOrPath)
|
|
599
|
-
|
|
600
605
|
const registeredInstrumentation = shimmer.registeredInstrumentations[instrumentation]
|
|
601
606
|
const hasResolvedFileInstrumentation =
|
|
602
607
|
registeredInstrumentation && registeredInstrumentation.onResolved
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
/* eslint sonarjs/cognitive-complexity: ["error", 19] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
|
|
9
9
|
|
|
10
10
|
const codec = require('../../util/codec')
|
|
11
|
+
const urltils = require('../../util/urltils')
|
|
11
12
|
const Segment = require('./segment')
|
|
12
13
|
const { Attributes, MAXIMUM_CUSTOM_ATTRIBUTES } = require('../../attributes')
|
|
13
14
|
const logger = require('../../logger').child({ component: 'trace' })
|
|
@@ -329,7 +330,9 @@ Trace.prototype._getRequestUri = function _getRequestUri() {
|
|
|
329
330
|
const canAddUri = this.attributes.hasValidDestination(DESTINATIONS.TRANS_TRACE, REQUEST_URI_KEY)
|
|
330
331
|
let requestUri = null // must be null if excluded
|
|
331
332
|
if (canAddUri) {
|
|
332
|
-
|
|
333
|
+
// obfuscate the path if config is set
|
|
334
|
+
const url = urltils.obfuscatePath(this.transaction.agent.config, this.transaction.url)
|
|
335
|
+
requestUri = url || UNKNOWN_URI_PLACEHOLDER
|
|
333
336
|
}
|
|
334
337
|
|
|
335
338
|
return requestUri
|
package/lib/util/urltils.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const url = require('url')
|
|
9
|
+
const logger = require('../logger').child({ component: 'urltils' })
|
|
9
10
|
|
|
10
11
|
const LOCALHOST_NAMES = {
|
|
11
12
|
'localhost': true,
|
|
@@ -162,6 +163,29 @@ module.exports = {
|
|
|
162
163
|
}
|
|
163
164
|
},
|
|
164
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Obfuscates path parameters with regex from config
|
|
168
|
+
*
|
|
169
|
+
* @param {Config} config The configuration containing the regex
|
|
170
|
+
* @param {string} path The path to be obfuscated
|
|
171
|
+
* @returns {string} The obfuscated path or the original path
|
|
172
|
+
*/
|
|
173
|
+
obfuscatePath: function obfuscatePath(config, path) {
|
|
174
|
+
const { enabled, regex } = config.url_obfuscation
|
|
175
|
+
if (typeof path !== 'string' || !enabled || !regex) {
|
|
176
|
+
return path
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const { pattern, flags = '', replacement = '' } = regex
|
|
180
|
+
try {
|
|
181
|
+
const regexPattern = new RegExp(pattern, flags)
|
|
182
|
+
return path.replace(regexPattern, replacement)
|
|
183
|
+
} catch (e) {
|
|
184
|
+
logger.warn('Invalid regular expression for url_obfuscation.regex.pattern', pattern)
|
|
185
|
+
return path
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
165
189
|
/**
|
|
166
190
|
* Copy a set of request parameters from one object to another,
|
|
167
191
|
* but do not overwrite any existing parameters in destination,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const logger = require('../logger.js').child({ component: 'gcp-info' })
|
|
9
9
|
const common = require('./common')
|
|
10
10
|
const NAMES = require('../metrics/names.js')
|
|
11
|
+
const JSONbig = require('json-bigint')({ useNativeBigInt: true })
|
|
11
12
|
let resultDict = null
|
|
12
13
|
|
|
13
14
|
module.exports = fetchGCPInfo
|
|
@@ -37,9 +38,11 @@ function fetchGCPInfo(agent, callback) {
|
|
|
37
38
|
if (err) {
|
|
38
39
|
return callback(err)
|
|
39
40
|
}
|
|
40
|
-
|
|
41
41
|
try {
|
|
42
|
-
data =
|
|
42
|
+
data = JSONbig.parse(data)
|
|
43
|
+
if (typeof data.id !== 'string') {
|
|
44
|
+
data.id = data.id.toString()
|
|
45
|
+
}
|
|
43
46
|
} catch (e) {
|
|
44
47
|
logger.debug(e, 'Failed to parse GCP metadata.')
|
|
45
48
|
data = null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.9.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -187,6 +187,7 @@
|
|
|
187
187
|
"@tyriar/fibonacci-heap": "^2.0.7",
|
|
188
188
|
"concat-stream": "^2.0.0",
|
|
189
189
|
"https-proxy-agent": "^5.0.0",
|
|
190
|
+
"json-bigint": "^1.0.0",
|
|
190
191
|
"json-stringify-safe": "^5.0.0",
|
|
191
192
|
"readable-stream": "^3.6.0",
|
|
192
193
|
"semver": "^5.3.0",
|
|
@@ -220,7 +221,7 @@
|
|
|
220
221
|
"glob": "^7.1.2",
|
|
221
222
|
"got": "^11.8.5",
|
|
222
223
|
"husky": "^6.0.0",
|
|
223
|
-
"jsdoc": "^
|
|
224
|
+
"jsdoc": "^4.0.0",
|
|
224
225
|
"lint-staged": "^11.0.0",
|
|
225
226
|
"lockfile-lint": "^4.9.6",
|
|
226
227
|
"memcached": ">=0.2.8",
|
|
@@ -233,7 +234,7 @@
|
|
|
233
234
|
"rimraf": "^2.6.3",
|
|
234
235
|
"should": "*",
|
|
235
236
|
"sinon": "^4.5.0",
|
|
236
|
-
"tap": "^16.
|
|
237
|
+
"tap": "^16.3.4",
|
|
237
238
|
"temp": "^0.8.1",
|
|
238
239
|
"testdouble": "^3.16.6",
|
|
239
240
|
"when": "*"
|