dd-trace 5.96.0 → 5.97.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 (114) hide show
  1. package/index.d.ts +34 -0
  2. package/package.json +9 -7
  3. package/packages/datadog-esbuild/index.js +20 -9
  4. package/packages/datadog-instrumentations/src/child_process.js +7 -17
  5. package/packages/datadog-instrumentations/src/crypto.js +1 -2
  6. package/packages/datadog-instrumentations/src/cucumber.js +4 -1
  7. package/packages/datadog-instrumentations/src/cypress-config.js +324 -0
  8. package/packages/datadog-instrumentations/src/cypress.js +86 -4
  9. package/packages/datadog-instrumentations/src/dns.js +1 -2
  10. package/packages/datadog-instrumentations/src/express.js +4 -4
  11. package/packages/datadog-instrumentations/src/fs.js +27 -29
  12. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
  14. package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
  17. package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
  18. package/packages/datadog-instrumentations/src/http/client.js +2 -3
  19. package/packages/datadog-instrumentations/src/http/server.js +2 -5
  20. package/packages/datadog-instrumentations/src/http2/client.js +1 -3
  21. package/packages/datadog-instrumentations/src/http2/server.js +1 -3
  22. package/packages/datadog-instrumentations/src/jest.js +13 -4
  23. package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +4 -1
  25. package/packages/datadog-instrumentations/src/net.js +2 -8
  26. package/packages/datadog-instrumentations/src/pino.js +1 -1
  27. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  28. package/packages/datadog-instrumentations/src/prisma.js +1 -2
  29. package/packages/datadog-instrumentations/src/selenium.js +4 -1
  30. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  31. package/packages/datadog-instrumentations/src/url.js +1 -3
  32. package/packages/datadog-instrumentations/src/vitest.js +5 -1
  33. package/packages/datadog-instrumentations/src/vm.js +1 -3
  34. package/packages/datadog-plugin-aws-sdk/src/base.js +4 -3
  35. package/packages/datadog-plugin-cucumber/src/index.js +7 -3
  36. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +57 -5
  37. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  38. package/packages/datadog-plugin-jest/src/index.js +4 -2
  39. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
  40. package/packages/datadog-plugin-mocha/src/index.js +5 -2
  41. package/packages/datadog-plugin-next/src/index.js +2 -14
  42. package/packages/datadog-plugin-openai/src/services.js +1 -0
  43. package/packages/datadog-webpack/index.js +3 -3
  44. package/packages/dd-trace/index.js +12 -10
  45. package/packages/dd-trace/src/agent/url.js +2 -2
  46. package/packages/dd-trace/src/aiguard/sdk.js +4 -0
  47. package/packages/dd-trace/src/appsec/blocking.js +3 -0
  48. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  51. package/packages/dd-trace/src/appsec/remote_config.js +1 -0
  52. package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
  53. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
  54. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  55. package/packages/dd-trace/src/config/defaults.js +316 -146
  56. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
  57. package/packages/dd-trace/src/config/helper.js +59 -10
  58. package/packages/dd-trace/src/config/index.js +569 -1505
  59. package/packages/dd-trace/src/config/parsers.js +256 -0
  60. package/packages/dd-trace/src/config/remote_config.js +59 -2
  61. package/packages/dd-trace/src/config/supported-configurations.json +350 -433
  62. package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
  63. package/packages/dd-trace/src/crashtracking/index.js +1 -7
  64. package/packages/dd-trace/src/debugger/index.js +1 -1
  65. package/packages/dd-trace/src/dogstatsd.js +12 -9
  66. package/packages/dd-trace/src/encode/0.4.js +1 -1
  67. package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
  68. package/packages/dd-trace/src/exporters/common/request.js +9 -0
  69. package/packages/dd-trace/src/exporters/common/writer.js +12 -2
  70. package/packages/dd-trace/src/heap_snapshots.js +3 -0
  71. package/packages/dd-trace/src/index.js +5 -2
  72. package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
  73. package/packages/dd-trace/src/llmobs/index.js +4 -1
  74. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
  75. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
  76. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
  77. package/packages/dd-trace/src/llmobs/sdk.js +12 -8
  78. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  79. package/packages/dd-trace/src/llmobs/tagger.js +9 -6
  80. package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
  81. package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
  82. package/packages/dd-trace/src/log/index.js +26 -55
  83. package/packages/dd-trace/src/log/writer.js +7 -19
  84. package/packages/dd-trace/src/noop/proxy.js +8 -0
  85. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  86. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  87. package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
  88. package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
  89. package/packages/dd-trace/src/plugin_manager.js +8 -6
  90. package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
  91. package/packages/dd-trace/src/plugins/plugin.js +7 -4
  92. package/packages/dd-trace/src/process-tags/index.js +3 -0
  93. package/packages/dd-trace/src/profiler.js +27 -2
  94. package/packages/dd-trace/src/profiling/config.js +73 -241
  95. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
  96. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
  97. package/packages/dd-trace/src/profiling/profiler.js +56 -44
  98. package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
  99. package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
  100. package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
  101. package/packages/dd-trace/src/propagation-hash/index.js +2 -1
  102. package/packages/dd-trace/src/proxy.js +32 -3
  103. package/packages/dd-trace/src/remote_config/index.js +3 -0
  104. package/packages/dd-trace/src/require-package-json.js +8 -4
  105. package/packages/dd-trace/src/ritm.js +58 -26
  106. package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
  107. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +3 -0
  108. package/packages/dd-trace/src/sampler.js +1 -1
  109. package/packages/dd-trace/src/standalone/index.js +3 -0
  110. package/packages/dd-trace/src/telemetry/index.js +2 -3
  111. package/packages/dd-trace/src/telemetry/send-data.js +5 -19
  112. package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
  113. package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
  114. package/packages/dd-trace/src/util.js +0 -9
