dd-trace 5.86.0 → 5.88.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 (105) hide show
  1. package/LICENSE-3rdparty.csv +60 -32
  2. package/ext/exporters.d.ts +1 -0
  3. package/ext/exporters.js +1 -0
  4. package/index.d.ts +243 -7
  5. package/package.json +9 -6
  6. package/packages/datadog-instrumentations/src/ai.js +54 -90
  7. package/packages/datadog-instrumentations/src/cucumber.js +14 -0
  8. package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +55 -14
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +15 -13
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +21 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +138 -12
  17. package/packages/datadog-instrumentations/src/http/client.js +119 -1
  18. package/packages/datadog-instrumentations/src/jest.js +179 -15
  19. package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
  20. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
  21. package/packages/datadog-instrumentations/src/mysql2.js +131 -64
  22. package/packages/datadog-instrumentations/src/playwright.js +9 -1
  23. package/packages/datadog-instrumentations/src/stripe.js +92 -0
  24. package/packages/datadog-instrumentations/src/vitest.js +11 -0
  25. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
  26. package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
  27. package/packages/datadog-plugin-azure-functions/src/index.js +53 -37
  28. package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
  29. package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
  30. package/packages/datadog-plugin-cucumber/src/index.js +9 -6
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +33 -0
  32. package/packages/datadog-plugin-cypress/src/support.js +48 -8
  33. package/packages/datadog-plugin-jest/src/index.js +12 -2
  34. package/packages/datadog-plugin-jest/src/util.js +2 -1
  35. package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
  36. package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
  37. package/packages/datadog-plugin-mocha/src/index.js +9 -6
  38. package/packages/datadog-plugin-playwright/src/index.js +10 -6
  39. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  40. package/packages/dd-trace/src/appsec/addresses.js +11 -0
  41. package/packages/dd-trace/src/appsec/channels.js +5 -1
  42. package/packages/dd-trace/src/appsec/downstream_requests.js +302 -0
  43. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
  46. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
  47. package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
  51. package/packages/dd-trace/src/appsec/index.js +103 -0
  52. package/packages/dd-trace/src/appsec/rasp/ssrf.js +66 -4
  53. package/packages/dd-trace/src/azure_metadata.js +0 -2
  54. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +14 -1
  55. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  56. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -0
  57. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
  58. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
  59. package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
  60. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  61. package/packages/dd-trace/src/config/defaults.js +148 -195
  62. package/packages/dd-trace/src/config/helper.js +43 -1
  63. package/packages/dd-trace/src/config/index.js +42 -14
  64. package/packages/dd-trace/src/config/supported-configurations.json +4115 -510
  65. package/packages/dd-trace/src/constants.js +0 -2
  66. package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
  67. package/packages/dd-trace/src/datastreams/pathway.js +22 -3
  68. package/packages/dd-trace/src/datastreams/processor.js +14 -1
  69. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -2
  70. package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -23
  71. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +23 -1
  72. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +3 -3
  73. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +168 -36
  74. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +18 -0
  75. package/packages/dd-trace/src/encode/agentless-json.js +141 -0
  76. package/packages/dd-trace/src/exporter.js +2 -0
  77. package/packages/dd-trace/src/exporters/agent/writer.js +22 -8
  78. package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
  79. package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
  80. package/packages/dd-trace/src/exporters/common/agents.js +1 -1
  81. package/packages/dd-trace/src/exporters/common/request.js +4 -4
  82. package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
  83. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
  84. package/packages/dd-trace/src/llmobs/sdk.js +34 -5
  85. package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
  86. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
  87. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
  88. package/packages/dd-trace/src/opentracing/span.js +6 -4
  89. package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
  90. package/packages/dd-trace/src/plugins/database.js +57 -45
  91. package/packages/dd-trace/src/plugins/outbound.js +27 -2
  92. package/packages/dd-trace/src/plugins/tracing.js +39 -4
  93. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +7 -0
  94. package/packages/dd-trace/src/plugins/util/test.js +48 -0
  95. package/packages/dd-trace/src/plugins/util/web.js +8 -7
  96. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
  97. package/packages/dd-trace/src/propagation-hash/index.js +145 -0
  98. package/packages/dd-trace/src/proxy.js +4 -0
  99. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  100. package/packages/dd-trace/src/startup-log.js +3 -3
  101. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
  102. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
  103. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
  104. package/packages/dd-trace/src/plugins/util/serverless.js +0 -8
  105. package/packages/dd-trace/src/scope/noop/scope.js +0 -21
