@depup/elastic-apm-node 4.15.0-depup.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.
Files changed (154) hide show
  1. package/LICENSE +26 -0
  2. package/NOTICE.md +442 -0
  3. package/README.md +48 -0
  4. package/changes.json +78 -0
  5. package/index.d.ts +398 -0
  6. package/index.js +11 -0
  7. package/lib/InflightEventSet.js +53 -0
  8. package/lib/activation-method.js +119 -0
  9. package/lib/agent.js +941 -0
  10. package/lib/apm-client/apm-client.js +313 -0
  11. package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
  12. package/lib/apm-client/http-apm-client/README.md +485 -0
  13. package/lib/apm-client/http-apm-client/central-config.js +41 -0
  14. package/lib/apm-client/http-apm-client/container-info.js +111 -0
  15. package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
  16. package/lib/apm-client/http-apm-client/index.js +1975 -0
  17. package/lib/apm-client/http-apm-client/logging.js +31 -0
  18. package/lib/apm-client/http-apm-client/ndjson.js +20 -0
  19. package/lib/apm-client/http-apm-client/truncate.js +434 -0
  20. package/lib/apm-client/noop-apm-client.js +73 -0
  21. package/lib/async-hooks-polyfill.js +58 -0
  22. package/lib/cloud-metadata/aws.js +175 -0
  23. package/lib/cloud-metadata/azure.js +123 -0
  24. package/lib/cloud-metadata/callback-coordination.js +159 -0
  25. package/lib/cloud-metadata/gcp.js +133 -0
  26. package/lib/cloud-metadata/index.js +175 -0
  27. package/lib/config/config.js +458 -0
  28. package/lib/config/normalizers.js +701 -0
  29. package/lib/config/schema.js +1007 -0
  30. package/lib/constants.js +35 -0
  31. package/lib/errors.js +303 -0
  32. package/lib/filters/sanitize-field-names.js +69 -0
  33. package/lib/http-request.js +249 -0
  34. package/lib/instrumentation/azure-functions.js +519 -0
  35. package/lib/instrumentation/context.js +56 -0
  36. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  37. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  38. package/lib/instrumentation/express-utils.js +91 -0
  39. package/lib/instrumentation/generic-span.js +322 -0
  40. package/lib/instrumentation/http-shared.js +424 -0
  41. package/lib/instrumentation/ids.js +39 -0
  42. package/lib/instrumentation/index.js +1127 -0
  43. package/lib/instrumentation/modules/@apollo/server.js +30 -0
  44. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  45. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  46. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  47. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  48. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  49. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  50. package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
  51. package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
  52. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  53. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  54. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  55. package/lib/instrumentation/modules/_lambda-handler.js +40 -0
  56. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  57. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  58. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  59. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  60. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  61. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  62. package/lib/instrumentation/modules/bluebird.js +93 -0
  63. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  64. package/lib/instrumentation/modules/elasticsearch.js +191 -0
  65. package/lib/instrumentation/modules/express-graphql.js +66 -0
  66. package/lib/instrumentation/modules/express-queue.js +28 -0
  67. package/lib/instrumentation/modules/express.js +162 -0
  68. package/lib/instrumentation/modules/fastify.js +172 -0
  69. package/lib/instrumentation/modules/finalhandler.js +41 -0
  70. package/lib/instrumentation/modules/generic-pool.js +85 -0
  71. package/lib/instrumentation/modules/graphql.js +256 -0
  72. package/lib/instrumentation/modules/handlebars.js +22 -0
  73. package/lib/instrumentation/modules/http.js +112 -0
  74. package/lib/instrumentation/modules/http2.js +320 -0
  75. package/lib/instrumentation/modules/https.js +68 -0
  76. package/lib/instrumentation/modules/ioredis.js +94 -0
  77. package/lib/instrumentation/modules/jade.js +18 -0
  78. package/lib/instrumentation/modules/kafkajs.js +476 -0
  79. package/lib/instrumentation/modules/knex.js +91 -0
  80. package/lib/instrumentation/modules/koa-router.js +74 -0
  81. package/lib/instrumentation/modules/koa.js +15 -0
  82. package/lib/instrumentation/modules/memcached.js +99 -0
  83. package/lib/instrumentation/modules/mimic-response.js +45 -0
  84. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  85. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  86. package/lib/instrumentation/modules/mongodb.js +259 -0
  87. package/lib/instrumentation/modules/mysql.js +200 -0
  88. package/lib/instrumentation/modules/mysql2.js +140 -0
  89. package/lib/instrumentation/modules/pg.js +148 -0
  90. package/lib/instrumentation/modules/pug.js +18 -0
  91. package/lib/instrumentation/modules/redis.js +176 -0
  92. package/lib/instrumentation/modules/restify.js +52 -0
  93. package/lib/instrumentation/modules/tedious.js +159 -0
  94. package/lib/instrumentation/modules/undici.js +270 -0
  95. package/lib/instrumentation/modules/ws.js +59 -0
  96. package/lib/instrumentation/noop-transaction.js +81 -0
  97. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  98. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  99. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  100. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  101. package/lib/instrumentation/run-context/RunContext.js +151 -0
  102. package/lib/instrumentation/run-context/index.js +23 -0
  103. package/lib/instrumentation/shimmer.js +123 -0
  104. package/lib/instrumentation/span-compression.js +239 -0
  105. package/lib/instrumentation/span.js +621 -0
  106. package/lib/instrumentation/template-shared.js +43 -0
  107. package/lib/instrumentation/timer.js +84 -0
  108. package/lib/instrumentation/transaction.js +571 -0
  109. package/lib/lambda.js +992 -0
  110. package/lib/load-source-map.js +100 -0
  111. package/lib/logging.js +212 -0
  112. package/lib/metrics/index.js +92 -0
  113. package/lib/metrics/platforms/generic/index.js +40 -0
  114. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  115. package/lib/metrics/platforms/generic/process-top.js +157 -0
  116. package/lib/metrics/platforms/generic/stats.js +34 -0
  117. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  118. package/lib/metrics/platforms/linux/index.js +19 -0
  119. package/lib/metrics/platforms/linux/stats.js +213 -0
  120. package/lib/metrics/queue.js +90 -0
  121. package/lib/metrics/registry.js +52 -0
  122. package/lib/metrics/reporter.js +119 -0
  123. package/lib/metrics/runtime.js +77 -0
  124. package/lib/middleware/connect.js +16 -0
  125. package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
  126. package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
  127. package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
  128. package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
  129. package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
  130. package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
  131. package/lib/opentelemetry-bridge/README.md +244 -0
  132. package/lib/opentelemetry-bridge/index.js +15 -0
  133. package/lib/opentelemetry-bridge/oblog.js +23 -0
  134. package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
  135. package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
  136. package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
  137. package/lib/opentelemetry-bridge/otelutils.js +99 -0
  138. package/lib/opentelemetry-bridge/setup.js +76 -0
  139. package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
  140. package/lib/opentelemetry-metrics/index.js +50 -0
  141. package/lib/parsers.js +225 -0
  142. package/lib/propwrap.js +147 -0
  143. package/lib/stacktraces.js +537 -0
  144. package/lib/symbols.js +15 -0
  145. package/lib/tracecontext/index.js +118 -0
  146. package/lib/tracecontext/traceparent.js +185 -0
  147. package/lib/tracecontext/tracestate.js +388 -0
  148. package/lib/wildcard-matcher.js +52 -0
  149. package/loader.mjs +7 -0
  150. package/package.json +299 -0
  151. package/start.d.ts +8 -0
  152. package/start.js +29 -0
  153. package/types/aws-lambda.d.ts +98 -0
  154. package/types/connect.d.ts +23 -0