@@ -1,177 +1,347 @@
1
1
  'use strict'
2
2
 
3
- const pkg = require('../pkg')
4
- const { isFalse, isTrue } = require('../util')
5
- const { DD_MAJOR } = require('../../../../version')
6
- const { getEnvironmentVariable: getEnv } = require('./helper')
3
+ const dns = require('dns')
4
+ const util = require('util')
7
5
 
6
+ const { DD_MAJOR } = require('../../../../version')
7
+ const { parsers, transformers, telemetryTransformers, setWarnInvalidValue } = require('./parsers')
8
8
  const {
9
9
  supportedConfigurations,
10
10
  } = /** @type {import('./helper').SupportedConfigurationsJson} */ (require('./supported-configurations.json'))
11
11
 
12
- const service = getEnv('AWS_LAMBDA_FUNCTION_NAME') ||
13
- getEnv('FUNCTION_NAME') || // Google Cloud Function Name set by deprecated runtimes
14
- getEnv('K_SERVICE') || // Google Cloud Function Name set by newer runtimes
15
- getEnv('WEBSITE_SITE_NAME') || // set by Azure Functions
16
- pkg.name ||
17
- 'node'
12
+ let log
13
+ let seqId = 0
14
+ const configWithOrigin = new Map()
15
+ const parseErrors = new Map()
16
+
17
+ if (DD_MAJOR >= 6) {
18
+ // Programmatic configuration of DD_IAST_SECURITY_CONTROLS_CONFIGURATION is not supported
19
+ // in newer major versions. This is special handled here until a better solution is found.
20
+ // TODO: Remove the programmatic configuration from supported-configurations.json once v5 is not supported anymore.
21
+ supportedConfigurations.DD_IAST_SECURITY_CONTROLS_CONFIGURATION[0].internalPropertyName =
22
+ supportedConfigurations.DD_IAST_SECURITY_CONTROLS_CONFIGURATION[0].configurationNames?.[0]
23
+ delete supportedConfigurations.DD_IAST_SECURITY_CONTROLS_CONFIGURATION[0].configurationNames
24
+ } else {
25
+ // Default value for DD_TRACE_STARTUP_LOGS is 'false' in older major versions.
26
+ // This is special handled here until a better solution is found.
27
+ // TODO: Remove this here once v5 is not supported anymore.
28
+ supportedConfigurations.DD_TRACE_STARTUP_LOGS[0].default = 'false'
29
+ }
18
30
 
