@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,946 @@
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 fs = require('fs');
10
+ const os = require('os');
11
+ const path = require('path');
12
+ const { URL } = require('url');
13
+
14
+ const AGENT_VERSION = require('../../package.json').version;
15
+ const { REDACTED } = require('../constants');
16
+
17
+ /**
18
+ * @typedef {Object} OptionDefinition
19
+ * @property {string} name the name of the configuration option
20
+ * @property {keyof TypeNormalizers} configType the type of the configuration option
21
+ * @property {any} defaultValue the default value of the property or undefined
22
+ * @property {any} [environmentValue] defined if value is provided via environment var
23
+ * @property {any} [startValue] defined if value is provided via `Agent.start()` API
24
+ * @property {any} [fileValue] defined if value is provided via config file
25
+ * @property {'environment' | 'start' | 'file'} [source] defined value comes from any source keeping its priorities (env, star, file, default)
26
+ * @property {string} [envVar] the name of the environment varaiable associated with the option
27
+ * @property {string} [envDeprecatedVar] the name of the deprecated environment varaiable associated with the option
28
+ * @property {string} [centralConfigName] name of option in central configuration
29
+ * @property {string} [crossAgentName] name of the same option in other agents
30
+ * @property {(v: any) => any} [redactFn] if passed the value will be redacted with it on serialization (preamble...)
31
+ */
32
+
33
+ // TODO: options with `crossAgentName` property
34
+ // check for more cross agent config option names in
35
+ // https://docs.google.com/spreadsheets/d/1JJjZotapacA3FkHc2sv_0wiChILi3uKnkwLTjtBmxwU/edit#gid=0
36
+
37
+ /**
38
+ * @type Array<OptionDefinition>
39
+ */
40
+ const CONFIG_SCHEMA = [
41
+ {
42
+ name: 'abortedErrorThreshold',
43
+ configType: 'durationSeconds',
44
+ defaultValue: '25s',
45
+ envVar: 'TRACELOG_ABORTED_ERROR_THRESHOLD',
46
+ },
47
+ {
48
+ name: 'active',
49
+ configType: 'boolean',
50
+ defaultValue: true,
51
+ envVar: 'TRACELOG_ACTIVE',
52
+ },
53
+ {
54
+ name: 'addPatch',
55
+ configType: 'stringKeyValuePairs',
56
+ defaultValue: undefined,
57
+ envVar: 'TRACELOG_ADD_PATCH',
58
+ },
59
+ {
60
+ name: 'breakdownMetrics',
61
+ configType: 'boolean',
62
+ defaultValue: true,
63
+ envVar: 'TRACELOG_BREAKDOWN_METRICS',
64
+ },
65
+ {
66
+ name: 'captureBody',
67
+ configType: 'select(off,all,errors,transactions)',
68
+ defaultValue: 'off',
69
+ envVar: 'TRACELOG_CAPTURE_BODY',
70
+ centralConfigName: 'capture_body',
71
+ crossAgentName: 'capture_body',
72
+ },
73
+ {
74
+ name: 'captureErrorLogStackTraces',
75
+ configType: 'select(messages,always)',
76
+ defaultValue: 'messages',
77
+ envVar: 'TRACELOG_CAPTURE_ERROR_LOG_STACK_TRACES',
78
+ },
79
+ {
80
+ name: 'captureExceptions',
81
+ configType: 'boolean',
82
+ defaultValue: true,
83
+ envVar: 'TRACELOG_CAPTURE_EXCEPTIONS',
84
+ },
85
+ {
86
+ name: 'captureHeaders',
87
+ configType: 'boolean',
88
+ defaultValue: true,
89
+ envVar: 'TRACELOG_CAPTURE_HEADERS',
90
+ },
91
+ {
92
+ name: 'cloudProvider',
93
+ configType: 'select(auto,gcp,azure,aws,none)',
94
+ defaultValue: 'auto',
95
+ envVar: 'TRACELOG_CLOUD_PROVIDER',
96
+ },
97
+ {
98
+ name: 'containerId',
99
+ configType: 'string',
100
+ defaultValue: undefined,
101
+ envVar: 'TRACELOG_CONTAINER_ID',
102
+ },
103
+ {
104
+ name: 'contextPropagationOnly',
105
+ configType: 'boolean',
106
+ defaultValue: false,
107
+ envVar: 'TRACELOG_CONTEXT_PROPAGATION_ONLY',
108
+ },
109
+ {
110
+ name: 'customMetricsHistogramBoundaries',
111
+ configType: 'sortedNumberArray',
112
+ // Exponential powers-of-2 bucket boundaries, rounded to 6 significant figures.
113
+ // 2**N for N in [-8, -7.5, -7, ..., 16, 16.5, 17]
114
+ // https://github.com/elastic/apm/blob/main/specs/agents/metrics-otel.md#histogram-aggregation
115
+ defaultValue: [
116
+ 0.00390625, 0.00552427, 0.0078125, 0.0110485, 0.015625, 0.0220971,
117
+ 0.03125, 0.0441942, 0.0625, 0.0883883, 0.125, 0.176777, 0.25, 0.353553,
118
+ 0.5, 0.707107, 1, 1.41421, 2, 2.82843, 4, 5.65685, 8, 11.3137, 16,
119
+ 22.6274, 32, 45.2548, 64, 90.5097, 128, 181.019, 256, 362.039, 512,
120
+ 724.077, 1024, 1448.15, 2048, 2896.31, 4096, 5792.62, 8192, 11585.2,
121
+ 16384, 23170.5, 32768, 46341, 65536, 92681.9, 131072,
122
+ ],
123
+ envVar: 'TRACELOG_CUSTOM_METRICS_HISTOGRAM_BOUNDARIES',
124
+ },
125
+ {
126
+ name: 'disableInstrumentations',
127
+ configType: 'stringArray',
128
+ defaultValue: [],
129
+ envVar: 'TRACELOG_DISABLE_INSTRUMENTATIONS',
130
+ },
131
+ {
132
+ name: 'disableMetrics',
133
+ configType: 'stringArray',
134
+ defaultValue: [],
135
+ envVar: 'TRACELOG_DISABLE_METRICS',
136
+ },
137
+ {
138
+ name: 'disableMetricsRegExp',
139
+ configType: 'wildcardArray',
140
+ defaultValue: [],
141
+ deps: ['disableMetrics'],
142
+ },
143
+ {
144
+ name: 'elasticsearchCaptureBodyUrls',
145
+ configType: 'stringArray',
146
+ defaultValue: [
147
+ '*/_search',
148
+ '*/_search/template',
149
+ '*/_msearch',
150
+ '*/_msearch/template',
151
+ '*/_async_search',
152
+ '*/_count',
153
+ '*/_sql',
154
+ '*/_eql/search',
155
+ ],
156
+ envVar: 'TRACELOG_ELASTICSEARCH_CAPTURE_BODY_URLS',
157
+ },
158
+ {
159
+ name: 'elasticsearchCaptureBodyUrlsRegExp',
160
+ configType: 'wildcardArray',
161
+ defaultValue: [],
162
+ deps: ['elasticsearchCaptureBodyUrls'],
163
+ },
164
+ {
165
+ name: 'environment',
166
+ configType: 'string',
167
+ defaultValue: process.env.NODE_ENV || 'development',
168
+ envVar: 'TRACELOG_ENVIRONMENT',
169
+ },
170
+ {
171
+ name: 'errorOnAbortedRequests',
172
+ configType: 'boolean',
173
+ defaultValue: false,
174
+ envVar: 'TRACELOG_ERROR_ON_ABORTED_REQUESTS',
175
+ },
176
+ {
177
+ name: 'exitSpanMinDuration',
178
+ configType: 'durationMilliseconds',
179
+ defaultValue: '0ms',
180
+ envVar: 'TRACELOG_EXIT_SPAN_MIN_DURATION',
181
+ centralConfigName: 'exit_span_min_duration',
182
+ crossAgentName: 'exit_span_min_duration',
183
+ },
184
+ {
185
+ name: 'globalLabels',
186
+ configType: 'stringKeyValuePairs',
187
+ defaultValue: undefined,
188
+ envVar: 'TRACELOG_GLOBAL_LABELS',
189
+ },
190
+ {
191
+ name: 'ignoreMessageQueues',
192
+ configType: 'stringArray',
193
+ defaultValue: [],
194
+ envVar: 'TRACELOG_IGNORE_MESSAGE_QUEUES',
195
+ centralConfigName: 'ignore_message_queues',
196
+ crossAgentName: 'ignore_message_queues',
197
+ },
198
+ {
199
+ name: 'ignoreMessageQueuesRegExp',
200
+ configType: 'wildcardArray',
201
+ defaultValue: [],
202
+ deps: ['ignoreMessageQueues'],
203
+ },
204
+ {
205
+ name: 'instrument',
206
+ configType: 'boolean',
207
+ defaultValue: true,
208
+ envVar: 'TRACELOG_INSTRUMENT',
209
+ },
210
+ {
211
+ name: 'instrumentIncomingHTTPRequests',
212
+ configType: 'boolean',
213
+ defaultValue: true,
214
+ envVar: 'TRACELOG_INSTRUMENT_INCOMING_HTTP_REQUESTS',
215
+ },
216
+ {
217
+ name: 'kubernetesNamespace',
218
+ configType: 'string',
219
+ defaultValue: undefined,
220
+ envVar: 'KUBERNETES_NAMESPACE',
221
+ },
222
+ {
223
+ name: 'kubernetesNodeName',
224
+ configType: 'string',
225
+ defaultValue: undefined,
226
+ envVar: 'KUBERNETES_NODE_NAME',
227
+ },
228
+ {
229
+ name: 'kubernetesPodName',
230
+ configType: 'string',
231
+ defaultValue: undefined,
232
+ envVar: 'KUBERNETES_POD_NAME',
233
+ },
234
+ {
235
+ name: 'kubernetesPodUID',
236
+ configType: 'string',
237
+ defaultValue: undefined,
238
+ envVar: 'KUBERNETES_POD_UID',
239
+ },
240
+ {
241
+ name: 'logLevel',
242
+ configType: 'select(debug,info,warning,error,critical,off,trace)',
243
+ defaultValue: 'info',
244
+ envVar: 'TRACELOG_LOG_LEVEL',
245
+ centralConfigName: 'log_level',
246
+ crossAgentName: 'log_level',
247
+ },
248
+ {
249
+ name: 'longFieldMaxLength',
250
+ configType: 'number',
251
+ defaultValue: 10000,
252
+ envVar: 'TRACELOG_LONG_FIELD_MAX_LENGTH',
253
+ },
254
+ {
255
+ name: 'metricsInterval',
256
+ configType: 'durationSeconds',
257
+ defaultValue: '30s',
258
+ envVar: 'TRACELOG_METRICS_INTERVAL',
259
+ },
260
+ {
261
+ name: 'metricsLimit',
262
+ configType: 'number',
263
+ defaultValue: 1000,
264
+ envVar: 'TRACELOG_METRICS_LIMIT',
265
+ },
266
+ {
267
+ name: 'sanitizeFieldNames',
268
+ configType: 'stringArray',
269
+ // These patterns are specified in the shared APM specs:
270
+ // https://github.com/elastic/apm/blob/main/specs/agents/sanitization.md
271
+ defaultValue: [
272
+ 'password',
273
+ 'passwd',
274
+ 'pwd',
275
+ 'secret',
276
+ '*key',
277
+ '*token*',
278
+ '*session*',
279
+ '*credit*',
280
+ '*card*',
281
+ '*auth*',
282
+ 'set-cookie',
283
+ '*principal*',
284
+ // These are default patterns only in the Node.js APM agent, historically
285
+ // from when the "is-secret" dependency was used.
286
+ 'pw',
287
+ 'pass',
288
+ 'connect.sid',
289
+ // Additional ones added to the Node.js APM agent.
290
+ 'cookie', // The "Cookie" HTTP request header is often sensitive.
291
+ ],
292
+ envVar: 'TRACELOG_SANITIZE_FIELD_NAMES',
293
+ centralConfigName: 'sanitize_field_names',
294
+ crossAgentName: 'sanitize_field_names',
295
+ },
296
+ {
297
+ name: 'sanitizeFieldNamesRegExp',
298
+ configType: 'wildcardArray',
299
+ defaultValue: [],
300
+ deps: ['sanitizeFieldNames'],
301
+ },
302
+ {
303
+ name: 'serviceNodeName',
304
+ configType: 'string',
305
+ defaultValue: undefined,
306
+ envVar: 'TRACELOG_SERVICE_NODE_NAME',
307
+ },
308
+ {
309
+ name: 'sourceLinesErrorAppFrames',
310
+ configType: 'number',
311
+ defaultValue: 5,
312
+ envVar: 'TRACELOG_SOURCE_LINES_ERROR_APP_FRAMES',
313
+ },
314
+ {
315
+ name: 'sourceLinesErrorLibraryFrames',
316
+ configType: 'number',
317
+ defaultValue: 5,
318
+ envVar: 'TRACELOG_SOURCE_LINES_ERROR_LIBRARY_FRAMES',
319
+ },
320
+ {
321
+ name: 'sourceLinesSpanAppFrames',
322
+ configType: 'number',
323
+ defaultValue: 0,
324
+ envVar: 'TRACELOG_SOURCE_LINES_SPAN_APP_FRAMES',
325
+ },
326
+ {
327
+ name: 'sourceLinesSpanLibraryFrames',
328
+ configType: 'number',
329
+ defaultValue: 0,
330
+ envVar: 'TRACELOG_SOURCE_LINES_SPAN_LIBRARY_FRAMES',
331
+ },
332
+ {
333
+ name: 'spanCompressionEnabled',
334
+ configType: 'boolean',
335
+ defaultValue: true,
336
+ envVar: 'TRACELOG_SPAN_COMPRESSION_ENABLED',
337
+ },
338
+ {
339
+ name: 'spanCompressionExactMatchMaxDuration',
340
+ configType: 'durationCompression',
341
+ defaultValue: '50ms',
342
+ envVar: 'TRACELOG_SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION',
343
+ },
344
+ {
345
+ name: 'spanCompressionSameKindMaxDuration',
346
+ configType: 'durationCompression',
347
+ defaultValue: '0ms',
348
+ envVar: 'TRACELOG_SPAN_COMPRESSION_SAME_KIND_MAX_DURATION',
349
+ },
350
+ {
351
+ name: 'stackTraceLimit',
352
+ configType: 'number',
353
+ defaultValue: 50,
354
+ envVar: 'TRACELOG_STACK_TRACE_LIMIT',
355
+ },
356
+ {
357
+ name: 'traceContinuationStrategy',
358
+ configType: 'select(continue,restart,external)',
359
+ defaultValue: 'continue',
360
+ envVar: 'TRACELOG_TRACE_CONTINUATION_STRATEGY',
361
+ centralConfigName: 'trace_continuation_strategy',
362
+ crossAgentName: 'trace_continuation_strategy',
363
+ },
364
+ {
365
+ name: 'transactionIgnoreUrls',
366
+ configType: 'stringArray',
367
+ defaultValue: [],
368
+ envVar: 'TRACELOG_TRANSACTION_IGNORE_URLS',
369
+ centralConfigName: 'transaction_ignore_urls',
370
+ crossAgentName: 'transaction_ignore_urls',
371
+ },
372
+ {
373
+ name: 'transactionIgnoreUrlRegExp',
374
+ configType: 'wildcardArray',
375
+ defaultValue: [],
376
+ deps: ['transactionIgnoreUrls'],
377
+ },
378
+ {
379
+ name: 'transactionMaxSpans',
380
+ configType: 'numberInfinity',
381
+ defaultValue: 500,
382
+ envVar: 'TRACELOG_TRANSACTION_MAX_SPANS',
383
+ centralConfigName: 'transaction_max_spans',
384
+ crossAgentName: 'transaction_max_spans',
385
+ },
386
+ {
387
+ name: 'transactionSampleRate',
388
+ configType: 'sampleRate',
389
+ defaultValue: 1,
390
+ envVar: 'TRACELOG_TRANSACTION_SAMPLE_RATE',
391
+ centralConfigName: 'transaction_sample_rate',
392
+ crossAgentName: 'transaction_sample_rate',
393
+ },
394
+ {
395
+ name: 'usePathAsTransactionName',
396
+ configType: 'boolean',
397
+ defaultValue: false,
398
+ envVar: 'TRACELOG_USE_PATH_AS_TRANSACTION_NAME',
399
+ },
400
+ {
401
+ name: 'spanStackTraceMinDuration',
402
+ configType: 'durationMillisecondsNegative',
403
+ // 'spanStackTraceMinDuration' is explicitly *not* defined in DEFAULTS
404
+ // because normalizeSpanStackTraceMinDuration() needs to know if a value
405
+ // was provided by the user.
406
+ defaultValue: undefined,
407
+ envVar: 'TRACELOG_SPAN_STACK_TRACE_MIN_DURATION',
408
+ centralConfigName: 'span_stack_trace_min_duration',
409
+ crossAgentName: 'span_stack_trace_min_duration',
410
+ },
411
+ {
412
+ name: 'contextManager',
413
+ configType: 'select(asynclocalstorage,asynchooks)',
414
+ // 'contextManager' is explicitly *not* defined in DEFAULTS because
415
+ // normalizeContextManager() needs to know if a value was provided by the
416
+ // user.
417
+ defaultValue: undefined,
418
+ envVar: 'TRACELOG_CONTEXT_MANAGER',
419
+ },
420
+ {
421
+ name: 'frameworkName',
422
+ configType: 'string',
423
+ defaultValue: undefined,
424
+ envVar: 'TRACELOG_FRAMEWORK_NAME',
425
+ },
426
+ {
427
+ name: 'frameworkVersion',
428
+ configType: 'string',
429
+ defaultValue: undefined,
430
+ envVar: 'TRACELOG_FRAMEWORK_VERSION',
431
+ },
432
+ {
433
+ name: 'hostname',
434
+ configType: 'string',
435
+ defaultValue: undefined,
436
+ envVar: 'TRACELOG_HOSTNAME',
437
+ },
438
+ {
439
+ name: 'serviceName',
440
+ configType: 'string',
441
+ defaultValue: undefined,
442
+ envVar: 'TRACELOG_SERVICE_NAME',
443
+ crossAgentName: 'service_name',
444
+ },
445
+ {
446
+ name: 'serviceVersion',
447
+ configType: 'string',
448
+ defaultValue: undefined,
449
+ envVar: 'TRACELOG_SERVICE_VERSION',
450
+ crossAgentName: 'service_version',
451
+ },
452
+ { name: 'ignoreUrls', configType: 'stringArray', defaultValue: undefined },
453
+ {
454
+ name: 'ignoreUrlRegExp',
455
+ configType: 'wildcardArray',
456
+ defaultValue: [],
457
+ deps: ['ignoreUrls'],
458
+ },
459
+ {
460
+ name: 'ignoreUrlStr',
461
+ configType: 'wildcardArray',
462
+ defaultValue: [],
463
+ deps: ['ignoreUrls'],
464
+ },
465
+ {
466
+ name: 'ignoreUserAgents',
467
+ configType: 'stringArray',
468
+ defaultValue: undefined,
469
+ },
470
+ {
471
+ name: 'ignoreUserAgentRegExp',
472
+ configType: 'wildcardArray',
473
+ defaultValue: [],
474
+ deps: ['ignoreUserAgents'],
475
+ },
476
+ {
477
+ name: 'ignoreUserAgentStr',
478
+ configType: 'wildcardArray',
479
+ defaultValue: [],
480
+ deps: ['ignoreUserAgents'],
481
+ },
482
+ // Log rotation
483
+ {
484
+ name: 'logRotationSchedule',
485
+ configType: 'string',
486
+ defaultValue: 'daily',
487
+ envVar: 'TRACELOG_LOG_ROTATION_SCHEDULE',
488
+ },
489
+ {
490
+ name: 'maxLocalRetentionDays',
491
+ configType: 'number',
492
+ defaultValue: 0,
493
+ envVar: 'TRACELOG_MAX_LOCAL_RETENTION_DAYS',
494
+ },
495
+ {
496
+ name: 'maxBufferSize',
497
+ configType: 'number',
498
+ defaultValue: 10000,
499
+ envVar: 'TRACELOG_MAX_BUFFER_SIZE',
500
+ },
501
+ // S3 upload
502
+ {
503
+ name: 's3Bucket',
504
+ configType: 'string',
505
+ defaultValue: undefined,
506
+ envVar: 'TRACELOG_S3_BUCKET',
507
+ },
508
+ {
509
+ name: 's3Region',
510
+ configType: 'string',
511
+ defaultValue: undefined,
512
+ envVar: 'TRACELOG_S3_REGION',
513
+ },
514
+ {
515
+ name: 's3KeyTemplate',
516
+ configType: 'string',
517
+ defaultValue:
518
+ '{serviceName}/{environment}/{date}/{hostname}-{pid}-{timestamp}.jsonl',
519
+ envVar: 'TRACELOG_S3_KEY_TEMPLATE',
520
+ },
521
+ {
522
+ name: 's3UploadIntervalMs',
523
+ configType: 'number',
524
+ defaultValue: 300000,
525
+ envVar: 'TRACELOG_S3_UPLOAD_INTERVAL_MS',
526
+ },
527
+ {
528
+ name: 's3GzipCompleted',
529
+ configType: 'boolean',
530
+ defaultValue: true,
531
+ envVar: 'TRACELOG_S3_GZIP_COMPLETED',
532
+ },
533
+ {
534
+ name: 's3GzipCurrent',
535
+ configType: 'boolean',
536
+ defaultValue: true,
537
+ envVar: 'TRACELOG_S3_GZIP_CURRENT',
538
+ },
539
+ {
540
+ name: 's3AccessKeyId',
541
+ configType: 'string',
542
+ defaultValue: undefined,
543
+ envVar: 'TRACELOG_S3_ACCESS_KEY_ID',
544
+ redactFn: () => REDACTED,
545
+ },
546
+ {
547
+ name: 's3SecretAccessKey',
548
+ configType: 'string',
549
+ defaultValue: undefined,
550
+ envVar: 'TRACELOG_S3_SECRET_ACCESS_KEY',
551
+ redactFn: () => REDACTED,
552
+ },
553
+ {
554
+ name: 's3SessionToken',
555
+ configType: 'string',
556
+ defaultValue: undefined,
557
+ envVar: 'TRACELOG_S3_SESSION_TOKEN',
558
+ redactFn: () => REDACTED,
559
+ },
560
+ // Special options that
561
+ // - may afect the whole config
562
+ // - change the behavior of thelogger
563
+ // - are for testing or internal use
564
+ {
565
+ name: 'configFile',
566
+ configType: 'string',
567
+ defaultValue: 'tracelog.config.js',
568
+ envVar: 'TRACELOG_CONFIG_FILE',
569
+ },
570
+ {
571
+ name: 'logger',
572
+ configType: 'logger',
573
+ defaultValue: undefined,
574
+ envVar: 'TRACELOG_LOGGER',
575
+ },
576
+ {
577
+ name: 'transport',
578
+ configType: 'function',
579
+ defaultValue: undefined,
580
+ internal: true,
581
+ },
582
+ ];
583
+
584
+ /**
585
+ * Retuns an object with th default values for all config options
586
+ * @returns {Record<string,any>}
587
+ */
588
+ function getDefaultOptions() {
589
+ return CONFIG_SCHEMA.reduce((acc, def) => {
590
+ if (typeof def.defaultValue !== 'undefined') {
591
+ acc[def.name] = def.defaultValue;
592
+ }
593
+ return acc;
594
+ }, {});
595
+ }
596
+
597
+ /**
598
+ * Retuns an object with the values of config options defined set
599
+ * in the environment
600
+ * @returns {Record<string,any>}
601
+ */
602
+ function getEnvironmentOptions() {
603
+ return CONFIG_SCHEMA.reduce((acc, def) => {
604
+ if ('environmentValue' in def) {
605
+ acc[def.name] = def.environmentValue;
606
+ }
607
+ return acc;
608
+ }, {});
609
+ }
610
+
611
+ /**
612
+ * Retuns an object with the values of config options defined set
613
+ * in the environment
614
+ * @returns {Record<string,any>}
615
+ */
616
+ function getFileOptions() {
617
+ return CONFIG_SCHEMA.reduce((acc, def) => {
618
+ if ('fileValue' in def) {
619
+ acc[def.name] = def.fileValue;
620
+ }
621
+ return acc;
622
+ }, {});
623
+ }
624
+
625
+ /**
626
+ * Support function to read environment after the module is loaded. This is needed to
627
+ * perform some tests that modify `process.env`
628
+ * - ./test/config.tes.js
629
+ * - ./test/logging-preamble.test.js
630
+ *
631
+ * TODO: remove this function when test are refactored using the good `runTestFixture` method
632
+ */
633
+ function readEnvOptions() {
634
+ CONFIG_SCHEMA.forEach((def) => {
635
+ if (def.envVar && process.env[def.envVar]) {
636
+ def.environmentValue = process.env[def.envVar];
637
+ def.source = 'environment';
638
+ } else if (def.envVar) {
639
+ // TODO: this is necessary since config.test.js sets different vars during tests
640
+ delete def.environmentValue;
641
+ delete def.source;
642
+ }
643
+ });
644
+ }
645
+
646
+ /**
647
+ * Tries to load a configuration file for the given path or from the ENV vars, if both undefined
648
+ * it uses the default config filename `elastic-apm-node.js`. If the config file is not present or
649
+ * there is something wrong in the file it will return no options
650
+ * @param {string} [confPath] the path to the file
651
+ * @returns {Record<string,any> | null}
652
+ */
653
+ function readFileOptions(confPath) {
654
+ const configFileDef = CONFIG_SCHEMA.find((def) => def.name === 'configFile');
655
+ const defaultValue = configFileDef.defaultValue;
656
+ const envValue = process.env[configFileDef.envVar];
657
+ const filePath = path.resolve(confPath || envValue || defaultValue);
658
+
659
+ if (fs.existsSync(filePath)) {
660
+ try {
661
+ return require(filePath);
662
+ } catch (err) {
663
+ console.error(
664
+ "Tracelog initialization error: Can't read config file %s",
665
+ filePath,
666
+ );
667
+ console.error(err.stack);
668
+ }
669
+ }
670
+
671
+ return null;
672
+ }
673
+
674
+ /**
675
+ * Sets into the schema the value given at agent start
676
+ *
677
+ * @param {Record<string,any>} options
678
+ */
679
+ function setStartOptions(options) {
680
+ if (!options) {
681
+ return;
682
+ }
683
+
684
+ const fileOpts = options.configFile && readFileOptions(options.configFile);
685
+
686
+ CONFIG_SCHEMA.forEach((def) => {
687
+ const source = def.source || 'default';
688
+
689
+ if (def.name in options) {
690
+ def.startValue = options[def.name];
691
+ if (source !== 'environment') {
692
+ def.source = 'start';
693
+ }
694
+ } else if (fileOpts && def.name in fileOpts) {
695
+ // console.log('setting from file', def.name, fileOpts[def.name], source);
696
+ def.fileValue = fileOpts[def.name];
697
+ if (source === 'default') {
698
+ def.source = 'file';
699
+ }
700
+ console.log(def);
701
+ }
702
+ });
703
+ }
704
+
705
+ /**
706
+ * Collects information about the agent, environment and configuration options that are
707
+ * sets from a source (env vars, start options or config file). For each config option it
708
+ * collects
709
+ * - source: where te value came from (environment, start, file)
710
+ * - sourceValue: the raw value provided im the source
711
+ * - normalizedValue: the value normalized by the configuration which is used in the agent
712
+ * - file: if source === 'file' this proprty is defined with the path of the config file
713
+ *
714
+ * @param {Object} config The configuration object with normalized options
715
+ * @returns {Object} Object with agent, environment, and configuration data.
716
+ */
717
+ function getPreambleData(config) {
718
+ const configFileDef = CONFIG_SCHEMA.find((def) => def.name === 'configFile');
719
+ const configFilePath = path.resolve(
720
+ configFileDef.environmentValue ||
721
+ configFileDef.startValue ||
722
+ configFileDef.defaultValue,
723
+ );
724
+
725
+ const result = {
726
+ agentVersion: AGENT_VERSION,
727
+ env: {
728
+ pid: process.pid,
729
+ proctitle: process.title,
730
+ // For darwin: https://en.wikipedia.org/wiki/Darwin_%28operating_system%29#Release_history
731
+ os: `${os.platform()} ${os.release()}`,
732
+ arch: os.arch(),
733
+ host: os.hostname(),
734
+ timezone: getTimezoneUtc(),
735
+ runtime: `Node.js ${process.version}`,
736
+ },
737
+ config: {},
738
+ };
739
+
740
+ const excludedKeys = new Set(['logger', 'transport']);
741
+ const mandatoryKeys = new Set([
742
+ 'serviceName',
743
+ 'serviceVersion',
744
+ 'logLevel',
745
+ ]);
746
+
747
+ CONFIG_SCHEMA.forEach((def) => {
748
+ if (
749
+ excludedKeys.has(def.name) ||
750
+ (!def.source && !mandatoryKeys.has(def.name))
751
+ ) {
752
+ return;
753
+ }
754
+
755
+ const details = {
756
+ source: def.source || 'default',
757
+ value: config[def.name],
758
+ };
759
+
760
+ const sourceVal = def[`${details.source}Value`];
761
+
762
+ if (def.crossAgentName) {
763
+ details.commonName = def.crossAgentName;
764
+ }
765
+
766
+ if (def.source === 'file') {
767
+ details.file = configFilePath;
768
+ }
769
+
770
+ if (typeof def.redactFn === 'function') {
771
+ details.value = def.redactFn(details.value);
772
+ } else if (sourceVal !== details.value) {
773
+ details.sourceValue = sourceVal;
774
+ }
775
+
776
+ result.config[def.name] = details;
777
+ });
778
+
779
+ return result;
780
+ }
781
+
782
+ /**
783
+ * Returns the current Timezone in UTC notation fomat
784
+ *
785
+ * @returns {String} ex. UTC-0600
786
+ */
787
+ function getTimezoneUtc() {
788
+ const offsetHours = -(new Date().getTimezoneOffset() / 60);
789
+ const offsetSign = offsetHours >= 0 ? '+' : '-';
790
+ const offsetPad = Math.abs(offsetHours) < 10 ? '0' : '';
791
+ return `UTC${offsetSign}${offsetPad}${Math.abs(offsetHours * 100)}`;
792
+ }
793
+
794
+ /**
795
+ * Takes an URL string and redacts user & password info if present in the basic
796
+ * auth part
797
+ *
798
+ * @param {String} urlStr The URL to strip sensitive info
799
+ * @returns {String || null} url with basic auth REDACTED
800
+ */
801
+ function sanitizeUrl(urlStr) {
802
+ if (!urlStr) {
803
+ return '';
804
+ }
805
+
806
+ const url = new URL(urlStr);
807
+ if (url.username) {
808
+ url.username = REDACTED;
809
+ }
810
+ if (url.password) {
811
+ url.password = REDACTED;
812
+ }
813
+
814
+ return decodeURIComponent(url.href);
815
+ }
816
+
817
+ // Fail fast if CONFIG_SCHEMA has issues
818
+ // - duplicated options
819
+ // - dependencies not defined or defined after the dependant option
820
+ // - type with no normalizer
821
+ const depSet = new Set();
822
+ const fileOpts = readFileOptions();
823
+
824
+ CONFIG_SCHEMA.forEach((def) => {
825
+ if (depSet.has(def.name)) {
826
+ throw Error(`config option ${def.name} duplicated.`);
827
+ }
828
+ if (def.deps && def.deps.some((d) => !depSet.has(d))) {
829
+ throw Error(
830
+ `config option ${def.name} dependencies (${def.deps}) need to be defined before.`,
831
+ );
832
+ }
833
+ depSet.add(def.name);
834
+
835
+ // Initalize values comming from environment and config file
836
+ if (def.envVar && process.env[def.envVar]) {
837
+ def.environmentValue = process.env[def.envVar];
838
+ def.source = 'environment';
839
+ } else if (fileOpts && typeof fileOpts[def.name] !== 'undefined') {
840
+ def.fileValue = fileOpts[def.name];
841
+ def.source = 'file';
842
+ }
843
+ });
844
+
845
+ // TODO: these constants below to be removed in the future (normalization)
846
+ function getConfigName(def) {
847
+ return def.name;
848
+ }
849
+ function optsOfTypes(types) {
850
+ if (!Array.isArray(types)) {
851
+ types = [types];
852
+ }
853
+ return CONFIG_SCHEMA.filter((def) => types.indexOf(def.configType) !== -1);
854
+ }
855
+
856
+ const BOOL_OPTS = optsOfTypes('boolean').map(getConfigName);
857
+ const NUM_OPTS = optsOfTypes(['number', 'numberInfinity']).map(getConfigName);
858
+ const BYTES_OPTS = optsOfTypes('byte').map(getConfigName);
859
+ const URL_OPTS = optsOfTypes('url').map(getConfigName);
860
+ const ARRAY_OPTS = optsOfTypes('stringArray').map(getConfigName);
861
+ const KEY_VALUE_OPTS = optsOfTypes('stringKeyValuePairs').map(getConfigName);
862
+ const MINUS_ONE_EQUAL_INFINITY = optsOfTypes(['numberInfinity']).map(
863
+ getConfigName,
864
+ );
865
+
866
+ const CENTRAL_CONFIG_OPTS = CONFIG_SCHEMA.filter(
867
+ (def) => def.centralConfigName,
868
+ ).reduce((acc, def) => {
869
+ acc[def.centralConfigName] = def.name;
870
+ return acc;
871
+ }, {});
872
+
873
+ const DURATION_OPTS = [
874
+ {
875
+ name: 'abortedErrorThreshold',
876
+ defaultUnit: 's',
877
+ allowedUnits: ['ms', 's', 'm'],
878
+ allowNegative: false,
879
+ },
880
+ {
881
+ name: 'exitSpanMinDuration',
882
+ defaultUnit: 'ms',
883
+ allowedUnits: ['us', 'ms', 's', 'm'],
884
+ allowNegative: false,
885
+ },
886
+ {
887
+ name: 'metricsInterval',
888
+ defaultUnit: 's',
889
+ allowedUnits: ['ms', 's', 'm'],
890
+ allowNegative: false,
891
+ },
892
+ {
893
+ name: 'spanCompressionExactMatchMaxDuration',
894
+ defaultUnit: 'ms',
895
+ allowedUnits: ['ms', 's', 'm'],
896
+ allowNegative: false,
897
+ },
898
+ {
899
+ name: 'spanCompressionSameKindMaxDuration',
900
+ defaultUnit: 'ms',
901
+ allowedUnits: ['ms', 's', 'm'],
902
+ allowNegative: false,
903
+ },
904
+ {
905
+ name: 'spanStackTraceMinDuration',
906
+ defaultUnit: 'ms',
907
+ allowedUnits: ['ms', 's', 'm'],
908
+ allowNegative: true,
909
+ },
910
+ ];
911
+ const CROSS_AGENT_CONFIG_VAR_NAME = CONFIG_SCHEMA.filter(
912
+ (def) => def.crossAgentName,
913
+ ).reduce((acc, def) => {
914
+ acc[def.name] = def.crossAgentName;
915
+ return acc;
916
+ }, {});
917
+
918
+ const ENV_TABLE = CONFIG_SCHEMA.filter((def) => def.envVar).reduce(
919
+ (acc, def) => {
920
+ acc[def.name] = def.envVar;
921
+ return acc;
922
+ },
923
+ {},
924
+ );
925
+
926
+ // Exports
927
+ module.exports = {
928
+ BOOL_OPTS,
929
+ NUM_OPTS,
930
+ DURATION_OPTS,
931
+ BYTES_OPTS,
932
+ MINUS_ONE_EQUAL_INFINITY,
933
+ ARRAY_OPTS,
934
+ KEY_VALUE_OPTS,
935
+ URL_OPTS,
936
+ CROSS_AGENT_CONFIG_VAR_NAME,
937
+ CENTRAL_CONFIG_OPTS,
938
+ ENV_TABLE,
939
+ CONFIG_SCHEMA,
940
+ getDefaultOptions,
941
+ getEnvironmentOptions,
942
+ getFileOptions,
943
+ setStartOptions,
944
+ getPreambleData,
945
+ readEnvOptions,
946
+ };