@@ -0,0 +1,701 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { URL } = require('url');
10
+
11
+ const { WildcardMatcher } = require('../wildcard-matcher');
12
+ const {
13
+ TRACE_CONTINUATION_STRATEGY_CONTINUE,
14
+ TRACE_CONTINUATION_STRATEGY_RESTART,
15
+ TRACE_CONTINUATION_STRATEGY_RESTART_EXTERNAL,
16
+ CONTEXT_MANAGER_ASYNCHOOKS,
17
+ CONTEXT_MANAGER_ASYNCLOCALSTORAGE,
18
+ } = require('../constants');
19
+
20
+ /**
21
+ * Normalizes the key/value pairs properties of the config options object
22
+ * KeyValuePairs config vars are either an object or a comma-separated string
23
+ * of key=value pairs (whitespace around the "key=value" strings is trimmed):
24
+ * {'foo': 'bar', 'eggs': 'spam'} => [['foo', 'bar'], ['eggs', 'spam']]
25
+ * foo=bar, eggs=spam => [['foo', 'bar'], ['eggs', 'spam']]
26
+ *
27
+ * @param {Record<String, unknown>} opts the configuration options to normalize
28
+ * @param {String[]} fields the list of fields to normalize as key/value pair
29
+ * @param {Record<String, unknown>} defaults the configuration defaults
30
+ * @param {import('../logging.js').Logger} logger
31
+ */
32
+ function normalizeKeyValuePairs(opts, fields, defaults, logger) {
33
+ for (const key of fields) {
34
+ if (key in opts) {
35
+ if (typeof opts[key] === 'object' && !Array.isArray(opts[key])) {
36
+ opts[key] = Object.entries(opts[key]);
37
+ continue;
38
+ }
39
+
40
+ if (!Array.isArray(opts[key]) && typeof opts[key] === 'string') {
41
+ opts[key] = opts[key].split(',').map((v) => v.trim());
42
+ }
43
+
44
+ if (Array.isArray(opts[key])) {
45
+ // Note: Currently this assumes no '=' in the value. Also this does not
46
+ // trim whitespace.
47
+ opts[key] = opts[key].map((value) =>
48
+ typeof value === 'string' ? value.split('=') : value,
49
+ );
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Normalizes the number properties of the config options object
57
+ *
58
+ * @param {Record<String, unknown>} opts the configuration options to normalize
59
+ * @param {String[]} fields the list of fields to normalize as number
60
+ * @param {Record<String, unknown>} defaults the configuration defaults
61
+ * @param {import('../logging.js').Logger} logger
62
+ */
63
+ function normalizeNumbers(opts, fields, defaults, logger) {
64
+ for (const key of fields) {
65
+ if (key in opts) opts[key] = Number(opts[key]);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Normalizes the number properties of the config options object
71
+ *
72
+ * @param {Record<String, unknown>} opts the configuration options to normalize
73
+ * @param {String[]} fields the list of fields to normalize as number
74
+ * @param {Record<String, unknown>} defaults the configuration defaults
75
+ * @param {import('../logging.js').Logger} logger
76
+ */
77
+ function normalizeInfinity(opts, fields, defaults, logger) {
78
+ for (const key of fields) {
79
+ if (opts[key] === -1) opts[key] = Infinity;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Translates a string byte size, e.g. '10kb', into an integer number of bytes.
85
+ *
86
+ * @param {String} input
87
+ * @param {string} key - the config option key
88
+ * @param {import('../logging.js').Logger} logger
89
+ * @returns {Number|undefined}
90
+ */
91
+ function bytes(input, key, logger) {
92
+ const matches = input.match(/^(\d+)(b|kb|mb|gb)$/i);
93
+ if (!matches) {
94
+ logger.warn(
95
+ 'units missing in size value "%s" for "%s" config option. Use one of b|kb|mb|gb',
96
+ input,
97
+ key,
98
+ );
99
+ return Number(input);
100
+ }
101
+
102
+ const suffix = matches[2].toLowerCase();
103
+ let value = Number(matches[1]);
104
+
105
+ if (!suffix || suffix === 'b') {
106
+ return value;
107
+ }
108
+
109
+ value *= 1024;
110
+ if (suffix === 'kb') {
111
+ return value;
112
+ }
113
+
114
+ value *= 1024;
115
+ if (suffix === 'mb') {
116
+ return value;
117
+ }
118
+
119
+ value *= 1024;
120
+ if (suffix === 'gb') {
121
+ return value;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Normalizes the byte properties of the config options object
127
+ *
128
+ * @param {Record<String, unknown>} opts the configuration options to normalize
129
+ * @param {String[]} fields the list of fields to normalize as bytes
130
+ * @param {Record<String, unknown>} defaults the configuration defaults
131
+ * @param {import('../logging.js').Logger} logger
132
+ */
133
+ function normalizeBytes(opts, fields, defaults, logger) {
134
+ for (const key of fields) {
135
+ if (key in opts) {
136
+ opts[key] = bytes(String(opts[key]), key, logger);
137
+ if (key === 'errorMessageMaxLength') {
138
+ logger.warn(
139
+ 'config option "errorMessageMaxLength" is deprecated. Use "longFieldMaxLength"',
140
+ );
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Convert a given duration config option into a number of seconds.
148
+ * If the given duration is invalid, this returns `null`.
149
+ * Units are *case-sensitive*.
150
+ *
151
+ * Examples:
152
+ * secondsFromDuration('30s', 's', ['ms', 's', 'm'], false) // => 30
153
+ * secondsFromDuration('-1s', 's', ['ms', 's', 'm'], false) // => null
154
+ * secondsFromDuration('-1ms', 's', ['ms', 's', 'm'], true) // => -0.001
155
+ * secondsFromDuration(500, 'ms', ['us', 'ms', 's', 'm'], false) // => 0.5
156
+ *
157
+ * @param {String|Number} duration - Typically a string of the form `<num><unit>`,
158
+ * for example `30s`, `-1ms`, `2m`. The `defaultUnit` is used if a unit is
159
+ * not part of the string, or if duration is a number. If given as a string,
160
+ * decimal ('1.5s') and exponential-notation ('1e-3s') values are not allowed.
161
+ * @param {String} defaultUnit
162
+ * @param {Array<String>} allowedUnits - An array of the allowed unit strings. This
163
+ * array may include any number of `us`, `ms`, `s`, and `m`.
164
+ * @param {Boolean} allowNegative - Whether a negative number is allowed.
165
+ * @param {string} key - the config option key
166
+ * @param {Logger} logger
167
+ * @returns {number|null}
168
+ */
169
+ function secondsFromDuration(
170
+ duration,
171
+ defaultUnit,
172
+ allowedUnits,
173
+ allowNegative,
174
+ key,
175
+ logger,
176
+ ) {
177
+ let val;
178
+ let unit;
179
+ if (typeof duration === 'string') {
180
+ let match;
181
+ if (allowNegative) {
182
+ match = /^(-?\d+)(\w+)?$/.exec(duration);
183
+ } else {
184
+ match = /^(\d+)(\w+)?$/.exec(duration);
185
+ }
186
+ if (!match) {
187
+ return null;
188
+ }
189
+ val = Number(match[1]);
190
+ if (isNaN(val) || !Number.isFinite(val)) {
191
+ return null;
192
+ }
193
+ unit = match[2];
194
+ if (!unit) {
195
+ logger.warn(
196
+ 'units missing in duration value "%s" for "%s" config option: using default units "%s"',
197
+ duration,
198
+ key,
199
+ defaultUnit,
200
+ );
201
+ unit = defaultUnit;
202
+ }
203
+ if (!allowedUnits.includes(unit)) {
204
+ return null;
205
+ }
206
+ } else if (typeof duration === 'number') {
207
+ if (isNaN(duration)) {
208
+ return null;
209
+ } else if (duration < 0 && !allowNegative) {
210
+ return null;
211
+ }
212
+ val = duration;
213
+ unit = defaultUnit;
214
+ } else {
215
+ return null;
216
+ }
217
+
218
+ // Scale to seconds.
219
+ switch (unit) {
220
+ case 'us':
221
+ val /= 1e6;
222
+ break;
223
+ case 'ms':
224
+ val /= 1e3;
225
+ break;
226
+ case 's':
227
+ break;
228
+ case 'm':
229
+ val *= 60;
230
+ break;
231
+ default:
232
+ throw new Error(`unknown unit "${unit}" from "${duration}"`);
233
+ }
234
+
235
+ return val;
236
+ }
237
+
238
+ /**
239
+ * Normalizes the duration properties of the config options object
240
+ *
241
+ * @param {Record<String, unknown>} opts the configuration options to normalize
242
+ * @param {Array<Object>} fields the list of fields to normalize as duration (with name, defaultUnit, allowedUnits, allowNegative)
243
+ * @param {Record<String, unknown>} defaults the configuration defaults
244
+ * @param {import('../logging.js').Logger} logger
245
+ */
246
+ function normalizeDurationOptions(opts, fields, defaults, logger) {
247
+ for (const optSpec of fields) {
248
+ const key = optSpec.name;
249
+ if (key in opts) {
250
+ const val = secondsFromDuration(
251
+ opts[key],
252
+ optSpec.defaultUnit,
253
+ optSpec.allowedUnits,
254
+ optSpec.allowNegative,
255
+ key,
256
+ logger,
257
+ );
258
+ if (val === null) {
259
+ if (key in defaults) {
260
+ const def = defaults[key];
261
+ logger.warn(
262
+ 'invalid duration value "%s" for "%s" config option: using default "%s"',
263
+ opts[key],
264
+ key,
265
+ def,
266
+ );
267
+ opts[key] = secondsFromDuration(
268
+ def,
269
+ optSpec.defaultUnit,
270
+ optSpec.allowedUnits,
271
+ optSpec.allowNegative,
272
+ key,
273
+ logger,
274
+ );
275
+ } else {
276
+ logger.warn(
277
+ 'invalid duration value "%s" for "%s" config option: ignoring this option',
278
+ opts[key],
279
+ key,
280
+ );
281
+ delete opts[key];
282
+ }
283
+ } else {
284
+ opts[key] = val;
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Normalizes the array properties of the config options object
292
+ * Array config vars are either already an array of strings, or a
293
+ * comma-separated string (whitespace is trimmed):
294
+ * 'foo, bar' => ['foo', 'bar']
295
+ *
296
+ * @param {Record<String, unknown>} opts the configuration options to normalize
297
+ * @param {String[]} fields the list of fields to normalize as arrays
298
+ * @param {Record<String, unknown>} defaults the configuration defaults
299
+ * @param {import('../logging.js').Logger} logger
300
+ */
301
+ function normalizeArrays(opts, fields, defaults, logger) {
302
+ for (const key of fields) {
303
+ if (key in opts && typeof opts[key] === 'string') {
304
+ opts[key] = opts[key].split(',').map((v) => v.trim());
305
+ }
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Parses "true"|"false" to boolean if not a boolean already and returns it. Returns undefined otherwise
311
+ *
312
+ * @param {import('../logging.js').Logger} logger
313
+ * @param {String} key
314
+ * @param {any} value
315
+ * @returns {Boolean|undefined}
316
+ */
317
+ function strictBool(logger, key, value) {
318
+ if (typeof value === 'boolean') {
319
+ return value;
320
+ }
321
+ // This will return undefined for unknown inputs, resulting in them being skipped.
322
+ switch (value) {
323
+ case 'false':
324
+ return false;
325
+ case 'true':
326
+ return true;
327
+ default: {
328
+ logger.warn('unrecognized boolean value "%s" for "%s"', value, key);
329
+ }
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Normalizes the boolean properties of the config options object
335
+ * Boolean config vars are either already a boolean, or a string
336
+ * representation of the boolean value: `true` or `false`
337
+ *
338
+ * @param {Record<String, unknown>} opts the configuration options to normalize
339
+ * @param {String[]} fields the list of fields to normalize as boolean
340
+ * @param {Record<String, unknown>} defaults the configuration defaults
341
+ * @param {import('../logging.js').Logger} logger
342
+ */
343
+ function normalizeBools(opts, fields, defaults, logger) {
344
+ for (const key of fields) {
345
+ if (key in opts) opts[key] = strictBool(logger, key, opts[key]);
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Checks validity of the URL properties of the config options object.
351
+ *
352
+ * @param {Record<String, unknown>} opts the configuration options to normalize
353
+ * @param {String[]} fields the list of fields to normalize as boolean
354
+ * @param {Record<String, unknown>} defaults the configuration defaults
355
+ * @param {import('../logging.js').Logger} logger
356
+ */
357
+ function normalizeUrls(opts, fields, defaults, logger) {
358
+ for (const key of fields) {
359
+ if (key in opts) {
360
+ try {
361
+ // eslint-disable-next-line no-unused-vars
362
+ const url = new URL(opts[key]);
363
+ // TODO: consider making the port explicit in the URL
364
+ // sourceValue http://foo.com => normalized http://foo.com:80
365
+ // sourceValue https://foo.com => normalized https://foo.com:443
366
+ } catch (err) {
367
+ logger.warn('Invalid "%s" config value, it must be a valid URL', key);
368
+ opts[key] = null;
369
+ }
370
+ }
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Normalizes the ignore options and places them in specific properties for string and RegExp values
376
+ *
377
+ * Ignore config vars are either an array of wildcard expressions or an array of strings and RegExps:
378
+ * of key=value pairs (whitespace around the "key=value" strings is trimmed):
379
+ * ['*foo', 'bar*'] => [ /^.*foo/, /^bar.*$/ ] (result goes to another property)
380
+ * ['foo', /url/pathname$/] => ['foo'] (strings are placed into a specific config option)
381
+ * => [/url/pathname$/] (RegExps are placed into a specific config option)
382
+ *
383
+ * @param {Record<String, unknown>} opts the configuration options to normalize
384
+ * @param {String[]} fields the list of fields to normalize as boolean
385
+ * @param {Record<String, unknown>} defaults the configuration defaults
386
+ * @param {import('../logging.js').Logger} logger
387
+ */
388
+ function normalizeIgnoreOptions(opts, fields, defaults, logger) {
389
+ // Params are meant to be used in upcoming changes
390
+ if (opts.transactionIgnoreUrls) {
391
+ opts.transactionIgnoreUrlRegExp = [];
392
+ const wildcard = new WildcardMatcher();
393
+ for (const ptn of opts.transactionIgnoreUrls) {
394
+ const re = wildcard.compile(ptn);
395
+ opts.transactionIgnoreUrlRegExp.push(re);
396
+ }
397
+ }
398
+
399
+ if (opts.ignoreUrls) {
400
+ opts.ignoreUrlStr = [];
401
+ opts.ignoreUrlRegExp = [];
402
+ for (const ptn of opts.ignoreUrls) {
403
+ if (typeof ptn === 'string') {
404
+ opts.ignoreUrlStr.push(ptn);
405
+ } else {
406
+ opts.ignoreUrlRegExp.push(ptn);
407
+ }
408
+ }
409
+ delete opts.ignoreUrls;
410
+ }
411
+
412
+ if (opts.ignoreUserAgents) {
413
+ opts.ignoreUserAgentStr = [];
414
+ opts.ignoreUserAgentRegExp = [];
415
+ for (const ptn of opts.ignoreUserAgents) {
416
+ if (typeof ptn === 'string') {
417
+ opts.ignoreUserAgentStr.push(ptn);
418
+ } else {
419
+ opts.ignoreUserAgentRegExp.push(ptn);
420
+ }
421
+ }
422
+ delete opts.ignoreUserAgents;
423
+ }
424
+
425
+ if (opts.ignoreMessageQueues) {
426
+ opts.ignoreMessageQueuesRegExp = [];
427
+ const wildcard = new WildcardMatcher();
428
+ for (const ptn of opts.ignoreMessageQueues) {
429
+ const re = wildcard.compile(ptn);
430
+ opts.ignoreMessageQueuesRegExp.push(re);
431
+ }
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Normalizes the wildcard matchers of sanitizeFieldNames and thansforms the into RegExps
437
+ *
438
+ * TODO: we are doing the same to some ignoreOptions
439
+ * @param {Record<String, unknown>} opts the configuration options to normalize
440
+ */
441
+ function normalizeSanitizeFieldNames(opts) {
442
+ if (opts.sanitizeFieldNames) {
443
+ opts.sanitizeFieldNamesRegExp = [];
444
+ const wildcard = new WildcardMatcher();
445
+ for (const ptn of opts.sanitizeFieldNames) {
446
+ const re = wildcard.compile(ptn);
447
+ opts.sanitizeFieldNamesRegExp.push(re);
448
+ }
449
+ }
450
+ }
451
+
452
+ // TODO: this is the same as normalizeSanitizeFieldNames
453
+ // maybe create a normalizeWildcardOptions???
454
+ function normalizeDisableMetrics(opts) {
455
+ if (opts.disableMetrics) {
456
+ opts.disableMetricsRegExp = []; // This line was not in the original code but raised an exception in the tests
457
+ const wildcard = new WildcardMatcher();
458
+ for (const ptn of opts.disableMetrics) {
459
+ const re = wildcard.compile(ptn);
460
+ opts.disableMetricsRegExp.push(re);
461
+ }
462
+ }
463
+ }
464
+
465
+ // TODO: same as above
466
+ function normalizeElasticsearchCaptureBodyUrls(opts) {
467
+ if (opts.elasticsearchCaptureBodyUrls) {
468
+ opts.elasticsearchCaptureBodyUrlsRegExp = [];
469
+ const wildcard = new WildcardMatcher();
470
+ for (const ptn of opts.elasticsearchCaptureBodyUrls) {
471
+ const re = wildcard.compile(ptn);
472
+ opts.elasticsearchCaptureBodyUrlsRegExp.push(re);
473
+ }
474
+ }
475
+ }
476
+
477
+ /**
478
+ * Makes sure the cloudProvider options is valid othherwise it set the default value.
479
+ *
480
+ * @param {Record<String, unknown>} opts the configuration options to normalize
481
+ * @param {String[]} fields the list of fields to normalize as duration
482
+ * @param {Record<String, unknown>} defaults the configuration defaults
483
+ * @param {import('../logging.js').Logger} logger
484
+ */
485
+ function normalizeCloudProvider(opts, fields, defaults, logger) {
486
+ if ('cloudProvider' in opts) {
487
+ const allowedValues = ['auto', 'gcp', 'azure', 'aws', 'none'];
488
+ if (allowedValues.indexOf(opts.cloudProvider) === -1) {
489
+ logger.warn(
490
+ 'Invalid "cloudProvider" config value %s, falling back to default %s',
491
+ opts.cloudProvider,
492
+ defaults.cloudProvider,
493
+ );
494
+ opts.cloudProvider = defaults.cloudProvider;
495
+ }
496
+ }
497
+ }
498
+
499
+ // `customMetricsHistogramBoundaries` must be a sorted array of numbers,
500
+ // without duplicates.
501
+ function normalizeCustomMetricsHistogramBoundaries(
502
+ opts,
503
+ fields,
504
+ defaults,
505
+ logger,
506
+ ) {
507
+ if (!('customMetricsHistogramBoundaries' in opts)) {
508
+ return;
509
+ }
510
+ let val = opts.customMetricsHistogramBoundaries;
511
+ if (typeof val === 'string') {
512
+ val = val.split(',').map((v) => Number(v.trim()));
513
+ }
514
+ let errReason = null;
515
+ if (!Array.isArray(val)) {
516
+ errReason = 'value is not an array';
517
+ } else if (val.some((el) => typeof el !== 'number' || isNaN(el))) {
518
+ errReason = 'array includes non-numbers';
519
+ } else {
520
+ for (let i = 0; i < val.length - 1; i++) {
521
+ if (val[i] === val[i + 1]) {
522
+ errReason = 'array has duplicate values';
523
+ break;
524
+ } else if (val[i] > val[i + 1]) {
525
+ errReason = 'array is not sorted';
526
+ break;
527
+ }
528
+ }
529
+ }
530
+ if (errReason) {
531
+ logger.warn(
532
+ 'Invalid "customMetricsHistogramBoundaries" config value %j, %s; falling back to default',
533
+ opts.customMetricsHistogramBoundaries,
534
+ errReason,
535
+ );
536
+ opts.customMetricsHistogramBoundaries =
537
+ defaults.customMetricsHistogramBoundaries;
538
+ } else {
539
+ opts.customMetricsHistogramBoundaries = val;
540
+ }
541
+ }
542
+
543
+ // transactionSampleRate is specified to be:
544
+ // - in the range [0,1]
545
+ // - rounded to 4 decimal places of precision (e.g. 0.0001, 0.5678, 0.9999)
546
+ // - with the special case that a value in the range (0, 0.0001] should be
547
+ // rounded to 0.0001 -- to avoid a small value being rounded to zero.
548
+ //
549
+ // https://github.com/elastic/apm/blob/main/specs/agents/tracing-sampling.md
550
+ function normalizeTransactionSampleRate(opts, fields, defaults, logger) {
551
+ if ('transactionSampleRate' in opts) {
552
+ // The value was already run through `Number(...)` in `normalizeNumbers`.
553
+ const rate = opts.transactionSampleRate;
554
+ if (isNaN(rate) || rate < 0 || rate > 1) {
555
+ opts.transactionSampleRate = defaults.transactionSampleRate;
556
+ logger.warn(
557
+ 'Invalid "transactionSampleRate" config value %s, falling back to default %s',
558
+ rate,
559
+ opts.transactionSampleRate,
560
+ );
561
+ } else if (rate > 0 && rate < 0.0001) {
562
+ opts.transactionSampleRate = 0.0001;
563
+ } else {
564
+ opts.transactionSampleRate = Math.round(rate * 10000) / 10000;
565
+ }
566
+ }
567
+ }
568
+
569
+ const ALLOWED_TRACE_CONTINUATION_STRATEGY = {
570
+ [TRACE_CONTINUATION_STRATEGY_CONTINUE]: true,
571
+ [TRACE_CONTINUATION_STRATEGY_RESTART]: true,
572
+ [TRACE_CONTINUATION_STRATEGY_RESTART_EXTERNAL]: true,
573
+ };
574
+ function normalizeTraceContinuationStrategy(opts, fields, defaults, logger) {
575
+ if (
576
+ 'traceContinuationStrategy' in opts &&
577
+ !(opts.traceContinuationStrategy in ALLOWED_TRACE_CONTINUATION_STRATEGY)
578
+ ) {
579
+ logger.warn(
580
+ 'Invalid "traceContinuationStrategy" config value %j, falling back to default %j',
581
+ opts.traceContinuationStrategy,
582
+ defaults.traceContinuationStrategy,
583
+ );
584
+ opts.traceContinuationStrategy = defaults.traceContinuationStrategy;
585
+ }
586
+ }
587
+
588
+ const ALLOWED_CONTEXT_MANAGER = {
589
+ [CONTEXT_MANAGER_ASYNCHOOKS]: true,
590
+ [CONTEXT_MANAGER_ASYNCLOCALSTORAGE]: true,
591
+ };
592
+
593
+ /**
594
+ * Normalize and validate the given values for `contextManager`, and the
595
+ * deprecated `asyncHooks` that it replaces.
596
+ *
597
+ * - `contextManager=asynchooks` means use the "async_hooks.createHook()"
598
+ * technique. This works in all supported versions of node, but can have
599
+ * significant performance overhead for Promise-heavy apps.
600
+ * - `contextManager=asynclocalstorage` means use the "AsyncLocalStorage"
601
+ * technique *if supported in the version of node* (>=14.5 || ^12.19.0).
602
+ * Otherwise, this will warn and fallback to "asynchooks".
603
+ * - No specified option means use the best async technique.
604
+ */
605
+ function normalizeContextManager(opts, fields, defaults, logger) {
606
+ // Treat the empty string, e.g. `ELASTIC_APM_CONTEXT_MANAGER=`, as if it had
607
+ // not been specified.
608
+ if (opts.contextManager === '') {
609
+ delete opts.contextManager;
610
+ }
611
+
612
+ if (
613
+ 'contextManager' in opts &&
614
+ !(opts.contextManager in ALLOWED_CONTEXT_MANAGER)
615
+ ) {
616
+ logger.warn(
617
+ 'Invalid "contextManager" config value %j, falling back to default behavior',
618
+ opts.contextManager,
619
+ );
620
+ delete opts.contextManager;
621
+ }
622
+ }
623
+
624
+ // Normalize provided values for `spanFramesMinDuration` (deprecated),
625
+ // `captureSpanStackTraces` (deprecated) and `spanStackTraceMinDuration` into
626
+ // a final value for `spanStackTraceMinDuration` that is used by the agent.
627
+ //
628
+ // This function expects `normalizeDurationOptions()` and `normalizeBools()`
629
+ // to have already been called.
630
+ //
631
+ // | spanStackTraceMinDuration | captureSpanStackTraces | spanFramesMinDuration | resultant spanStackTraceMinDuration |
632
+ // | ------------------------- | ---------------------- | ----------------------- | ----------------------------------- |
633
+ // | - | - | - | `-1ms` (no span stacktraces) |
634
+ // | `-1ms` (negative value) | (any value is ignored) | (any value is ignored) | `-1ms` (no span stacktraces) |
635
+ // | `0ms` (zero value) | (any value is ignored) | (any value is ignored) | `0ms` (stacktraces for all spans) |
636
+ // | `5ms` (positive value) | (any value is ignored) | (any value is ignored) | `5ms` |
637
+ // | - | `false` | (any value) | `-1ms` (no span stacktraces) |
638
+ // | - | `true` | - | `10ms` (backwards compatible value) |
639
+ // | - | `true` or unspecified | `0ms` (zero value) | `-1ms` (no span stacktraces) |
640
+ // | - | `true` or unspecified | `-1ms` (negative value) | `0ms` (stacktraces for all spans) |
641
+ // | - | `true` or unspecified | `5ms` (positive value) | `5ms` |
642
+ function normalizeSpanStackTraceMinDuration(opts, fields, defaults, logger) {
643
+ const before = {};
644
+ if (opts.captureSpanStackTraces !== undefined)
645
+ before.captureSpanStackTraces = opts.captureSpanStackTraces;
646
+ if (opts.spanFramesMinDuration !== undefined)
647
+ before.spanFramesMinDuration = opts.spanFramesMinDuration;
648
+ if (opts.spanStackTraceMinDuration !== undefined)
649
+ before.spanStackTraceMinDuration = opts.spanStackTraceMinDuration;
650
+
651
+ if ('spanStackTraceMinDuration' in opts) {
652
+ // If the new option was specified, then use it and ignore the old two.
653
+ } else if (opts.captureSpanStackTraces === false) {
654
+ opts.spanStackTraceMinDuration = -1; // Turn off span stacktraces.
655
+ } else if ('spanFramesMinDuration' in opts) {
656
+ if (opts.spanFramesMinDuration === 0) {
657
+ opts.spanStackTraceMinDuration = -1; // Turn off span stacktraces.
658
+ } else if (opts.spanFramesMinDuration < 0) {
659
+ opts.spanStackTraceMinDuration = 0; // Stacktraces for all spans.
660
+ } else {
661
+ opts.spanStackTraceMinDuration = opts.spanFramesMinDuration;
662
+ }
663
+ } else if (opts.captureSpanStackTraces === true) {
664
+ // For backwards compat, use the default `spanFramesMinDuration` value
665
+ // from before `spanStackTraceMinDuration` was introduced.
666
+ opts.spanStackTraceMinDuration = 10 / 1e3; // 10ms
667
+ } else {
668
+ // None of the three options was specified.
669
+ opts.spanStackTraceMinDuration = -1; // Turn off span stacktraces.
670
+ }
671
+ delete opts.captureSpanStackTraces;
672
+ delete opts.spanFramesMinDuration;
673
+
674
+ // Log if something potentially interesting happened here.
675
+ if (Object.keys(before).length > 0) {
676
+ const after = { spanStackTraceMinDuration: opts.spanStackTraceMinDuration };
677
+ logger.trace({ before, after }, 'normalizeSpanStackTraceMinDuration');
678
+ }
679
+ }
680
+
681
+ module.exports = {
682
+ normalizeArrays,
683
+ normalizeBools,
684
+ normalizeBytes,
685
+ normalizeCloudProvider,
686
+ normalizeCustomMetricsHistogramBoundaries,
687
+ normalizeDisableMetrics,
688
+ normalizeDurationOptions,
689
+ normalizeElasticsearchCaptureBodyUrls,
690
+ normalizeIgnoreOptions,
691
+ normalizeInfinity,
692
+ normalizeKeyValuePairs,
693
+ normalizeNumbers,
694
+ normalizeSanitizeFieldNames,
695
+ normalizeTransactionSampleRate,
696
+ secondsFromDuration,
697
+ normalizeTraceContinuationStrategy,
698
+ normalizeContextManager,
699
+ normalizeSpanStackTraceMinDuration,
700
+ normalizeUrls,
701
+ };