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 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/docs/
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/docs/.
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/docs/)
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
 
@@ -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) ([v3.6.11](https://github.com/jsdoc/jsdoc/tree/v3.6.11)), distributed under the [Apache-2.0 License](https://github.com/jsdoc/jsdoc/blob/v3.6.11/LICENSE.md):
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.0](https://github.com/tapjs/node-tap/tree/v16.3.0)), distributed under the [ISC License](https://github.com/tapjs/node-tap/blob/v16.3.0/LICENSE):
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-2022 Isaac Z. Schlueter and Contributors
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
@@ -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
- }
@@ -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
 
@@ -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
+ }
@@ -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({ config, key, envVar: value.env, paths, formatter: value.formatter })
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 = 'collector.' + region + '.nr-data.net'
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.pathname
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 createWrappedLogger() {
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
- createModuleUsageMetric('winston', agent.metrics)
38
-
39
- if (isLocalDecoratingEnabled(config) || isMetricsEnabled(config)) {
40
- registerFormatter({ opts, agent, winston })
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
- const winstonLogger = createLogger.apply(this, args)
73
+ createModuleUsageMetric('winston', agent.metrics)
44
74
 
45
- if (isLogForwardingEnabled(config, agent)) {
46
- winstonLogger.add(new NrTransport({ agent }))
47
- }
75
+ if (isLocalDecoratingEnabled(config) || isMetricsEnabled(config)) {
76
+ registerFormatter({ opts, agent, winston })
77
+ }
48
78
 
49
- return winstonLogger
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
  /**
@@ -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
  }
@@ -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',
@@ -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
- requestUri = this.transaction.url || UNKNOWN_URI_PLACEHOLDER
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
@@ -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 = JSON.parse(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.8.0",
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": "^3.6.3",
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.0.1",
237
+ "tap": "^16.3.4",
237
238
  "temp": "^0.8.1",
238
239
  "testdouble": "^3.16.6",
239
240
  "when": "*"