@@ -0,0 +1,236 @@
1
+ 'use strict'
2
+
3
+ const http = require('http')
4
+ const https = require('https')
5
+ const zlib = require('zlib')
6
+
7
+ const { storage } = require('../../../../datadog-core')
8
+ const log = require('../../log')
9
+ const { httpAgent, httpsAgent } = require('../../exporters/common/agents')
10
+ const { urlToHttpOptions } = require('../../exporters/common/url-to-http-options-polyfill')
11
+
12
+ const RATE_LIMIT_MAX_WAIT_MS = 30_000
13
+ const RETRY_BASE_MS = 5000
14
+ const RETRY_JITTER_MS = 2500
15
+
16
+ /**
17
+ * Calculates retry delay with jitter to prevent thundering herd.
18
+ * Delay is RETRY_BASE_MS + random(0, RETRY_JITTER_MS) (e.g. 5–7.5 seconds).
19
+ *
20
+ * @returns {number} Delay in milliseconds
21
+ */
22
+ function getRetryDelay () {
23
+ return RETRY_BASE_MS + (Math.random() * RETRY_JITTER_MS)
24
+ }
25
+
26
+ /**
27
+ * Determines if a network error is retriable (transient failures only).
28
+ * ECONNREFUSED is retried because it can be transient (service starting up,
29
+ * restarts, rolling deploys, k8s pod/readiness transitions). ENOTFOUND is
30
+ * excluded as it indicates DNS failure or wrong host and is usually not transient.
31
+ *
32
+ * @param {Error} err - The error to check
33
+ * @returns {boolean}
34
+ */
35
+ function isRetriableNetworkError (err) {
36
+ if (!err.code) return false
37
+ return err.code === 'ECONNREFUSED' ||
38
+ err.code === 'ECONNRESET' ||
39
+ err.code === 'ETIMEDOUT' ||
40
+ err.code === 'EPIPE'
41
+ }
42
+
43
+ function parseUrl (urlObjOrString) {
44
+ if (urlObjOrString !== null && typeof urlObjOrString === 'object') {
45
+ return urlToHttpOptions(urlObjOrString)
46
+ }
47
+
48
+ const url = urlToHttpOptions(new URL(urlObjOrString))
49
+
50
+ if (url.protocol === 'unix:' && url.hostname === '.') {
51
+ const udsPath = urlObjOrString.slice(5)
52
+ url.path = udsPath
53
+ url.pathname = udsPath
54
+ }
55
+
56
+ return url
57
+ }
58
+
59
+ /**
60
+ * Simplified HTTP request for test optimization (library config). Uses common HTTP agents.
61
+ * Retries: 429 (with X-ratelimit-reset, max 30s wait),
62
+ * >=500 and transient network errors (5–7.5s delay with jitter). Max one retry.
63
+ * Destroys connections on errors to prevent reuse of bad connections. Preserves
64
+ * original status code across retries for telemetry.
65
+ *
66
+ * @param {string} data - Request body (e.g. JSON string)
67
+ * @param {object} options - { url, path?, method?, headers?, timeout? } (may be mutated)
68
+ * @param {Function} callback - (err, res, statusCode) => void
69
+ */
70
+ function request (data, options, callback) {
71
+ const headers = options.headers ? { ...options.headers } : {}
72
+ headers['Content-Length'] = Buffer.byteLength(data, 'utf8')
73
+
74
+ const opts = { ...options, method: 'POST', headers }
75
+
76
+ if (opts.url) {
77
+ const url = parseUrl(opts.url)
78
+ if (url.protocol === 'unix:') {
79
+ opts.socketPath = url.pathname
80
+ } else {
81
+ opts.path = opts.path ?? url.path
82
+ opts.protocol = url.protocol
83
+ opts.hostname = url.hostname
84
+ opts.port = url.port
85
+ }
86
+ }
87
+
88
+ const timeout = opts.timeout || 2000
89
+ const isSecure = opts.protocol === 'https:'
90
+ const client = isSecure ? https : http
91
+
92
+ if (!opts.socketPath) {
93
+ opts.agent = isSecure ? httpsAgent : httpAgent
94
+ }
95
+
96
+ let hasRetried = false
97
+ let firstStatusCode = null
98
+
99
+ const makeRequest = () => {
100
+ storage('legacy').run({ noop: true }, () => {
101
+ const req = client.request(opts, (res) => {
102
+ // Capture non-2xx status code as soon as we see it so telemetry preserves it if the retry
103
+ // fails with a network error (no HTTP response) before 'end' fires
104
+ if (res.statusCode >= 400 && firstStatusCode === null) {
105
+ firstStatusCode = res.statusCode
106
+ }
107
+
108
+ const chunks = []
109
+
110
+ res.setTimeout(timeout)
111
+
112
+ res.on('data', chunk => {
113
+ chunks.push(chunk)
114
+ })
115
+
116
+ res.once('end', () => {
117
+ const buffer = Buffer.concat(chunks)
118
+
119
+ if (res.statusCode >= 200 && res.statusCode <= 299) {
120
+ const isGzip = res.headers['content-encoding'] === 'gzip'
121
+ if (isGzip) {
122
+ zlib.gunzip(buffer, (err, result) => {
123
+ if (err) {
124
+ log.error('Could not gunzip response: %s', err.message)
125
+ callback(null, '', res.statusCode)
126
+ } else {
127
+ callback(null, result.toString(), res.statusCode)
128
+ }
129
+ })
130
+ } else {
131
+ callback(null, buffer.toString(), res.statusCode)
132
+ }
133
+ return
134
+ }
135
+
136
+ if (res.statusCode === 429 && !hasRetried) {
137
+ const resetHeader = res.headers['x-ratelimit-reset']
138
+ const resetTs = (resetHeader === null || resetHeader === undefined)
139
+ ? Number.NaN
140
+ : Number.parseInt(resetHeader, 10)
141
+ const waitMs = Number.isFinite(resetTs) ? Math.max(0, resetTs * 1000 - Date.now()) : Number.NaN
142
+
143
+ if (Number.isFinite(waitMs) && waitMs <= RATE_LIMIT_MAX_WAIT_MS) {
144
+ hasRetried = true
145
+ setTimeout(makeRequest, waitMs)
146
+ return
147
+ }
148
+
149
+ if (!Number.isFinite(waitMs) || waitMs > RATE_LIMIT_MAX_WAIT_MS) {
150
+ log.debug('Rate limited (429): drop payload (wait %sms > %sms or invalid header)',
151
+ Number.isFinite(waitMs) ? waitMs : 'N/A', RATE_LIMIT_MAX_WAIT_MS)
152
+ }
153
+ } else if (res.statusCode >= 500 && !hasRetried) {
154
+ try {
155
+ if (req.socket) req.socket.destroy()
156
+ } catch {
157
+ // ignore
158
+ }
159
+ hasRetried = true
160
+ setTimeout(makeRequest, getRetryDelay())
161
+ return
162
+ }
163
+
164
+ const error = buildError(res, buffer, opts)
165
+ // Use original status code if this is a failed retry
166
+ callback(error, null, firstStatusCode === null ? res.statusCode : firstStatusCode)
167
+ })
168
+ })
169
+
170
+ req.once('error', err => {
171
+ try {
172
+ if (req.socket) req.socket.destroy()
173
+ } catch {
174
+ // ignore
175
+ }
176
+
177
+ // Retry on retriable network errors
178
+ if (!hasRetried && isRetriableNetworkError(err)) {
179
+ hasRetried = true
180
+ setTimeout(makeRequest, getRetryDelay())
181
+ return
182
+ }
183
+
184
+ // Pass original status code (if any) for accurate telemetry
185
+ callback(err, null, firstStatusCode)
186
+ })
187
+
188
+ req.setTimeout(timeout, () => {
189
+ try {
190
+ if (typeof req.abort === 'function') {
191
+ req.abort()
192
+ } else {
193
+ req.destroy()
194
+ }
195
+ } catch {
196
+ // ignore
197
+ }
198
+ })
199
+
200
+ req.write(data, 'utf8')
201
+ req.end()
202
+ })
203
+ }
204
+
205
+ makeRequest()
206
+ }
207
+
208
+ /**
209
+ * @param {object} res - IncomingMessage
210
+ * @param {Buffer} buffer - Response body
211
+ * @param {object} options - Request options
212
+ * @returns {Error}
213
+ */
214
+ function buildError (res, buffer, options) {
215
+ let errorMessage = ''
216
+ try {
217
+ const fullUrl = new URL(
218
+ options.path,
219
+ options.url || options.hostname || `http://localhost:${options.port}`
220
+ ).href
221
+ errorMessage = `Error from ${fullUrl}: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}.`
222
+ } catch {
223
+ // ignore
224
+ }
225
+
226
+ const responseData = buffer.toString()
227
+ if (responseData) {
228
+ errorMessage += ` Response from the endpoint: "${responseData}"`
229
+ }
230
+
231
+ const error = new log.NoTransmitError(errorMessage)
232
+ error.status = res.statusCode
233
+ return error
234
+ }
235
+
236
+ module.exports = request
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const request = require('../../exporters/common/request')
3
+ const request = require('../requests/request')
4
4
  const id = require('../../id')