19
31
  /**
20
- * @param {string|null} raw
21
- * @param {string} type
22
- * @returns {string|number|boolean|Record<string, string>|unknown[]|undefined}
32
+ * Warns about an invalid value for an option and adds the error to the last telemetry entry if it is not already set.
33
+ * Logging happens only if the error is not already set or the option name is different from the last telemetry entry.
34
+ *
35
+ * @param {unknown} value - The value that is invalid.
36
+ * @param {string} optionName - The name of the option.
37
+ * @param {string} source - The source of the value.
38
+ * @param {string} baseMessage - The base message to use for the warning.
39
+ * @param {Error} [error] - An error that was thrown while parsing the value.
23
40
  */
24
- function parseDefaultByType (raw, type) {
25
- if (raw === null) {
26
- return
41
+ function warnInvalidValue (value, optionName, source, baseMessage, error) {
42
+ const canonicalName = (optionsTable[optionName]?.canonicalName ?? optionName) + source
43
+ // Lazy load log module to avoid circular dependency
44
+ if (!parseErrors.has(canonicalName)) {
45
+ // TODO: Rephrase: It will fallback to former source (or default if not set)
46
+ let message = `${baseMessage}: ${util.inspect(value)} for ${optionName} (source: ${source}), picked default`
47
+ if (error) {
48
+ error.stack = error.toString()
49
+ message += `\n\n${util.inspect(error)}`
50
+ }
51
+ parseErrors.set(canonicalName, { message })
52
+ log ??= require('../log')
53
+ const logLevel = error ? 'error' : 'warn'
54
+ log[logLevel](message)
27
55
  }
56
+ }
57
+ setWarnInvalidValue(warnInvalidValue)
28
58
 
29
- switch (type) {
30
- case 'boolean':
31
- if (isTrue(raw)) return true
32
- if (isFalse(raw)) return false
33
- // TODO: What should we do with these?
34
- return
35
- case 'int':
36
- case 'decimal': {
37
- return Number(raw)
38
- }
39
- case 'array': {
40
- if (!raw || raw.length === 0) return []
41
- // TODO: Make the parsing a helper that is reused.
42
- return raw.split(',').map(item => {
43
- const colonIndex = item.indexOf(':')
44
- if (colonIndex === -1) {
45
- return item.trim()
46
- }
47
- const key = item.slice(0, colonIndex).trim()
48
- const value = item.slice(colonIndex + 1).trim()
49
- return `${key}:${value}`
50
- })
59
+ /** @type {import('./config-types').ConfigDefaults} */
60
+ const defaults = {
61
+ instrumentationSource: 'manual',
62
+ isServiceUserProvided: false,
63
+ isServiceNameInferred: true,
64
+ plugins: true,
65
+ isCiVisibility: false,
66
+ lookup: dns.lookup,
67
+ logger: undefined,
68
+ }
69
+
70
+ for (const [name, value] of Object.entries(defaults)) {
71
+ configWithOrigin.set(`${name}default`, {
72
+ name,
73
+ value: value ?? null,
74
+ origin: 'default',
75
+ seq_id: seqId++,
76
+ })
77
+ }
78
+
79
+ /**
80
+ * @param {unknown} value
81
+ * @param {string} origin
82
+ * @param {string} optionName
83
+ */
84
+ function generateTelemetry (value = null, origin, optionName) {
85
+ const { type, canonicalName = optionName } = configurationsTable[optionName] ?? { type: typeof value }
86
+ // TODO: Consider adding a preParser hook to the parsers object.
87
+ if (canonicalName === 'OTEL_RESOURCE_ATTRIBUTES') {
88
+ value = telemetryTransformers.MAP(value)
89
+ }
90
+ // TODO: Should we not send defaults to telemetry to reduce size?
91
+ // TODO: How to handle aliases/actual names in the future? Optional fields? Normalize the name at intake?
92
+ // TODO: Validate that space separated tags are parsed by the backend. Optimizations would be possible with that.
93
+ // TODO: How to handle telemetry reporting for aliases?
94
+ if (value !== null) {
95
+ if (telemetryTransformers[type]) {
96
+ value = telemetryTransformers[type](value)
97
+ } else if (typeof value === 'object' && value !== null) {
98
+ value = value instanceof URL
99
+ ? String(value)
100
+ : JSON.stringify(value)
101
+ } else if (typeof value === 'function') {
102
+ value = value.name || 'function'
51
103
  }
52
- case 'map': {
53
- if (!raw || raw.length === 0) return {}
54
- // TODO: Make the parsing a helper that is reused.
55
- /** @type {Record<string, string>} */
56
- const entries = {}
57
- for (const item of raw.split(',')) {
58
- const colonIndex = item.indexOf(':')
59
- if (colonIndex === -1) {
60
- const key = item.trim()
61
- if (key.length > 0) {
62
- entries[key] = ''
104
+ }
105
+ const telemetryEntry = {
106
+ name: canonicalName,
107
+ value,
108
+ origin,
109
+ seq_id: seqId++,
110
+ }
111
+ const error = parseErrors.get(`${canonicalName}${origin}`)
112
+ if (error) {
113
+ parseErrors.delete(`${canonicalName}${origin}`)
114
+ telemetryEntry.error = error
115
+ }
116
+ configWithOrigin.set(`${canonicalName}${origin}`, telemetryEntry)
117
+ }
118
+
119
+ // Iterate over the object and always handle the leaf properties as lookup.
120
+ // Example entries:
121
+ //
122
+ // cloudPayloadTagging: {
123
+ // nestedProperties: [
124
+ // 'rules',
125
+ // 'requestsEnabled',
126
+ // 'responses',
127
+ // ],
128
+ // option: {
129
+ // property: 'rules',
130
+ // parser: parsers.JSON,
131
+ // canonicalName: 'DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING',
132
+ // transformer: transformers.toCamelCase,
133
+ // },
134
+ // },
135
+ // 'cloudPayloadTagging.responses': {
136
+ // nestedProperties: [
137
+ // 'enabled',
138
+ // ],
139
+ // },
140
+ // 'cloudPayloadTagging.rules': {},
141
+ // 'cloudPayloadTagging.requestsEnabled': {},
142
+ // 'cloudPayloadTagging.responses.enabled': {}
143
+ const optionsTable = {
144
+ // Additional properties that are not supported by the supported-configurations.json file.
145
+ lookup: {
146
+ transformer (value) {
147
+ if (typeof value === 'function') {
148
+ return value
149
+ }
150
+ },
151
+ property: 'lookup',
152
+ },
153
+ logger: {
154
+ transformer (object) {
155
+ // Create lazily to avoid the overhead when not used.
156
+ // Match at least one log level.
157
+ const knownLogLevels = new Set(supportedConfigurations.DD_TRACE_LOG_LEVEL[0].allowed?.split('|'))
158
+ if (typeof object !== 'object' || object === null) {
159
+ return object
160
+ }
161
+ let matched = false
162
+ for (const logLevel of knownLogLevels) {
163
+ if (object[logLevel] !== undefined) {
164
+ if (typeof object[logLevel] !== 'function') {
165
+ warnInvalidValue(object[logLevel], 'logger', 'default', `Invalid log level ${logLevel}`)
166
+ return
63
167
  }
64
- continue
65
- }
66
- const key = item.slice(0, colonIndex).trim()
67
- const value = item.slice(colonIndex + 1).trim()
68
- if (key.length > 0) {
69
- entries[key] = value
168
+ matched = true
70
169
  }
71
170
  }
72
- return entries
73
- }
74
- default:
75
- return raw
171
+ if (matched) {
172
+ return object
173
+ }
174
+ },
175
+ property: 'logger',
176
+ },
177
+ isCiVisibility: {
178
+ property: 'isCiVisibility',
179
+ },
180
+ plugins: {
181
+ property: 'plugins',
182
+ },
183
+ }
184
+
185
+ const parser = (value, optionName, source) => {
186
+ const { type, canonicalName = optionName } = configurationsTable[optionName]
187
+ const parsed = parsers[type](value, canonicalName)
188
+ if (parsed === undefined) {
189
+ warnInvalidValue(value, optionName, source, `Invalid ${type} input`)
76
190
  }
191
+ return parsed
77
192
  }
78
193
 
79
- /** @type {Record<string, unknown>} */
80
- const metadataDefaults = {}
81
- for (const entries of Object.values(supportedConfigurations)) {
194
+ /**
195
+ * @template {import('./config-types').ConfigPath} TPath
196
+ * @type {Partial<Record<TPath, {
197
+ * property?: string,
198
+ * parser: (value: unknown, optionName: string, source: string) => unknown,
199
+ * canonicalName?: string,
200
+ * transformer?: (value: unknown, optionName: string, source: string) => unknown,
201
+ * telemetryTransformer?: (value: unknown) => unknown
202
+ * }>>} ConfigurationsTable
203
+ */
204
+ const configurationsTable = {}
205
+
206
+ // One way aliases. Must be applied in apply calculated entries.
207
+ const fallbackConfigurations = new Map()
208
+
209
+ const regExps = {}
210
+
211
+ for (const [canonicalName, entries] of Object.entries(supportedConfigurations)) {
212
+ if (entries.length !== 1) {
213
+ // TODO: Determine if we really want to support multiple entries for a canonical name.
214
+ // This would be needed to show official support for multiple diverging implementations
215
+ // at a time with by checking for another configuration that is not the canonical name.
216
+ throw new Error(
217
+ `Multiple entries found for canonical name: ${canonicalName}. ` +
218
+ 'This is currently not supported and must be implemented, if needed.'
219
+ )
220
+ }
82
221
  for (const entry of entries) {
83
- // TODO: Replace $dynamic with method names that would be called and that
84
- // are also called when the user passes through the value. That way the
85
- // handling is unified and methods can be declared as default.
86
- // The name of that method should be expressive for users.
87
- // TODO: Add handling for all environment variable names. They should not
88
- // need a configuration name for being listed with their default.
89
- if (!Array.isArray(entry.configurationNames)) {
90
- continue
222
+ const configurationNames = entry.internalPropertyName ? [entry.internalPropertyName] : entry.configurationNames
223
+ const fullPropertyName = configurationNames?.[0] ?? canonicalName
224
+ const type = entry.type.toUpperCase()
225
+
226
+ let transformer = transformers[entry.transform]
227
+ if (entry.allowed) {
228
+ regExps[entry.allowed] ??= new RegExp(`^(${entry.allowed})$`, 'i')
229
+ const allowed = regExps[entry.allowed]
230
+ const originalTransform = transformer
231
+ transformer = (value, optionName, source) => {
232
+ if (!allowed.test(value)) {
233
+ warnInvalidValue(value, optionName, source, 'Invalid value')
234
+ return
235
+ }
236
+ if (originalTransform) {
237
+ value = originalTransform(value)
238
+ }
239
+ return value
240
+ }
91
241
  }
92
242
 
93
- const parsedValue = parseDefaultByType(entry.default, entry.type)
94
- for (const configurationName of entry.configurationNames) {
95
- metadataDefaults[configurationName] = entry.default === null ? undefined : parsedValue
243
+ const option = { parser, type }
244
+
245
+ if (fullPropertyName !== canonicalName) {
246
+ option.property = fullPropertyName
247
+ option.canonicalName = canonicalName
248
+ configurationsTable[fullPropertyName] = option
249
+ }
250
+ if (transformer) {
251
+ option.transformer = transformer
252
+ }
253
+ if (entry.configurationNames) {
254
+ addOption(option, type, entry.configurationNames)
255
+ }
256
+ configurationsTable[canonicalName] = option
257
+
258
+ if (entry.default === null) {
259
+ defaults[fullPropertyName] = undefined
260
+ } else {
261
+ let parsedDefault = parser(entry.default, fullPropertyName, 'default')
262
+ if (entry.transform) {
263
+ parsedDefault = transformer(parsedDefault, fullPropertyName, 'default')
264
+ }
265
+ defaults[fullPropertyName] = parsedDefault
266
+ }
267
+ generateTelemetry(defaults[fullPropertyName], 'default', fullPropertyName)
268
+
269
+ if (entry.aliases) {
270
+ for (const alias of entry.aliases) {
271
+ if (!supportedConfigurations[alias]) {
272
+ // An actual alias has no matching entry
273
+ continue
274
+ }
275
+ if (!supportedConfigurations[alias].aliases?.includes(canonicalName)) {
276
+ // Alias will be replaced with the full property name of the alias, if it exists.
277
+ fallbackConfigurations.set(fullPropertyName, alias)
278
+ }
279
+ }
96
280
  }
97
281
  }
98
282
  }
99
283
 
100
- // Defaults required by JS config merge/applyCalculated that are not represented in supported-configurations.
101
- const defaultsWithoutSupportedConfigurationEntry = {
102
- 'cloudPayloadTagging.rules': [],
103
- 'cloudPayloadTagging.requestsEnabled': false,
104
- 'cloudPayloadTagging.responsesEnabled': false,
105
- isAzureFunction: false,
106
- isCiVisibility: false,
107
- isGCPFunction: false,
108
- instrumentationSource: 'manual',
109
- isServiceUserProvided: false,
110
- isServiceNameInferred: true,
111
- lookup: undefined,
112
- plugins: true,
284
+ // Replace the alias with the canonical property name.
285
+ for (const [fullPropertyName, alias] of fallbackConfigurations) {
286
+ if (configurationsTable[alias].property) {
287
+ fallbackConfigurations.set(fullPropertyName, configurationsTable[alias].property)
288
+ }
113
289
  }
114
290
 
115
- // These values are documented in supported-configurations as CI Visibility
116
- // defaults. Keep startup baseline false and let #applyCalculated() switch them
117
- // when CI Visibility is active.
118
- // TODO: These entries should be removed. They are off by default
119
- // because they rely on other configs.
120
- const defaultsWithConditionalRuntimeBehavior = {
121
- startupLogs: DD_MAJOR >= 6,
122
- isGitUploadEnabled: false,
123
- isImpactedTestsEnabled: false,
124
- isIntelligentTestRunnerEnabled: false,
125
- isManualApiEnabled: false,
126
- isTestManagementEnabled: false,
127
- // TODO: These are not conditional, they would just be of type number.
128
- 'dogstatsd.port': '8125',
129
- port: '8126',
130
- // Override due to expecting numbers, not strings. TODO: Replace later.
131
- 'grpc.client.error.statuses': [
132
- 1,
133
- 2,
134
- 3,
135
- 4,
136
- 5,
137
- 6,
138
- 7,
139
- 8,
140
- 9,
141
- 10,
142
- 11,
143
- 12,
144
- 13,
145
- 14,
146
- 15,
147
- 16,
148
- ],
149
- 'grpc.server.error.statuses': [
150
- 2,
151
- 3,
152
- 4,
153
- 5,
154
- 6,
155
- 7,
156
- 8,
157
- 9,
158
- 10,
159
- 11,
160
- 12,
161
- 13,
162
- 14,
163
- 15,
164
- 16,
165
- ],
166
- }
291
+ function addOption (option, type, configurationNames) {
292
+ for (const name of configurationNames) {
293
+ let index = -1
294
+ let lastNestedProperties
295
+ while (true) {
296
+ const nextIndex = name.indexOf('.', index + 1)
297
+ const intermediateName = nextIndex === -1 ? name : name.slice(0, nextIndex)
298
+ if (lastNestedProperties) {
299
+ lastNestedProperties.add(intermediateName.slice(index + 1))
300
+ }
167
301
 
168
- /** @type {Record<string, unknown>} */
169
- const defaults = {
170
- ...defaultsWithoutSupportedConfigurationEntry,
171
- ...metadataDefaults,
172
- ...defaultsWithConditionalRuntimeBehavior,
173
- service,
174
- version: pkg.version,
302
+ if (nextIndex === -1) {
303
+ if (optionsTable[name]) {
304
+ if (optionsTable[name].nestedProperties && !optionsTable[name].option) {
305
+ optionsTable[name].option = option
306
+ break
307
+ }
308
+ throw new Error(`Duplicate configuration name: ${name}`)
309
+ }
310
+ optionsTable[name] = option
311
+ break
312
+ }
313
+
314
+ lastNestedProperties = new Set()
315
+ index = nextIndex
316
+
317
+ if (!optionsTable[intermediateName]) {
318
+ optionsTable[intermediateName] = {
319
+ nestedProperties: lastNestedProperties,
320
+ }
321
+ } else if (optionsTable[intermediateName].nestedProperties) {
322
+ lastNestedProperties = optionsTable[intermediateName].nestedProperties
323
+ } else {
324
+ optionsTable[intermediateName] = {
325
+ nestedProperties: lastNestedProperties,
326
+ option: optionsTable[intermediateName],
327
+ }
328
+ }
329
+ }
330
+ }
175
331
  }
176
332
 
177
- module.exports = defaults
333
+ module.exports = {
334
+ configurationsTable,
335
+
336
+ defaults,
337
+
338
+ fallbackConfigurations,
339
+
340
+ optionsTable,
341
+
342
+ configWithOrigin,
343
+
344
+ parseErrors,
345
+
346
+ generateTelemetry,
347
+ }
@@ -88,10 +88,12 @@ export interface GeneratedConfig {
88
88
  DD_CIVISIBILITY_TEST_MODULE_ID: string | undefined;
89
89
  DD_CIVISIBILITY_TEST_SESSION_ID: string | undefined;
90
90
  DD_CUSTOM_TRACE_ID: string | undefined;
91
+ DD_ENABLE_LAGE_PACKAGE_NAME: boolean;
91
92
  DD_ENABLE_NX_SERVICE_NAME: boolean;
92
93
  DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_DIR: string;
93
94
  DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_ENABLED: boolean;
94
95
  DD_EXPERIMENTAL_TEST_OPT_SETTINGS_CACHE: string;
96
+ DD_EXPERIMENTAL_TEST_REQUESTS_FS_CACHE: boolean;
95
97
  DD_EXTERNAL_ENV: string | undefined;
96
98
  DD_GIT_BRANCH: string | undefined;
97
99
  DD_GIT_COMMIT_AUTHOR_DATE: string | undefined;
@@ -379,6 +381,7 @@ export interface GeneratedConfig {
379
381
  env: string | undefined;
380
382
  experimental: {
381
383
  aiguard: {
384
+ block: boolean;
382
385
  enabled: boolean;
383
386
  endpoint: string | undefined;
384
387
  maxContentSize: number;
@@ -475,7 +478,6 @@ export interface GeneratedConfig {
475
478
  };
476
479
  openAiLogsEnabled: boolean;
477
480
  OTEL_EXPORTER_OTLP_ENDPOINT: string | undefined;
478
- OTEL_LOG_LEVEL: "debug" | "info" | "warn" | "error" | undefined;
479
481
  OTEL_LOGS_EXPORTER: "none" | "otlp" | undefined;
480
482
  OTEL_METRICS_EXPORTER: "none" | "otlp" | undefined;
481
483
  OTEL_RESOURCE_ATTRIBUTES: Record<string, string>;
@@ -547,6 +549,7 @@ export interface GeneratedConfig {
547
549
  debug: boolean;
548
550
  dependencyCollection: boolean;
549
551
  enabled: boolean;
552
+ extendedHeartbeatInterval: number;
550
553
  heartbeatInterval: number;
551
554
  logCollection: boolean;
552
555
  metrics: boolean;
@@ -9,6 +9,9 @@
9
9
  * @property {string|number|boolean|null|object|unknown[]} default
10
10
  * @property {string[]} [aliases]
11
11
  * @property {string[]} [configurationNames]
12
+ * @property {string} [internalPropertyName]
13
+ * @property {string} [transform]
14
+ * @property {string} [allowed]
12
15
  * @property {string|boolean} [deprecated]
13
16
  */
14
17
 
@@ -57,6 +60,13 @@ for (const [canonical, configuration] of Object.entries(supportedConfigurations)
57
60
  const aliasToCanonical = {}
58
61
  for (const canonical of Object.keys(aliases)) {
59
62
  for (const alias of aliases[canonical]) {
63
+ if (supportedConfigurations[alias]) {
64
+ // Allow 'fallback' aliases to be used for other configurations.
65
+ // This is used to handle the case where an alias could be used for multiple configurations.
66
+ // For example, OTEL_EXPORTER_OTLP_ENDPOINT is used for OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
67
+ // and OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.
68
+ continue
69
+ }
60
70
  if (aliasToCanonical[alias]) {
61
71
  throw new Error(`The alias ${alias} is already used for ${aliasToCanonical[alias]}.`)
62
72
  }
@@ -99,22 +109,37 @@ function loadStableConfig () {
99
109
  }
100
110
 
101
111
  function getValueFromSource (name, source) {
102
- const value = source[name]
112
+ if (source[name] !== undefined) {
113
+ return source[name]
114
+ }
103
115
 
104
- if (value === undefined && aliases[name]) {
116
+ if (aliases[name]) {
105
117
  for (const alias of aliases[name]) {
106
118
  if (source[alias] !== undefined) {
107
119
  return source[alias]
108
120
  }
109
121
  }
110
122
  }
123
+ }
111
124
 
112
- return value
125
+ function getEnvNameFromSource (name, source) {
126
+ if (source[name] !== undefined) {
127
+ return name
128
+ }
129
+
130
+ if (aliases[name]) {
131
+ for (const alias of aliases[name]) {
132
+ if (source[alias] !== undefined) {
133
+ return alias
134
+ }
135
+ }
136
+ }
113
137
  }
114
138
 
115
139
  function validateAccess (name) {
116
- if ((name.startsWith('DD_') || name.startsWith('OTEL_') || aliasToCanonical[name]) &&
117
- !supportedConfigurations[name]) {
140
+ if ((name.startsWith('DD_') || name.startsWith('OTEL_')) &&
141
+ !supportedConfigurations[name] &&
142
+ !aliasToCanonical[name]) {
118
143
  throw new Error(`Missing ${name} env/configuration in "supported-configurations.json" file.`)
119
144
  }
120
145
  }
@@ -144,10 +169,9 @@ module.exports = {
144
169
  *
145
170
  * @returns {TracerEnv} The environment variables
146
171
  */
147
- getEnvironmentVariables () {
172
+ getEnvironmentVariables (source = process.env, internalOnly = false) {
148
173
  const configs = {}
149
- for (const [key, value] of Object.entries(process.env)) {
150
- // TODO(BridgeAR): Handle telemetry reporting for aliases.
174
+ for (const [key, value] of Object.entries(source)) {
151
175
  if (key.startsWith('DD_') || key.startsWith('OTEL_') || aliasToCanonical[key]) {
152
176
  if (supportedConfigurations[key]) {
153
177
  configs[key] = value
@@ -155,7 +179,7 @@ module.exports = {
155
179
  // The alias should only be used if the actual configuration is not set
156
180
  // In case that more than a single alias exist, use the one defined first in our own order
157
181
  for (const alias of aliases[aliasToCanonical[key]]) {
158
- if (process.env[alias] !== undefined) {
182
+ if (source[alias] !== undefined) {
159
183
  configs[aliasToCanonical[key]] = value
160
184
  break
161
185
  }
@@ -165,9 +189,10 @@ module.exports = {
165
189
  // debug(
166
190
  // `Missing configuration ${env} in supported-configurations file. The environment variable is ignored.`
167
191
  // )
192
+ // This could be moved inside the main config logic.
168
193
  }
169
194
  deprecationMethods[key]?.()
170
- } else {
195
+ } else if (!internalOnly) {
171
196
  configs[key] = value
172
197
  }
173
198
  }
@@ -211,4 +236,28 @@ module.exports = {
211
236
  return getValueFromSource(name, localStableConfig)
212
237
  }
213
238
  },
239
+
240
+ /**
241
+ * Returns the actual environment variable name used for a supported configuration
242
+ * from a specific environment-based source.
243
+ *
244
+ * @param {string} name Environment variable name
245
+ * @returns {string|undefined}
246
+ */
247
+ getConfiguredEnvName (name) {
248
+ validateAccess(name)
249
+
250
+ if (!stableConfigLoaded) {
251
+ loadStableConfig()
252
+ }
253
+
254
+ for (const source of [fleetStableConfig, process.env, localStableConfig]) {
255
+ if (source !== undefined) {
256
+ const fromSource = getEnvNameFromSource(name, source)
257
+ if (fromSource !== undefined) {
258
+ return fromSource
259
+ }
260
+ }
261
+ }
262
+ },
214
263
  }