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