5
5
  const { getValueFromEnvSources } = require('../../config/helper')
6
6
  const log = require('../../log')
@@ -1,15 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const pkg = require('../pkg')
4
- const { GRPC_CLIENT_ERROR_STATUSES, GRPC_SERVER_ERROR_STATUSES } = require('../constants')
4
+ const { isFalse, isTrue } = require('../util')
5
5
  const { getEnvironmentVariable: getEnv } = require('./helper')
6
6
 
7
- // eslint-disable-next-line @stylistic/max-len
8
- const qsRegex = String.raw`(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\s|%20)*(?:=|%3D)[^&]+|(?:"|%22)(?:\s|%20)*(?::|%3A)(?:\s|%20)*(?:"|%22)(?:%2[^2]|%[^2]|[^"%])+(?:"|%22))|bearer(?:\s|%20)+[a-z0-9\._\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\w=-]|%3D)+\.ey[I-L](?:[\w=-]|%3D)+(?:\.(?:[\w.+\/=-]|%3D|%2F|%2B)+)?|[\-]{5}BEGIN(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY[\-]{5}[^\-]+[\-]{5}END(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY|ssh-rsa(?:\s|%20)*(?:[a-z0-9\/\.+]|%2F|%5C|%2B){100,}`
9
- // eslint-disable-next-line @stylistic/max-len
10
- const defaultWafObfuscatorKeyRegex = String.raw`(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\.net[_-]sessionid|sid|jwt`
11
- // eslint-disable-next-line @stylistic/max-len
12
- const defaultWafObfuscatorValueRegex = String.raw`(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\.net(?:[_-]|-)sessionid|sid|jwt)(?:\s*=([^;&]+)|"\s*:\s*("[^"]+"|\d+))|bearer\s+([a-z0-9\._\-]+)|token\s*:\s*([a-z0-9]{13})|gh[opsu]_([0-9a-zA-Z]{36})|ey[I-L][\w=-]+\.(ey[I-L][\w=-]+(?:\.[\w.+\/=-]+)?)|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY[\-]{5}([^\-]+)[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*([a-z0-9\/\.+]{100,})`
7
+ const {
8
+ supportedConfigurations,
9
+ } = /** @type {import('./helper').SupportedConfigurationsJson} */ (require('./supported-configurations.json'))
13
10
 
