newrelic 13.6.5 → 13.6.6
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 +15 -0
- package/lib/config/default.js +19 -70
- package/lib/config/index.js +6 -99
- package/lib/config/samplers.js +196 -0
- package/lib/subscribers/express/use.js +72 -21
- package/package.json +1 -1
package/NEWS.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
### v13.6.6 (2025-11-19)
|
|
2
|
+
|
|
3
|
+
#### Bug fixes
|
|
4
|
+
|
|
5
|
+
* Updated `app.use` or `router.use` Express instrumentation to properly wrap all middleware defined ([#3520](https://github.com/newrelic/node-newrelic/pull/3520)) ([d3b20d0](https://github.com/newrelic/node-newrelic/commit/d3b20d09a9c3d21bff081771d8c11caff9c6a6e5))
|
|
6
|
+
|
|
7
|
+
#### Documentation
|
|
8
|
+
|
|
9
|
+
* Updated compatibility report ([#3522](https://github.com/newrelic/node-newrelic/pull/3522)) ([72ec652](https://github.com/newrelic/node-newrelic/commit/72ec65252700ab5f3831dab35d05b607a0723aa6)) ([#3516](https://github.com/newrelic/node-newrelic/pull/3516)) ([35defb0](https://github.com/newrelic/node-newrelic/commit/35defb009b7d3bbec0ffe084b434e368aa1cbc7c))
|
|
10
|
+
|
|
11
|
+
#### Tests
|
|
12
|
+
|
|
13
|
+
* Pinned next.js to skip 11.0.10 and 11.0.11 ([#3521](https://github.com/newrelic/node-newrelic/pull/3521)) ([20977ee](https://github.com/newrelic/node-newrelic/commit/20977eeb8552b7a445f5407d8df272aae49ea873))
|
|
14
|
+
|
|
1
15
|
### v13.6.5 (2025-11-17)
|
|
2
16
|
|
|
3
17
|
#### Bug fixes
|
|
@@ -8064,3 +8078,4 @@ Special thanks to Ryan Copley (@RyanCopley) for the contribution.
|
|
|
8064
8078
|
|
|
8065
8079
|
|
|
8066
8080
|
|
|
8081
|
+
|
package/lib/config/default.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const defaultConfig = module.exports
|
|
9
9
|
const { array, int, float, boolean, object, objectList, allowList, regex } = require('./formatters')
|
|
10
10
|
const pkgInstrumentation = require('./build-instrumentation-config')
|
|
11
|
+
const { config: samplerConfigs } = require('./samplers')
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* A function that returns the definition of the agent configuration
|
|
@@ -1156,6 +1157,7 @@ defaultConfig.definition = () => {
|
|
|
1156
1157
|
},
|
|
1157
1158
|
|
|
1158
1159
|
sampler: {
|
|
1160
|
+
...samplerConfigs,
|
|
1159
1161
|
/**
|
|
1160
1162
|
* The sampling target for adaptive sampling is controlled via this attribute when configuring the default/adaptive sampler.
|
|
1161
1163
|
* The default sampling target is 10 transactions/min when it is not specified but **MUST** be within the range of [1, 120] (inclusive).
|
|
@@ -1166,77 +1168,23 @@ defaultConfig.definition = () => {
|
|
|
1166
1168
|
formatter: int,
|
|
1167
1169
|
default: 10
|
|
1168
1170
|
},
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
//
|
|
1176
|
-
// Example setting root sampler via config to trace id ratio based -
|
|
1177
|
-
// root: {
|
|
1178
|
-
// trace_id_ratio_based: {
|
|
1179
|
-
// ratio: 0.5
|
|
1180
|
-
// }
|
|
1181
|
-
// }
|
|
1182
|
-
//
|
|
1183
|
-
root: {
|
|
1184
|
-
formatter: allowList.bind(null, ['trace_id_ratio_based', 'adaptive', 'always_on', 'always_off', 'default']),
|
|
1185
|
-
default: 'default',
|
|
1186
|
-
// if setting root to trace_id_ratio_based via env vars, set NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_ROOT: 'trace_id_ratio_based'
|
|
1187
|
-
// and then set NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_ROOT_TRACE_ID_RATIO_BASED_RATIO to the desired ratio
|
|
1188
|
-
trace_id_ratio_based: {
|
|
1189
|
-
ratio: {
|
|
1190
|
-
formatter: float,
|
|
1191
|
-
env: 'NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_ROOT_TRACE_ID_RATIO_BASED_RATIO'
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
},
|
|
1195
|
-
|
|
1196
|
-
/**
|
|
1197
|
-
* When set to `always_on`, the sampled flag in the `traceparent` header
|
|
1198
|
-
* being set to "true" will result in the local transaction being sampled
|
|
1199
|
-
* with a priority value of "2". When set to `always_off`, the local
|
|
1200
|
-
* transaction will never be sampled. At the default setting, the sampling
|
|
1201
|
-
* decision will be determined according to the normal algorithm.
|
|
1202
|
-
*
|
|
1203
|
-
* This setting takes precedence over the `remote_parent_not_sampled`
|
|
1204
|
-
* setting.
|
|
1205
|
-
*/
|
|
1206
|
-
remote_parent_sampled: {
|
|
1207
|
-
formatter: allowList.bind(null, ['trace_id_ratio_based', 'adaptive', 'always_on', 'always_off', 'default']),
|
|
1208
|
-
default: 'default',
|
|
1209
|
-
// if setting remote_parent_sampled to trace_id_ratio_based via env vars, set NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_REMOTE_PARENT_SAMPLED: 'trace_id_ratio_based'
|
|
1210
|
-
// and then set NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_REMOTE_PARENT_SAMPLED_TRACE_ID_RATIO_BASED_RATIO to the desired ratio
|
|
1211
|
-
trace_id_ratio_based: {
|
|
1212
|
-
ratio: {
|
|
1213
|
-
formatter: float,
|
|
1214
|
-
env: 'NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_REMOTE_PARENT_SAMPLED_TRACE_ID_RATIO_BASED_RATIO'
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1171
|
+
full_granularity: {
|
|
1172
|
+
enabled: {
|
|
1173
|
+
default: true,
|
|
1174
|
+
formatter: boolean
|
|
1175
|
+
},
|
|
1176
|
+
...samplerConfigs
|
|
1217
1177
|
},
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
*/
|
|
1229
|
-
remote_parent_not_sampled: {
|
|
1230
|
-
formatter: allowList.bind(null, ['trace_id_ratio_based', 'adaptive', 'always_on', 'always_off', 'default']),
|
|
1231
|
-
default: 'default',
|
|
1232
|
-
// if setting remote_parent_not_sampled to trace_id_ratio_based via env vars, set NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_REMOTE_PARENT_NOT_SAMPLED: 'trace_id_ratio_based'
|
|
1233
|
-
// and then set NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_REMOTE_PARENT_NOT_SAMPLED_TRACE_ID_RATIO_BASED_RATIO to the desired ratio
|
|
1234
|
-
trace_id_ratio_based: {
|
|
1235
|
-
ratio: {
|
|
1236
|
-
formatter: float,
|
|
1237
|
-
env: 'NEW_RELIC_DISTRIBUTED_TRACING_SAMPLER_REMOTE_PARENT_NOT_SAMPLED_TRACE_ID_RATIO_BASED_RATIO'
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1178
|
+
partial_granularity: {
|
|
1179
|
+
enabled: {
|
|
1180
|
+
default: false,
|
|
1181
|
+
formatter: boolean
|
|
1182
|
+
},
|
|
1183
|
+
type: {
|
|
1184
|
+
formatter: allowList.bind(null, ['compact', 'essential', 'reduced']),
|
|
1185
|
+
default: 'essential'
|
|
1186
|
+
},
|
|
1187
|
+
...samplerConfigs
|
|
1240
1188
|
}
|
|
1241
1189
|
}
|
|
1242
1190
|
},
|
|
@@ -1909,4 +1857,5 @@ function setNestedKey(obj, keys, value) {
|
|
|
1909
1857
|
|
|
1910
1858
|
obj[keys[len - 1]] = value
|
|
1911
1859
|
}
|
|
1860
|
+
|
|
1912
1861
|
defaultConfig.setNestedKey = setNestedKey
|
package/lib/config/index.js
CHANGED
|
@@ -25,6 +25,7 @@ const mergeServerConfig = new MergeServerConfig()
|
|
|
25
25
|
const { boolean: isTruthular } = require('./formatters')
|
|
26
26
|
const configDefinition = definition()
|
|
27
27
|
const parseLabels = require('../util/label-parser')
|
|
28
|
+
const { buildTraceIdRatioSamplers, setTraceIdRatioSamplerFromEnv } = require('./samplers')
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* CONSTANTS -- we gotta lotta 'em
|
|
@@ -1065,7 +1066,7 @@ Config.prototype._fromPassed = function _fromPassed(external, internal, arbitrar
|
|
|
1065
1066
|
}
|
|
1066
1067
|
|
|
1067
1068
|
// Handle trace_id_ratio_based for distributed tracing sampler configuration
|
|
1068
|
-
|
|
1069
|
+
buildTraceIdRatioSamplers({ config: external, key, incomingConfig: internal, logger })
|
|
1069
1070
|
|
|
1070
1071
|
if (key === 'ssl' && !isTruthular(external.ssl)) {
|
|
1071
1072
|
logger.warn(SSL_WARNING)
|
|
@@ -1090,54 +1091,6 @@ Config.prototype._fromPassed = function _fromPassed(external, internal, arbitrar
|
|
|
1090
1091
|
}
|
|
1091
1092
|
}
|
|
1092
1093
|
|
|
1093
|
-
/**
|
|
1094
|
-
* Builds the trace_id_ratio_based for distributed tracing sampler configuration structure
|
|
1095
|
-
*
|
|
1096
|
-
* @param {string} key - The configuration key being processed
|
|
1097
|
-
* @param {object} internal - The internal configuration object being modified
|
|
1098
|
-
* @param {object} samplerConfig - Sampler configuration object from defaultsConfig
|
|
1099
|
-
*/
|
|
1100
|
-
Config.prototype._buildTraceIdRatioSamplers = function _buildTraceIdRatioSamplers(key, internal, samplerConfig) {
|
|
1101
|
-
if (key !== 'sampler') {
|
|
1102
|
-
return
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
const samplers = Object.keys(samplerConfig)
|
|
1106
|
-
|
|
1107
|
-
// user can set multiple samplers
|
|
1108
|
-
for (const sampler of samplers) {
|
|
1109
|
-
if (this._isTraceIdRatioBasedConfig(sampler, samplerConfig)) {
|
|
1110
|
-
const ratioValue = samplerConfig[sampler].trace_id_ratio_based.ratio
|
|
1111
|
-
if (ratioValue) {
|
|
1112
|
-
internal[key][sampler] = {
|
|
1113
|
-
trace_id_ratio_based: {
|
|
1114
|
-
ratio: ratioValue
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
logger.trace('Setting trace id ratio based sampler on %s', samplers)
|
|
1118
|
-
} else {
|
|
1119
|
-
logger.error('Not setting trace id ratio based sampler on %s.', samplers)
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
/**
|
|
1126
|
-
* Checks if the external config contains a trace_id_ratio_based for distributed tracing
|
|
1127
|
-
* sampler configuration
|
|
1128
|
-
*
|
|
1129
|
-
* @param {string} selectedSampler - The selected sampler key
|
|
1130
|
-
* @param {object} samplerConfig - The sampler configuration object
|
|
1131
|
-
* @returns {boolean} true if it's a trace_id_ratio_based config
|
|
1132
|
-
*/
|
|
1133
|
-
Config.prototype._isTraceIdRatioBasedConfig = function _isTraceIdRatioBasedConfig(selectedSampler, samplerConfig) {
|
|
1134
|
-
const samplers = ['root', 'remote_parent_sampled', 'remote_parent_not_sampled']
|
|
1135
|
-
|
|
1136
|
-
return samplers.includes(selectedSampler) &&
|
|
1137
|
-
typeof samplerConfig[selectedSampler] === 'object' &&
|
|
1138
|
-
'trace_id_ratio_based' in samplerConfig[selectedSampler]
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
1094
|
/**
|
|
1142
1095
|
* Some values should be picked up only if they're not otherwise set, like
|
|
1143
1096
|
* the Windows / Azure application name. Don't set it if there's already
|
|
@@ -1214,53 +1167,6 @@ function setFromEnv({ config, key, envVar, formatter, paths }) {
|
|
|
1214
1167
|
}
|
|
1215
1168
|
}
|
|
1216
1169
|
|
|
1217
|
-
/**
|
|
1218
|
-
* Assigns the value of the distributed tracing sampler env var as an trace_id_ratio_based
|
|
1219
|
-
* object to the sampler in the config.
|
|
1220
|
-
*
|
|
1221
|
-
* @param {object} params object passed to fn
|
|
1222
|
-
* @param {string} params.key key of the sampler
|
|
1223
|
-
* Example: 'root' or 'remote_parent_sampled'
|
|
1224
|
-
* @param {object} params.value value of the sampler
|
|
1225
|
-
* Example: { default: 'default', formatter: someFunc, trace_id_ratio_based: {ratio} }
|
|
1226
|
-
* @param {object} params.config agent config
|
|
1227
|
-
* @param {Array} params.paths list of leaf nodes leading to the sampling configuration value
|
|
1228
|
-
* Example: ['distributed_tracing', 'sampler']
|
|
1229
|
-
*/
|
|
1230
|
-
function setTraceIDRatioSampler({ key, value, config, paths }) {
|
|
1231
|
-
// trace id ratio based sampler is nested in leaf nodes under distributed_tracing > samplers > key
|
|
1232
|
-
// so don't continue if path is empty
|
|
1233
|
-
if (paths.length === 0) {
|
|
1234
|
-
return
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
const samplers = ['root', 'remote_parent_sampled', 'remote_parent_not_sampled']
|
|
1238
|
-
if (samplers.includes(key) && paths[paths.length - 1] === 'sampler') {
|
|
1239
|
-
// config.distributed_tracing.sampler.[TYPE OF SAMPLER] needs to be set to trace_id_ratio_based
|
|
1240
|
-
// in order to continue
|
|
1241
|
-
if (config.distributed_tracing.sampler[key] !== 'trace_id_ratio_based') {
|
|
1242
|
-
return
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// set the trace id based ratio
|
|
1246
|
-
const envVar = value.trace_id_ratio_based.ratio.env
|
|
1247
|
-
const formatter = value.trace_id_ratio_based.ratio.formatter
|
|
1248
|
-
const setting = process.env[envVar]
|
|
1249
|
-
if (setting) {
|
|
1250
|
-
const formattedSetting = formatter(setting)
|
|
1251
|
-
config.distributed_tracing.sampler[key] = {
|
|
1252
|
-
trace_id_ratio_based: {
|
|
1253
|
-
ratio: formattedSetting
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
logger.trace('Setting %s environment variable', envVar)
|
|
1257
|
-
} else {
|
|
1258
|
-
logger.error('Not setting %s environment variable. Setting sampler root to default.', envVar)
|
|
1259
|
-
config.distributed_tracing.sampler.root = 'default'
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
1170
|
/**
|
|
1265
1171
|
* Recursively visit the nodes of the config definition and look for environment variable names, overriding any configuration values that are found.
|
|
1266
1172
|
*
|
|
@@ -1304,11 +1210,12 @@ Config.prototype._fromEnvironment = function _fromEnvironment(
|
|
|
1304
1210
|
// since sampler was set to a string but now has to be converted to an object.
|
|
1305
1211
|
// This is called here because we need set the sampler to a value first in config before setting
|
|
1306
1212
|
// trace id ratio sampler (if the user set that)
|
|
1307
|
-
|
|
1213
|
+
setTraceIdRatioSamplerFromEnv({
|
|
1308
1214
|
key,
|
|
1309
|
-
value,
|
|
1310
1215
|
config,
|
|
1311
|
-
paths
|
|
1216
|
+
paths,
|
|
1217
|
+
logger,
|
|
1218
|
+
setNestedKey
|
|
1312
1219
|
})
|
|
1313
1220
|
continue
|
|
1314
1221
|
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const { allowList, float } = require('./formatters')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This configuration is currently the same for `distributed_tracing.sampler`,
|
|
11
|
+
* `distributed_tracing.sampler.full_granularity` and `distributed_tracing.sampler.partial_granularity`
|
|
12
|
+
* Note: This also just defines the outer structure. These values are mixed data types. Most of the time
|
|
13
|
+
* it is just a string. But if you want to use the `trace_id_ratio_based` sampler, then you need to
|
|
14
|
+
* provide an object with a `ratio` property. All of that mapping is done in `lib/config/index.js`
|
|
15
|
+
* This is done to allow compatibility with how OpenTelemetry sets their samplers and be backwards compatible as well.
|
|
16
|
+
*/
|
|
17
|
+
const config = {
|
|
18
|
+
/**
|
|
19
|
+
* Example setting root sampler via config to a string value -
|
|
20
|
+
* root: 'always_on'
|
|
21
|
+
*
|
|
22
|
+
* Example setting root sampler via config to trace id ratio based -
|
|
23
|
+
* root: {
|
|
24
|
+
* trace_id_ratio_based: {
|
|
25
|
+
* ratio: 0.5
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
*/
|
|
29
|
+
root: {
|
|
30
|
+
formatter: allowList.bind(null, ['trace_id_ratio_based', 'adaptive', 'always_on', 'always_off', 'default']),
|
|
31
|
+
default: 'default',
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* When set to `always_on`, the sampled flag in the `traceparent` header
|
|
36
|
+
* being set to "true" will result in the local transaction being sampled
|
|
37
|
+
* with a priority value of "2". When set to `always_off`, the local
|
|
38
|
+
* transaction will never be sampled. At the default setting, the sampling
|
|
39
|
+
* decision will be determined according to the normal algorithm.
|
|
40
|
+
*
|
|
41
|
+
* This setting takes precedence over the `remote_parent_not_sampled`
|
|
42
|
+
* setting.
|
|
43
|
+
*/
|
|
44
|
+
remote_parent_sampled: {
|
|
45
|
+
formatter: allowList.bind(null, ['trace_id_ratio_based', 'adaptive', 'always_on', 'always_off', 'default']),
|
|
46
|
+
default: 'default',
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* When set to `always_on`, the local transaction will be sampled with a
|
|
51
|
+
* priority of "2".
|
|
52
|
+
* When set to `always_off`, the local transaction will never be sampled.
|
|
53
|
+
* At the default setting, the sampling decision will be determined
|
|
54
|
+
* according to the normal algorithm.
|
|
55
|
+
*
|
|
56
|
+
* This setting only affects decisions when the traceparent sampled flag
|
|
57
|
+
* is set to 0.
|
|
58
|
+
*/
|
|
59
|
+
remote_parent_not_sampled: {
|
|
60
|
+
formatter: allowList.bind(null, ['trace_id_ratio_based', 'adaptive', 'always_on', 'always_off', 'default']),
|
|
61
|
+
default: 'default',
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Builds the trace_id_ratio_based for distributed tracing sampler configuration structure
|
|
67
|
+
*
|
|
68
|
+
* @param {object} params to function
|
|
69
|
+
* @param {object} params.config - The full configuration object
|
|
70
|
+
* @param {string} params.key - The configuration key being processed
|
|
71
|
+
* @param {object} params.incomingConfig The internal configuration object being modified
|
|
72
|
+
* @param {Logger} params.logger - The logger instance
|
|
73
|
+
*/
|
|
74
|
+
function buildTraceIdRatioSamplers({ config, key, incomingConfig, logger }) {
|
|
75
|
+
if (!isValidDTSampler(key)) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const samplerConfig = config[key]
|
|
80
|
+
const samplers = Object.keys(samplerConfig)
|
|
81
|
+
|
|
82
|
+
// user can set multiple samplers
|
|
83
|
+
for (const sampler of samplers) {
|
|
84
|
+
if (isTraceIdRatioBasedConfig(sampler, samplerConfig)) {
|
|
85
|
+
const ratioValue = samplerConfig[sampler].trace_id_ratio_based.ratio
|
|
86
|
+
if (ratioValue) {
|
|
87
|
+
incomingConfig[key][sampler] = {
|
|
88
|
+
trace_id_ratio_based: {
|
|
89
|
+
ratio: ratioValue
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
logger.trace('Setting trace_id_ratio_based.ratio on %s.%s', key, sampler)
|
|
93
|
+
} else {
|
|
94
|
+
logger.trace('Not setting trace_id_ratio_based on %s.%s as ratio value is not present.', key, sampler)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Checks if the external config contains a trace_id_ratio_based for distributed tracing
|
|
102
|
+
* sampler configuration
|
|
103
|
+
*
|
|
104
|
+
* @param {string} sampler - The selected sampler key
|
|
105
|
+
* @param {object} samplerConfig - The sampler configuration object
|
|
106
|
+
* @returns {boolean} true if it's a trace_id_ratio_based config
|
|
107
|
+
*/
|
|
108
|
+
function isTraceIdRatioBasedConfig(sampler, samplerConfig) {
|
|
109
|
+
return isValidDTSamplerType(sampler) &&
|
|
110
|
+
typeof samplerConfig[sampler] === 'object' &&
|
|
111
|
+
'trace_id_ratio_based' in samplerConfig[sampler]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check if the sampler is a valid value of either 'sampler', 'full_granularity', or 'partial_granularity'
|
|
116
|
+
* @param {string} sampler - The sampler key to validate
|
|
117
|
+
* @returns {boolean} true if valid, false otherwise
|
|
118
|
+
*/
|
|
119
|
+
function isValidDTSampler(sampler) {
|
|
120
|
+
return ['sampler', 'full_granularity', 'partial_granularity'].includes(sampler)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Check if the sampler type is a valid value of either 'root', 'remote_parent_sampled', or 'remote_parent_not_sampled'.
|
|
125
|
+
* @param {string} samplerType - The sampler type to validate
|
|
126
|
+
* @returns {boolean} true if valid, false otherwise
|
|
127
|
+
*/
|
|
128
|
+
function isValidDTSamplerType(samplerType) {
|
|
129
|
+
return ['root', 'remote_parent_sampled', 'remote_parent_not_sampled'].includes(samplerType)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Assigns the value of the distributed tracing samplers env var as an trace_id_ratio_based
|
|
134
|
+
* object to the sampler in the config.
|
|
135
|
+
*
|
|
136
|
+
* @param {object} params object passed to fn
|
|
137
|
+
* @param {string} params.key key of the sampler
|
|
138
|
+
* Example: 'root' or 'remote_parent_sampled'
|
|
139
|
+
* @param {object} params.config agent config
|
|
140
|
+
* @param {Array} params.paths list of leaf nodes leading to the sampling configuration value
|
|
141
|
+
* Example: ['distributed_tracing', 'sampler']
|
|
142
|
+
* @param {Function} params.setNestedKey function to set nested key in config
|
|
143
|
+
* @param {Logger} params.logger logger instance
|
|
144
|
+
*/
|
|
145
|
+
function setTraceIdRatioSamplerFromEnv({ key, config, paths, setNestedKey, logger }) {
|
|
146
|
+
// trace id ratio based sampler is nested in leaf nodes under distributed_tracing > samplers > key
|
|
147
|
+
// so don't continue if path is empty
|
|
148
|
+
if (paths.length === 0) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const lastPath = paths[paths.length - 1]
|
|
153
|
+
if (isValidDTSampler(lastPath) && isValidDTSamplerType(key)) {
|
|
154
|
+
// Get the value of `config.distributed_tracing.sampler[key]` or `config.distributed_tracing.sampler.full_granularity[key]` or `config.distributed_tracing.sampler.partial_granularity[key]`
|
|
155
|
+
const nestedValue = getNestedValue(config, [...paths, key])
|
|
156
|
+
if (nestedValue !== 'trace_id_ratio_based') {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// set the trace id based ratio
|
|
161
|
+
const envVar = `NEW_RELIC_${[...paths, key, 'trace_id_ratio_based', 'ratio'].join('_').toUpperCase()}`
|
|
162
|
+
const setting = process.env[envVar]
|
|
163
|
+
if (setting) {
|
|
164
|
+
const formattedSetting = float(setting)
|
|
165
|
+
setNestedKey(config, [...paths, key], { trace_id_ratio_based: { ratio: formattedSetting } })
|
|
166
|
+
logger.trace('Setting %s environment variable to %s', envVar, formattedSetting)
|
|
167
|
+
} else {
|
|
168
|
+
logger.trace('Not setting %s environment variable. Setting %s.%s to `default`', envVar, paths.join('.'), key)
|
|
169
|
+
setNestedKey(config, [...paths, key], 'default')
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Retrieves a value from a nested object by providing the list of parent keys.
|
|
175
|
+
* @param {object} obj object to assign value to
|
|
176
|
+
* @param {Array} keys list of parent keys
|
|
177
|
+
* @returns {*} value of the nested object key
|
|
178
|
+
*/
|
|
179
|
+
function getNestedValue(obj, keys) {
|
|
180
|
+
const len = keys.length
|
|
181
|
+
for (let i = 0; i < len - 1; i++) {
|
|
182
|
+
const elem = keys[i]
|
|
183
|
+
if (!obj[elem]) {
|
|
184
|
+
obj[elem] = {}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
obj = obj[elem]
|
|
188
|
+
}
|
|
189
|
+
return obj[keys[len - 1]]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
config,
|
|
194
|
+
buildTraceIdRatioSamplers,
|
|
195
|
+
setTraceIdRatioSamplerFromEnv
|
|
196
|
+
}
|
|
@@ -14,32 +14,83 @@ class ExpressUseSubscriber extends ExpressSubscriber {
|
|
|
14
14
|
|
|
15
15
|
handler(data) {
|
|
16
16
|
const { arguments: args } = data
|
|
17
|
-
|
|
18
|
-
let fnIndex = 1
|
|
19
|
-
let method = null
|
|
20
|
-
if (typeof route === 'function') {
|
|
21
|
-
fn = route
|
|
22
|
-
route = null
|
|
23
|
-
fnIndex = 0
|
|
24
|
-
}
|
|
25
|
-
let segmentName = null
|
|
17
|
+
const route = this.routeParser(args[0])
|
|
26
18
|
// sometimes route is undefined, default to `/` for segmentName only
|
|
27
19
|
const routeName = route || '/'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
this.wrapAllMiddleware({
|
|
21
|
+
middlewares: args,
|
|
22
|
+
route,
|
|
23
|
+
routeName
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Iterates over all arguments passed to `.use` and wraps any middleware functions
|
|
29
|
+
* They can be:
|
|
30
|
+
* 1. single middleware
|
|
31
|
+
* 2. series of middleware
|
|
32
|
+
* 3. array of middleware
|
|
33
|
+
* 4. combination of any mentioned above
|
|
34
|
+
*
|
|
35
|
+
* @param {object} params to function
|
|
36
|
+
* @param {Array} params.middlewares array of middleware to wrap
|
|
37
|
+
* @param {string|null} params.route route defined in `.use`
|
|
38
|
+
* @param {string} params.routeName route name for segment naming
|
|
39
|
+
*/
|
|
40
|
+
wrapAllMiddleware({ middlewares, route, routeName }) {
|
|
41
|
+
for (let i = 0; i < middlewares.length; i++) {
|
|
42
|
+
const middleware = middlewares[i]
|
|
43
|
+
if (Array.isArray(middleware)) {
|
|
44
|
+
this.wrapAllMiddleware({ middlewares: middleware, route })
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// we only want to wrap functions
|
|
49
|
+
// This is the route definition not middleware
|
|
50
|
+
if (typeof middleware !== 'function') {
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let segmentName = null
|
|
55
|
+
let method = null
|
|
56
|
+
// Pre v5 these were marked as `lazyrouter`
|
|
57
|
+
// check for both
|
|
58
|
+
if (middleware?.lazyrouter || middleware?.name === 'mounted_app') {
|
|
59
|
+
segmentName = `${this.wrapper.system}/Mounted App: ${routeName}`
|
|
60
|
+
} else if (middleware?.stack) {
|
|
61
|
+
segmentName = `${this.wrapper.system}/Router: ${routeName}`
|
|
62
|
+
method = 'handle'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const wrappedMw = this.wrapper.wrap({
|
|
66
|
+
handler: middleware[method] ?? middleware,
|
|
67
|
+
route,
|
|
68
|
+
segmentName
|
|
69
|
+
})
|
|
70
|
+
if (method) {
|
|
71
|
+
middlewares[i][method] = wrappedMw
|
|
72
|
+
} else {
|
|
73
|
+
middlewares[i] = wrappedMw
|
|
74
|
+
}
|
|
35
75
|
}
|
|
76
|
+
}
|
|
36
77
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Handles extracting the route from a `.use` method.
|
|
80
|
+
* If a route is not defined it defaults to `null`
|
|
81
|
+
*
|
|
82
|
+
* @param {*} route first arg passed to `.use`
|
|
83
|
+
* @returns {*} route or null
|
|
84
|
+
*/
|
|
85
|
+
routeParser(route) {
|
|
86
|
+
if (route instanceof RegExp) {
|
|
87
|
+
return `/${route.source}/`
|
|
88
|
+
} else if (typeof route === 'string') {
|
|
89
|
+
return route
|
|
90
|
+
} else if (Array.isArray(route) && typeof route[0] !== 'function') {
|
|
91
|
+
return route.join(',')
|
|
42
92
|
}
|
|
93
|
+
return null
|
|
43
94
|
}
|
|
44
95
|
}
|
|
45
96
|
|