14
11
  const service = getEnv('AWS_LAMBDA_FUNCTION_NAME') ||
15
12
  getEnv('FUNCTION_NAME') || // Google Cloud Function Name set by deprecated runtimes
@@ -18,204 +15,160 @@ const service = getEnv('AWS_LAMBDA_FUNCTION_NAME') ||
18
15
  pkg.name ||
19
16
  'node'
20
17
 
21
- module.exports = {
22
- apiKey: undefined,
23
- appKey: undefined,
24
- apmTracingEnabled: true,
25
- 'appsec.apiSecurity.enabled': true,
26
- 'appsec.apiSecurity.sampleDelay': 30,
27
- 'appsec.apiSecurity.endpointCollectionEnabled': true,
28
- 'appsec.apiSecurity.endpointCollectionMessageLimit': 300,
29
- 'appsec.blockedTemplateGraphql': undefined,
30
- 'appsec.blockedTemplateHtml': undefined,
31
- 'appsec.blockedTemplateJson': undefined,
32
- 'appsec.enabled': undefined,
33
- 'appsec.eventTracking.mode': 'identification',
34
- // TODO appsec.extendedHeadersCollection is deprecated, to delete in a major
35
- 'appsec.extendedHeadersCollection.enabled': false,
36
- 'appsec.extendedHeadersCollection.redaction': true,
37
- 'appsec.extendedHeadersCollection.maxHeaders': 50,
38
- 'appsec.obfuscatorKeyRegex': defaultWafObfuscatorKeyRegex,
39
- 'appsec.obfuscatorValueRegex': defaultWafObfuscatorValueRegex,
40
- 'appsec.rasp.enabled': true,
41
- // TODO Deprecated, to delete in a major
42
- 'appsec.rasp.bodyCollection': false,
43
- 'appsec.rateLimit': 100,
44
- 'appsec.rules': undefined,
45
- 'appsec.sca.enabled': null,
46
- 'appsec.stackTrace.enabled': true,
47
- 'appsec.stackTrace.maxDepth': 32,
48
- 'appsec.stackTrace.maxStackTraces': 2,
49
- 'appsec.wafTimeout': 5e3, // µs
50
- baggageMaxBytes: 8192,
51
- baggageMaxItems: 64,
52
- baggageTagKeys: 'user.id,session.id,account.id',
53
- clientIpEnabled: false,
54
- clientIpHeader: null,
18
+ /**
19
+ * @param {string|null} raw
20
+ * @param {string} type
21
+ * @returns {string|number|boolean|Record<string, string>|unknown[]|undefined}
22
+ */
23
+ function parseDefaultByType (raw, type) {
24
+ if (raw === null) {
25
+ return
26
+ }
27
+
28
+ switch (type) {
29
+ case 'boolean':
30
+ if (isTrue(raw)) return true
31
+ if (isFalse(raw)) return false
32
+ // TODO: What should we do with these?
33
+ return
34
+ case 'int':
35
+ case 'decimal': {
36
+ return Number(raw)
37
+ }
38
+ case 'array': {
39
+ if (!raw || raw.length === 0) return []
40
+ // TODO: Make the parsing a helper that is reused.
41
+ return raw.split(',').map(item => {
42
+ const colonIndex = item.indexOf(':')
43
+ if (colonIndex === -1) {
44
+ return item.trim()
45
+ }
46
+ const key = item.slice(0, colonIndex).trim()
47
+ const value = item.slice(colonIndex + 1).trim()
48
+ return `${key}:${value}`
49
+ })
50
+ }
51
+ case 'map': {
52
+ if (!raw || raw.length === 0) return {}
53
+ // TODO: Make the parsing a helper that is reused.
54
+ /** @type {Record<string, string>} */
55
+ const entries = {}
56
+ for (const item of raw.split(',')) {
57
+ const colonIndex = item.indexOf(':')
58
+ if (colonIndex === -1) {
59
+ const key = item.trim()
60
+ if (key.length > 0) {
61
+ entries[key] = ''
62
+ }
63
+ continue
64
+ }
65
+ const key = item.slice(0, colonIndex).trim()
66
+ const value = item.slice(colonIndex + 1).trim()
67
+ if (key.length > 0) {
68
+ entries[key] = value
69
+ }
70
+ }
71
+ return entries
72
+ }
73
+ default:
74
+ return raw
75
+ }
76
+ }
77
+
78
+ /** @type {Record<string, unknown>} */
79
+ const metadataDefaults = {}
80
+ for (const entries of Object.values(supportedConfigurations)) {
81
+ for (const entry of entries) {
82
+ // TODO: Replace $dynamic with method names that would be called and that
83
+ // are also called when the user passes through the value. That way the
84
+ // handling is unified and methods can be declared as default.
85
+ // The name of that method should be expressive for users.
86
+ // TODO: Add handling for all environment variable names. They should not
87
+ // need a configuration name for being listed with their default.
88
+ if (!Array.isArray(entry.configurationNames)) {
89
+ continue
90
+ }
91
+
92
+ const parsedValue = parseDefaultByType(entry.default, entry.type)
93
+ for (const configurationName of entry.configurationNames) {
94
+ metadataDefaults[configurationName] = entry.default === null ? undefined : parsedValue
95
+ }
96
+ }
97
+ }
98
+
99
+ // Defaults required by JS config merge/applyCalculated that are not represented in supported-configurations.
100
+ const defaultsWithoutSupportedConfigurationEntry = {
101
+ 'cloudPayloadTagging.rules': [],
55
102
  'cloudPayloadTagging.requestsEnabled': false,
56
103
  'cloudPayloadTagging.responsesEnabled': false,
57
- 'cloudPayloadTagging.maxDepth': 10,
58
- 'cloudPayloadTagging.rules': [],
59
- 'crashtracking.enabled': true,
60
- 'codeOriginForSpans.enabled': true,
61
- 'codeOriginForSpans.experimental.exit_spans.enabled': false,
62
- dbmPropagationMode: 'disabled',
63
- 'dogstatsd.hostname': '127.0.0.1',
64
- 'dogstatsd.port': '8125',
65
- dsmEnabled: false,
66
- 'dynamicInstrumentation.captureTimeoutMs': 15,
67
- 'dynamicInstrumentation.enabled': false,
68
- 'dynamicInstrumentation.probeFile': undefined,
69
- 'dynamicInstrumentation.redactedIdentifiers': [],
70
- 'dynamicInstrumentation.redactionExcludedIdentifiers': [],
71
- 'dynamicInstrumentation.uploadIntervalSeconds': 1,
72
- env: undefined,
73
- 'experimental.aiguard.enabled': false,
74
- 'experimental.aiguard.endpoint': undefined,
75
- 'experimental.aiguard.maxMessagesLength': 16,
76
- 'experimental.aiguard.maxContentSize': 512 * 1024,
77
- 'experimental.aiguard.timeout': 10_000, // ms
78
- 'experimental.enableGetRumData': false,
79
- 'experimental.exporter': undefined,
80
- 'experimental.flaggingProvider.enabled': false,
81
- 'experimental.flaggingProvider.initializationTimeoutMs': 30_000,
82
- flushInterval: 2000,
83
- flushMinSpans: 1000,
84
- gitMetadataEnabled: true,
85
- graphqlErrorExtensions: [],
86
- 'grpc.client.error.statuses': GRPC_CLIENT_ERROR_STATUSES,
87
- 'grpc.server.error.statuses': GRPC_SERVER_ERROR_STATUSES,
88
- headerTags: [],
89
- 'heapSnapshot.count': 0,
90
- 'heapSnapshot.destination': '',
91
- 'heapSnapshot.interval': 3600,
92
- hostname: '127.0.0.1',
93
- 'iast.dbRowsToTaint': 1,
94
- 'iast.deduplicationEnabled': true,
95
- 'iast.enabled': false,
96
- 'iast.maxConcurrentRequests': 2,
97
- 'iast.maxContextOperations': 2,
98
- 'iast.redactionEnabled': true,
99
- 'iast.redactionNamePattern': null,
100
- 'iast.redactionValuePattern': null,
101
- 'iast.requestSampling': 30,
102
- 'iast.securityControlsConfiguration': null,
103
- 'iast.telemetryVerbosity': 'INFORMATION',
104
- 'iast.stackTrace.enabled': true,
105
- injectionEnabled: [],
106
- 'installSignature.id': null,
107
- 'installSignature.time': null,
108
- 'installSignature.type': null,
109
- instrumentationSource: 'manual',
110
- injectForce: null,
111
104
  isAzureFunction: false,
112
105
  isCiVisibility: false,
113
- isEarlyFlakeDetectionEnabled: false,
114
- isFlakyTestRetriesEnabled: false,
115
- flakyTestRetriesCount: 5,
116
106
  isGCPFunction: false,
117
- isGCPPubSubPushSubscriptionEnabled: true,
107
+ instrumentationSource: 'manual',
108
+ isServiceUserProvided: false,
109
+ lookup: undefined,
110
+ plugins: true,
111
+ }
112
+
113
+ // These values are documented in supported-configurations as CI Visibility
114
+ // defaults. Keep startup baseline false and let #applyCalculated() switch them
115
+ // when CI Visibility is active.
116
+ // TODO: These entries should be removed. They are off by default
117
+ // because they rely on other configs.
118
+ const defaultsWithConditionalRuntimeBehavior = {
118
119
  isGitUploadEnabled: false,
120
+ isImpactedTestsEnabled: false,
119
121
  isIntelligentTestRunnerEnabled: false,
120
122
  isManualApiEnabled: false,
121
- 'langchain.spanCharLimit': 128,
122
- 'langchain.spanPromptCompletionSampleRate': 1,
123
- 'llmobs.agentlessEnabled': undefined,
124
- 'llmobs.enabled': false,
125
- 'llmobs.mlApp': undefined,
126
- ciVisibilityTestSessionName: '',
127
- ciVisAgentlessLogSubmissionEnabled: false,
128
- legacyBaggageEnabled: true,
129
- isTestDynamicInstrumentationEnabled: false,
130
- isServiceUserProvided: false,
131
- testManagementAttemptToFixRetries: 20,
132
123
  isTestManagementEnabled: false,
133
- isImpactedTestsEnabled: false,
134
- logInjection: true,
135
- otelLogsEnabled: false,
136
- otelUrl: undefined,
137
- otelLogsUrl: undefined, // Will be computed using agent host
138
- otelHeaders: undefined,
139
- otelLogsHeaders: '',
140
- otelProtocol: 'http/protobuf',
141
- otelLogsProtocol: 'http/protobuf',
142
- otelLogsTimeout: 10_000,
143
- otelTimeout: 10_000,
144
- otelBatchTimeout: 5000,
145
- otelMaxExportBatchSize: 512,
146
- otelMaxQueueSize: 2048,
147
- otelMetricsEnabled: false,
148
- otelMetricsUrl: undefined, // Will be computed using agent host
149
- otelMetricsHeaders: '',
150
- otelMetricsProtocol: 'http/protobuf',
151
- otelMetricsTimeout: 10_000,
152
- otelMetricsExportTimeout: 7500,
153
- otelMetricsExportInterval: 10_000,
154
- otelMetricsTemporalityPreference: 'DELTA', // DELTA, CUMULATIVE, or LOWMEMORY
155
- lookup: undefined,
156
- inferredProxyServicesEnabled: false,
157
- memcachedCommandEnabled: false,
158
- middlewareTracingEnabled: true,
159
- openAiLogsEnabled: false,
160
- 'openai.spanCharLimit': 128,
161
- peerServiceMapping: {},
162
- plugins: true,
124
+ // TODO: These are not conditional, they would just be of type number.
125
+ 'dogstatsd.port': '8125',
163
126
  port: '8126',
164
- 'profiling.enabled': undefined,
165
- 'propagateProcessTags.enabled': undefined,
166
- 'profiling.exporters': 'agent',
167
- 'profiling.sourceMap': true,
168
- 'profiling.longLivedThreshold': undefined,
169
- protocolVersion: '0.4',
170
- queryStringObfuscation: qsRegex,
171
- 'remoteConfig.enabled': true,
172
- 'remoteConfig.pollInterval': 5, // seconds
173
- reportHostname: false,
174
- resourceRenamingEnabled: false,
175
- 'runtimeMetrics.enabled': false,
176
- 'runtimeMetrics.eventLoop': true,
177
- 'runtimeMetrics.gc': true,
178
- runtimeMetricsRuntimeId: false,
179
- sampleRate: undefined,
180
- 'sampler.rateLimit': 100,
181
- 'sampler.rules': [],
182
- 'sampler.spanSamplingRules': [],
183
- scope: undefined,
127
+ // Override due to expecting numbers, not strings. TODO: Replace later.
128
+ 'grpc.client.error.statuses': [
129
+ 1,
130
+ 2,
131
+ 3,
132
+ 4,
133
+ 5,
134
+ 6,
135
+ 7,
136
+ 8,
137
+ 9,
138
+ 10,
139
+ 11,
140
+ 12,
141
+ 13,
142
+ 14,
143
+ 15,
144
+ 16,
145
+ ],
146
+ 'grpc.server.error.statuses': [
147
+ 2,
148
+ 3,
149
+ 4,
150
+ 5,
151
+ 6,
152
+ 7,
153
+ 8,
154
+ 9,
155
+ 10,
156
+ 11,
157
+ 12,
158
+ 13,
159
+ 14,
160
+ 15,
161
+ 16,
162
+ ],
163
+ }
164
+
165
+ /** @type {Record<string, unknown>} */
166
+ const defaults = {
167
+ ...defaultsWithoutSupportedConfigurationEntry,
168
+ ...metadataDefaults,
169
+ ...defaultsWithConditionalRuntimeBehavior,
184
170
  service,
185
- serviceMapping: {},
186
- site: 'datadoghq.com',
187
- spanAttributeSchema: 'v0',
188
- spanComputePeerService: false,
189
- spanLeakDebug: 0,
190
- spanRemoveIntegrationFromService: false,
191
- startupLogs: false,
192
- 'stats.enabled': false,
193
- tags: {},
194
- tagsHeaderMaxLength: 512,
195
- 'telemetry.debug': false,
196
- 'telemetry.dependencyCollection': true,
197
- 'telemetry.enabled': true,
198
- 'telemetry.heartbeatInterval': 60_000,
199
- 'telemetry.logCollection': true,
200
- 'telemetry.metrics': true,
201
- traceEnabled: true,
202
- traceId128BitGenerationEnabled: true,
203
- traceId128BitLoggingEnabled: true,
204
- tracePropagationExtractFirst: false,
205
- tracePropagationBehaviorExtract: 'continue',
206
- 'tracePropagationStyle.inject': ['datadog', 'tracecontext', 'baggage'],
207
- 'tracePropagationStyle.extract': ['datadog', 'tracecontext', 'baggage'],
208
- 'tracePropagationStyle.otelPropagators': false,
209
- traceWebsocketMessagesEnabled: true,
210
- traceWebsocketMessagesInheritSampling: true,
211
- traceWebsocketMessagesSeparateTraces: true,
212
- tracing: true,
213
- url: undefined,
214
171
  version: pkg.version,
215
- instrumentation_config_id: undefined,
216
- 'vertexai.spanCharLimit': 128,
217
- 'vertexai.spanPromptCompletionSampleRate': 1,
218
- 'trace.aws.addSpanPointers': true,
219
- 'trace.dynamoDb.tablePrimaryKeys': undefined,
220
- 'trace.nativeSpanEvents': false,
221
172
  }
173
+
174
+ module.exports = defaults
@@ -2,8 +2,25 @@
2
2
 
3
3
  /* eslint-disable eslint-rules/eslint-process-env */
4
4
 
5
+ /**
6
+ * @typedef {object} SupportedConfigurationEntry
7
+ * @property {string} implementation
8
+ * @property {string} type
9
+ * @property {string|number|boolean|null|object|unknown[]} default
10
+ * @property {string[]} [aliases]
11
+ * @property {string[]} [configurationNames]
12
+ * @property {string|boolean} [deprecated]
13
+ */
14
+
15
+ /**
16
+ * @typedef {object} SupportedConfigurationsJson
17
+ * @property {Record<`DD_${string}` | `OTEL_${string}`, SupportedConfigurationEntry[]>} supportedConfigurations
18
+ */
19
+
5
20
  const { deprecate } = require('util')
6
- const { supportedConfigurations, aliases, deprecations } = require('./supported-configurations.json')
21
+ const {
22
+ supportedConfigurations,
23
+ } = /** @type {SupportedConfigurationsJson} */ (require('./supported-configurations.json'))
7
24
 
8
25
  /**
9
26
  * Types for environment variable handling.
@@ -12,6 +29,31 @@ const { supportedConfigurations, aliases, deprecations } = require('./supported-
12
29
  * @typedef {Partial<typeof process.env> & Partial<Record<SupportedEnvKey, string|undefined>>} TracerEnv
13
30
  */
14
31
 
32
+ // Backwards-compatible views for old helper logic:
33
+ // - `aliases`: Record<canonicalEnvVar, string[]>
34
+ // - `deprecations`: Record<deprecatedEnvVar, string> (message suffix)
35
+ const aliases = {}
36
+ const deprecations = {}
37
+
38
+ for (const [canonical, configuration] of Object.entries(supportedConfigurations)) {
39
+ for (const implementation of configuration) {
40
+ if (implementation.deprecated) {
41
+ deprecations[canonical] = implementation.deprecated
42
+ // Deprecated entries with an alias may not be listed in the supported configurations map
43
+ if (implementation.aliases) {
44
+ delete supportedConfigurations[canonical]
45
+ continue
46
+ }
47
+ }
48
+ if (Array.isArray(implementation.aliases)) {
49
+ for (const alias of implementation.aliases) {
50
+ aliases[canonical] ??= new Set()
51
+ aliases[canonical].add(alias)
52
+ }
53
+ }
54
+ }
55
+ }
56
+
15
57
  const aliasToCanonical = {}
16
58
  for (const canonical of Object.keys(aliases)) {
17
59
  for (const alias of aliases[canonical]) {