dd-trace 6.9.0 → 6.11.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 (175) hide show
  1. package/README.md +0 -17
  2. package/ci/vitest-no-worker-init-setup.mjs +112 -10
  3. package/index.d.ts +150 -3
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +14 -10
  7. package/packages/datadog-esbuild/index.js +0 -34
  8. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  10. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  11. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  12. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
  13. package/packages/datadog-instrumentations/src/cucumber.js +26 -11
  14. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  15. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  16. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  17. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  18. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  20. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
  21. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  26. package/packages/datadog-instrumentations/src/jest.js +26 -18
  27. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  28. package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
  29. package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
  30. package/packages/datadog-instrumentations/src/mysql.js +45 -15
  31. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  32. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  33. package/packages/datadog-instrumentations/src/pg.js +116 -92
  34. package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
  35. package/packages/datadog-instrumentations/src/playwright.js +149 -2
  36. package/packages/datadog-instrumentations/src/router.js +2 -0
  37. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
  38. package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
  39. package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
  40. package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
  41. package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  45. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
  46. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  47. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  48. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  49. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  50. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  51. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  52. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  53. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
  54. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  55. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  56. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  57. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  58. package/packages/datadog-plugin-electron/src/index.js +2 -0
  59. package/packages/datadog-plugin-fs/src/index.js +1 -1
  60. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  61. package/packages/datadog-plugin-jest/src/index.js +11 -4
  62. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  63. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  64. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
  65. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  66. package/packages/datadog-plugin-mocha/src/index.js +606 -40
  67. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  68. package/packages/datadog-plugin-nats/src/index.js +1 -1
  69. package/packages/datadog-plugin-next/src/index.js +1 -19
  70. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  71. package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
  72. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  73. package/packages/datadog-plugin-pg/src/index.js +1 -0
  74. package/packages/datadog-plugin-playwright/src/index.js +42 -13
  75. package/packages/datadog-plugin-vitest/src/index.js +14 -20
  76. package/packages/datadog-plugin-ws/src/util.js +2 -1
  77. package/packages/datadog-webpack/index.js +0 -11
  78. package/packages/dd-trace/index.js +0 -1
  79. package/packages/dd-trace/src/agent/info.js +6 -5
  80. package/packages/dd-trace/src/aiguard/client.js +100 -1
  81. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  82. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  83. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  84. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  85. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  86. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  87. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  88. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  89. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  90. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  91. package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
  92. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  93. package/packages/dd-trace/src/carrier.js +356 -0
  94. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  95. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
  96. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
  97. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
  100. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  101. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  102. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  103. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  104. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
  105. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  106. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  107. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
  108. package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
  109. package/packages/dd-trace/src/config/index.js +13 -2
  110. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  111. package/packages/dd-trace/src/config/supported-configurations.json +24 -0
  112. package/packages/dd-trace/src/datastreams/context.js +6 -1
  113. package/packages/dd-trace/src/datastreams/index.js +2 -0
  114. package/packages/dd-trace/src/datastreams/manager.js +14 -3
  115. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  116. package/packages/dd-trace/src/datastreams/processor.js +16 -13
  117. package/packages/dd-trace/src/datastreams/size.js +5 -0
  118. package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
  119. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  120. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  121. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  122. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  123. package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
  124. package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
  125. package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
  126. package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
  127. package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
  128. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  129. package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
  130. package/packages/dd-trace/src/llmobs/index.js +34 -2
  131. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  132. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  133. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  134. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  135. package/packages/dd-trace/src/llmobs/util.js +78 -0
  136. package/packages/dd-trace/src/noop/proxy.js +3 -4
  137. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  138. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  139. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  140. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  141. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  142. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  143. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  145. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  146. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  147. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  148. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  149. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  150. package/packages/dd-trace/src/plugin_manager.js +5 -3
  151. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  152. package/packages/dd-trace/src/plugins/index.js +1 -0
  153. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
  154. package/packages/dd-trace/src/plugins/util/test.js +93 -14
  155. package/packages/dd-trace/src/plugins/util/web.js +56 -2
  156. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  157. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  158. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  159. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  160. package/packages/dd-trace/src/proxy.js +38 -29
  161. package/packages/dd-trace/src/ritm.js +9 -2
  162. package/packages/dd-trace/src/serverless.js +37 -0
  163. package/packages/dd-trace/src/span_format.js +1 -0
  164. package/packages/dd-trace/src/span_stats.js +30 -3
  165. package/packages/dd-trace/src/storage-channels.js +86 -0
  166. package/packages/dd-trace/src/tracer.js +9 -2
  167. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  168. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  169. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  170. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  171. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  172. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  173. package/packages/dd-trace/src/feature-registry.js +0 -29
  174. package/packages/dd-trace/src/openfeature/register.js +0 -35
  175. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -127,7 +127,7 @@ class TestWorkerCiVisibilityExporter {
127
127
  }
128
128
 
129
129
  /**
130
- * @param {() => void} [onDone]
130
+ * @param {(error?: Error) => void} [onDone]
131
131
  */
132
132
  flush (onDone) {
133
133
  if (!onDone) {
@@ -141,9 +141,11 @@ class TestWorkerCiVisibilityExporter {
141
141
  }
142
142
 
143
143
  let pendingWriters = this._telemetryWriter ? 4 : 3
144
- const onWriterFlushed = () => {
144
+ let flushError
145
+ const onWriterFlushed = (error) => {
146
+ flushError ||= error
145
147
  pendingWriters--
146
- if (pendingWriters === 0) onDone()
148
+ if (pendingWriters === 0) onDone(flushError)
147
149
  }
148
150
 
149
151
  this._writer.flush(onWriterFlushed)
@@ -21,7 +21,7 @@ class Writer {
21
21
  }
22
22
 
23
23
  /**
24
- * @param {() => void} [onDone]
24
+ * @param {(error?: Error) => void} [onDone]
25
25
  */
26
26
  flush (onDone) {
27
27
  const count = this._encoder.count()
@@ -69,8 +69,9 @@ class Writer {
69
69
 
70
70
  // child_process workers (jest default, cucumber)
71
71
  if (process.send) {
72
- process.send(payload, () => {
73
- onDone()
72
+ process.send(payload, (error) => {
73
+ if (error) log.error('Error sending message to parent process', error)
74
+ onDone(error)
74
75
  })
75
76
  return
76
77
  }
@@ -82,9 +83,12 @@ class Writer {
82
83
  parentPort.postMessage(payload)
83
84
  } catch (error) {
84
85
  log.error('Error posting message to parent port', error)
85
- } finally {
86
- onDone()
86
+ onDone(error)
87
+ return
87
88
  }
89
+ // postMessage has no acknowledgement callback. Completion means the
90
+ // message was accepted by the local port, not processed by the parent.
91
+ onDone()
88
92
  return
89
93
  }
90
94
 
@@ -34,10 +34,11 @@ function parseSkippableSuitesResponse (
34
34
  if (validateRequiredFields) {
35
35
  validateSkippableTestsResponse(parsedResponse, { validationMode })
36
36
  }
37
- const coverage = {}
37
+ let coverage
38
38
  const coverageByFilename = parsedResponse.meta?.coverage
39
39
  if (coverageByFilename) {
40
40
  for (const [filename, bitmap] of Object.entries(coverageByFilename)) {
41
+ coverage ??= {}
41
42
  coverage[filename.replaceAll('\\', '/')] = bitmap
42
43
  }
43
44
  }
@@ -0,0 +1,39 @@
1
+ 'use strict'
2
+
3
+ const EPOCH_SECONDS_THRESHOLD = 1_000_000_000
4
+
5
+ /**
6
+ * Returns the delay requested by a Test Optimization intake rate-limit response.
7
+ *
8
+ * @param {import('node:http').IncomingHttpHeaders} [headers]
9
+ * @returns {number}
10
+ */
11
+ function getRateLimitResetDelay (headers) {
12
+ let retryAfter = headers?.['retry-after']
13
+ if (Array.isArray(retryAfter)) retryAfter = retryAfter[0]
14
+ if (typeof retryAfter === 'string' && retryAfter.trim() !== '') {
15
+ const delaySeconds = Number(retryAfter)
16
+ if (Number.isFinite(delaySeconds)) {
17
+ if (delaySeconds >= 0) return delaySeconds * 1000
18
+ } else {
19
+ const resetTimestamp = Date.parse(retryAfter)
20
+ if (Number.isFinite(resetTimestamp)) return Math.max(0, resetTimestamp - Date.now())
21
+ }
22
+ }
23
+
24
+ let reset = headers?.['x-ratelimit-reset']
25
+ if (Array.isArray(reset)) reset = reset[0]
26
+ if (typeof reset !== 'string' || reset.trim() === '') return NaN
27
+
28
+ const resetSeconds = Number(reset)
29
+ if (!Number.isFinite(resetSeconds) || resetSeconds < 0) return NaN
30
+
31
+ // Datadog defines this header as delay seconds. Preserve compatibility with
32
+ // realistic Unix timestamps without confusing ordinary durations with epochs.
33
+ if (resetSeconds >= EPOCH_SECONDS_THRESHOLD) {
34
+ return Math.max(0, resetSeconds * 1000 - Date.now())
35
+ }
36
+ return resetSeconds * 1000
37
+ }
38
+
39
+ module.exports = { getRateLimitResetDelay }
@@ -13,12 +13,13 @@ const {
13
13
  singleJitteredDelay,
14
14
  } = require('../../exporters/common/retry')
15
15
  const { parseUrl } = require('../../exporters/common/url')
16
+ const { getRateLimitResetDelay } = require('./rate-limit')
16
17
 
17
18
  const legacyStorage = storage('legacy')
18
19
 
19
20
  /**
20
21
  * Simplified HTTP request for test optimization (library config). Uses common HTTP agents.
21
- * Retries: 429 (with X-ratelimit-reset, max 30s wait),
22
+ * Retries: 429 (with Retry-After or X-RateLimit-Reset, max 30s wait),
22
23
  * >=500 and transient network errors (5–7.5s delay with jitter). Max one retry.
23
24
  * Destroys connections on errors to prevent reuse of bad connections. Preserves
24
25
  * original status code across retries for telemetry.
@@ -98,11 +99,7 @@ function request (data, options, callback) {
98
99
  }
99
100
 
100
101
  if (res.statusCode === 429 && !hasRetried) {
101
- const resetHeader = res.headers['x-ratelimit-reset']
102
- const resetTs = (resetHeader === null || resetHeader === undefined)
103
- ? NaN
104
- : Number.parseInt(resetHeader, 10)
105
- const waitMs = Number.isFinite(resetTs) ? Math.max(0, resetTs * 1000 - Date.now()) : NaN
102
+ const waitMs = getRateLimitResetDelay(res.headers)
106
103
 
107
104
  if (Number.isFinite(waitMs) && waitMs <= RATE_LIMIT_MAX_WAIT_MS) {
108
105
  hasRetried = true
@@ -4,8 +4,8 @@ const { readFileSync } = require('node:fs')
4
4
  const { extname } = require('node:path')
5
5
 
6
6
  const getConfig = require('../../config')
7
- const request = require('../../exporters/common/request')
8
7
  const log = require('../../log')
8
+ const request = require('../exporters/request')
9
9
 
10
10
  const UPLOAD_TIMEOUT_MS = 30_000
11
11
  const TEST_SCREENSHOT_ENDPOINT_PREFIX = '/api/v2/ci/test-runs/'
@@ -74,10 +74,12 @@ function toIdempotencyQueryValue (idempotencyKey) {
74
74
  * @param {URL} options.url - The base URL for the screenshot upload
75
75
  * @param {boolean} [options.isEvpProxy] - Whether to upload through the Agent's evp_proxy
76
76
  * @param {string} [options.evpProxyPrefix] - The evp_proxy path prefix (e.g. '/evp_proxy/v4')
77
+ * @param {number} [options.deadline] - Absolute finalization deadline in epoch milliseconds
78
+ * @param {AbortSignal} [options.signal] - Signal used to cancel the upload
77
79
  * @param {Function} callback - Callback function (err)
78
80
  */
79
81
  function uploadTestScreenshot (
80
- { filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix },
82
+ { filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix, deadline, signal },
81
83
  callback
82
84
  ) {
83
85
  const { DD_API_KEY } = getConfig()
@@ -125,6 +127,8 @@ function uploadTestScreenshot (
125
127
  path: `${basePath}?${query}`,
126
128
  timeout: UPLOAD_TIMEOUT_MS,
127
129
  url,
130
+ deadline,
131
+ signal,
128
132
  }
129
133
 
130
134
  if (isEvpProxy) {
@@ -209,6 +209,7 @@ export interface GeneratedConfig {
209
209
  DD_TRACE_BEAUTIFUL_LOGS: boolean;
210
210
  DD_TRACE_BLUEBIRD_ENABLED: boolean;
211
211
  DD_TRACE_BODY_PARSER_ENABLED: boolean;
212
+ DD_TRACE_BROWSER_BUNYAN_ENABLED: boolean;
212
213
  DD_TRACE_BSON_ENABLED: boolean;
213
214
  DD_TRACE_BULLMQ_ENABLED: boolean;
214
215
  DD_TRACE_BUNYAN_ENABLED: boolean;
@@ -272,6 +273,7 @@ export interface GeneratedConfig {
272
273
  DD_TRACE_HAPI_HAPI_ENABLED: boolean;
273
274
  DD_TRACE_HONO_ENABLED: boolean;
274
275
  DD_TRACE_HTTP_ENABLED: boolean;
276
+ DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
275
277
  DD_TRACE_HTTP2_ENABLED: boolean;
276
278
  DD_TRACE_HTTPS_ENABLED: boolean;
277
279
  DD_TRACE_IOREDIS_ENABLED: boolean;
@@ -339,6 +341,7 @@ export interface GeneratedConfig {
339
341
  DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
340
342
  DD_TRACE_OPENTELEMETRY_SDK_TRACE_NODE_ENABLED: boolean;
341
343
  DD_TRACE_ORACLEDB_ENABLED: boolean;
344
+ DD_TRACE_OTEL_CTX_ENABLED: boolean;
342
345
  DD_TRACE_OTEL_ENABLED: boolean;
343
346
  DD_TRACE_OTEL_SEMANTICS_ENABLED: boolean;
344
347
  DD_TRACE_PASSPORT_ENABLED: boolean;
@@ -727,6 +730,7 @@ export interface GeneratedEnvVarConfig {
727
730
  DD_HEAP_SNAPSHOT_COUNT: number;
728
731
  DD_HEAP_SNAPSHOT_DESTINATION: string;
729
732
  DD_HEAP_SNAPSHOT_INTERVAL: number;
733
+ DD_HTTP_SERVER_ERROR_STATUSES: string;
730
734
  DD_IAST_DB_ROWS_TO_TAINT: number;
731
735
  DD_IAST_DEDUPLICATION_ENABLED: boolean;
732
736
  DD_IAST_ENABLED: boolean;
@@ -892,6 +896,7 @@ export interface GeneratedEnvVarConfig {
892
896
  DD_TRACE_BEAUTIFUL_LOGS: boolean;
893
897
  DD_TRACE_BLUEBIRD_ENABLED: boolean;
894
898
  DD_TRACE_BODY_PARSER_ENABLED: boolean;
899
+ DD_TRACE_BROWSER_BUNYAN_ENABLED: boolean;
895
900
  DD_TRACE_BSON_ENABLED: boolean;
896
901
  DD_TRACE_BULLMQ_ENABLED: boolean;
897
902
  DD_TRACE_BUNYAN_ENABLED: boolean;
@@ -966,6 +971,7 @@ export interface GeneratedEnvVarConfig {
966
971
  DD_TRACE_HEADER_TAGS: string[];
967
972
  DD_TRACE_HONO_ENABLED: boolean;
968
973
  DD_TRACE_HTTP_ENABLED: boolean;
974
+ DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
969
975
  DD_TRACE_HTTP2_ENABLED: boolean;
970
976
  DD_TRACE_HTTPS_ENABLED: boolean;
971
977
  DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED: boolean;
@@ -1037,6 +1043,7 @@ export interface GeneratedEnvVarConfig {
1037
1043
  DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
1038
1044
  DD_TRACE_OPENTELEMETRY_SDK_TRACE_NODE_ENABLED: boolean;
1039
1045
  DD_TRACE_ORACLEDB_ENABLED: boolean;
1046
+ DD_TRACE_OTEL_CTX_ENABLED: boolean;
1040
1047
  DD_TRACE_OTEL_ENABLED: boolean;
1041
1048
  DD_TRACE_OTEL_SEMANTICS_ENABLED: boolean;
1042
1049
  DD_TRACE_PARTIAL_FLUSH_MIN_SPANS: number;
@@ -15,6 +15,7 @@ const { isTrue } = require('../util')
15
15
  const telemetry = require('../telemetry')
16
16
  const telemetryMetrics = require('../telemetry/metrics')
17
17
  const {
18
+ getServerlessPlatformTags,
18
19
  IS_SERVERLESS,
19
20
  getIsGCPFunction,
20
21
  getIsAzureFunction,
@@ -477,9 +478,13 @@ class Config extends ConfigBase {
477
478
 
478
479
  // Apply the OTel sampler when the user opted into OTel traces or explicitly set the sampler.
479
480
  // OTEL_TRACES_SAMPLER has `default: parentbased_always_on` (per OTel spec), so opt-in users
480
- // that don't set the sampler still get parent-based sampling.
481
+ // that don't set the sampler still get parent-based sampling. Electron exporter spans go over
482
+ // the Electron SDK's IPC bridge rather than OTLP (see opentracing/tracer.js), so an
483
+ // OTEL_TRACES_EXPORTER=otlp set for an unrelated telemetry pipeline shouldn't also override
484
+ // dd-trace's own sampling policy in that case.
481
485
  if (!trackedConfigOrigins.has('sampleRate') &&
482
- (trackedConfigOrigins.has('OTEL_TRACES_SAMPLER') || this.OTEL_TRACES_EXPORTER === 'otlp')) {
486
+ (trackedConfigOrigins.has('OTEL_TRACES_SAMPLER') ||
487
+ (this.OTEL_TRACES_EXPORTER === 'otlp' && this.experimental.exporter !== 'electron'))) {
483
488
  setAndTrack(this, 'sampleRate',
484
489
  getFromOtelSamplerMap(this.OTEL_TRACES_SAMPLER, this.OTEL_TRACES_SAMPLER_ARG))
485
490
  }
@@ -597,6 +602,12 @@ class Config extends ConfigBase {
597
602
  this.tags.version = this.version
598
603
  }
599
604
  this.tags['runtime-id'] = RUNTIME_ID
605
+ const platformTags = getServerlessPlatformTags()
606
+ if (platformTags) {
607
+ for (let i = 0; i < platformTags.length; i += 2) {
608
+ this.tags[platformTags[i]] ??= platformTags[i + 1]
609
+ }
610
+ }
600
611
 
601
612
  if (IS_SERVERLESS) {
602
613
  setAndTrack(this, 'telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED', false)
@@ -58,6 +58,13 @@ function applyMajorOverrides (supportedConfigurations, majorVersion) {
58
58
  delete supportedConfigurations.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED
59
59
  // eslint-disable-next-line eslint-rules/eslint-env-aliases
60
60
  dropAlias(supportedConfigurations.DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED, 'DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED')
61
+
62
+ if (majorVersion >= 7) {
63
+ // The Electron plugin moved to the Electron SDK and is opt-in (disabled by default) from v7 on,
64
+ // while remaining enabled by default on earlier majors (see ElectronPlugin.optIn).
65
+ const electronEntry = supportedConfigurations.DD_TRACE_ELECTRON_ENABLED?.[0]
66
+ if (electronEntry) electronEntry.default = 'false'
67
+ }
61
68
  }
62
69
 
63
70
  /**
@@ -2452,6 +2452,13 @@
2452
2452
  "default": "true"
2453
2453
  }
2454
2454
  ],
2455
+ "DD_TRACE_BROWSER_BUNYAN_ENABLED": [
2456
+ {
2457
+ "implementation": "A",
2458
+ "type": "boolean",
2459
+ "default": "true"
2460
+ }
2461
+ ],
2455
2462
  "DD_TRACE_BSON_ENABLED": [
2456
2463
  {
2457
2464
  "implementation": "A",
@@ -2999,6 +3006,16 @@
2999
3006
  "default": "true"
3000
3007
  }
3001
3008
  ],
3009
+ "DD_TRACE_HTTP_SERVER_ERROR_STATUSES": [
3010
+ {
3011
+ "implementation": "A",
3012
+ "type": "string",
3013
+ "default": "500-599",
3014
+ "aliases": [
3015
+ "DD_HTTP_SERVER_ERROR_STATUSES"
3016
+ ]
3017
+ }
3018
+ ],
3002
3019
  "DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED": [
3003
3020
  {
3004
3021
  "implementation": "A",
@@ -3508,6 +3525,13 @@
3508
3525
  "default": "false"
3509
3526
  }
3510
3527
  ],
3528
+ "DD_TRACE_OTEL_CTX_ENABLED": [
3529
+ {
3530
+ "implementation": "A",
3531
+ "type": "boolean",
3532
+ "default": "false"
3533
+ }
3534
+ ],
3511
3535
  "DD_TRACE_PARTIAL_FLUSH_MIN_SPANS": [
3512
3536
  {
3513
3537
  "implementation": "B",
@@ -13,7 +13,12 @@ function getDataStreamsContext () {
13
13
  function setDataStreamsContext (dataStreamsContext) {
14
14
  log.debug('Setting new DSM Context: %j.', dataStreamsContext)
15
15
 
16
- if (dataStreamsContext) legacyStorage.enterWith({ ...legacyStorage.getStore(), dataStreamsContext })
16
+ // `undefined` clears: a message that carries no context must not inherit the previous message's.
17
+ // The identity check keeps the common no-context case from allocating a store per message.
18
+ const store = legacyStorage.getStore()
19
+ if (store?.dataStreamsContext !== dataStreamsContext) {
20
+ legacyStorage.enterWith({ ...store, dataStreamsContext })
21
+ }
17
22
  }
18
23
 
19
24
  module.exports = {
@@ -5,6 +5,7 @@ const {
5
5
  getHeadersSize,
6
6
  getMessageSize,
7
7
  getSizeOrZero,
8
+ PATHWAY_FIELD_BYTES,
8
9
  } = require('./size')
9
10
 
10
11
  // This is only needed because DSM code is spread across existing tracing
@@ -121,6 +122,7 @@ module.exports = {
121
122
  DataStreamsManager,
122
123
  DataStreamsProcessor,
123
124
  SchemaBuilder,
125
+ PATHWAY_FIELD_BYTES,
124
126
 
125
127
  // These are small functions so they are exposed directly and not lazy loaded.
126
128
  getAmqpMessageSize,
@@ -8,17 +8,28 @@ class DataStreamsManager {
8
8
  this._dataStreamsProcessor = processor
9
9
  }
10
10
 
11
- setCheckpoint (edgeTags, span, payloadSize = 0) {
11
+ /**
12
+ * @param {string[]} edgeTags
13
+ * @param {import('../opentracing/span')|null} span
14
+ * @param {number} [payloadSize]
15
+ * @param {number} [pathwayContextSize] See `DataStreamsProcessor#setCheckpoint`.
16
+ * @returns {object|undefined}
17
+ */
18
+ setCheckpoint (edgeTags, span, payloadSize = 0, pathwayContextSize) {
12
19
  const ctx = this._dataStreamsProcessor.setCheckpoint(
13
- edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize
20
+ edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize, pathwayContextSize
14
21
  )
15
22
  DataStreamsContext.setDataStreamsContext(ctx)
16
23
  return ctx
17
24
  }
18
25
 
26
+ /**
27
+ * @param {Record<string, string>|undefined} carrier Pass every message's carrier, including the
28
+ * ones without a context: that is what starts a new pathway instead of extending the previous
29
+ * message's.
30
+ */
19
31
  decodeDataStreamsContext (carrier) {
20
32
  const ctx = DsmPathwayCodec.decode(carrier)
21
- // we erase the previous context everytime we decode a new one
22
33
  DataStreamsContext.setDataStreamsContext(ctx)
23
34
  return ctx
24
35
  }
@@ -5,23 +5,25 @@
5
5
  // this inconsistency is ok because hashes do not need to be consistent across services
6
6
  const crypto = require('crypto')
7
7
  const { LRUCache } = require('../../../../vendor/dist/lru-cache')
8
+ const {
9
+ hasDsmBase64,
10
+ hasDsmBinary,
11
+ pickDsm,
12
+ readDsmBase64,
13
+ readDsmBinary,
14
+ writeDsmBase64,
15
+ } = require('../carrier')
8
16
  const log = require('../log')
9
- const pick = require('../../../datadog-core/src/utils/src/pick')
10
17
  const { encodeVarintInto, decodeVarint } = require('./encoding')
11
18
 
12
19
  const cache = new LRUCache({ max: 500 })
13
20
 
14
- const CONTEXT_PROPAGATION_KEY = 'dd-pathway-ctx'
15
- const CONTEXT_PROPAGATION_KEY_BASE64 = 'dd-pathway-ctx-base64'
16
-
17
21
  const PATHWAY_CONTEXT_BYTES = 20
18
22
 
19
23
  // Reused across `encodePathwayContext` calls; the buffer is fully rewritten before each
20
24
  // `Buffer.from(...)` copy-out so callers never observe mutation between checkpoints.
21
25
  const pathwayScratch = Buffer.allocUnsafe(PATHWAY_CONTEXT_BYTES)
22
26
 
23
- const logKeys = [CONTEXT_PROPAGATION_KEY, CONTEXT_PROPAGATION_KEY_BASE64]
24
-
25
27
  function shaHash (checkpointString) {
26
28
  // Copy out of the 32-byte digest so the LRU cache doesn't retain it.
27
29
  return Buffer.from(crypto.createHash('sha256').update(checkpointString).digest().subarray(0, 8))
@@ -114,7 +116,7 @@ function decodePathwayContext (pathwayContext) {
114
116
  }
115
117
 
116
118
  /**
117
- * @param {string} pathwayContext
119
+ * @param {string | Buffer} pathwayContext
118
120
  * @returns {ReturnType<typeof decodePathwayContext>|undefined}
119
121
  */
120
122
  function decodePathwayContextBase64 (pathwayContext) {
@@ -142,10 +144,10 @@ const DsmPathwayCodec = {
142
144
  encode (dataStreamsContext, carrier) {
143
145
  if (!dataStreamsContext || !dataStreamsContext.hash) return
144
146
  carrier ??= {}
145
- carrier[CONTEXT_PROPAGATION_KEY_BASE64] = encodePathwayContextBase64(dataStreamsContext)
147
+ writeDsmBase64(carrier, encodePathwayContextBase64(dataStreamsContext))
146
148
 
147
149
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
148
- log.debug(() => `Injected into DSM carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
150
+ log.debug(() => `Injected into DSM carrier: ${JSON.stringify(pickDsm(carrier))}.`)
149
151
 
150
152
  return carrier
151
153
  },
@@ -155,25 +157,25 @@ const DsmPathwayCodec = {
155
157
  * @returns {ReturnType<typeof decodePathwayContext>|undefined}
156
158
  */
157
159
  decode (carrier) {
158
- // eslint-disable-next-line eslint-rules/eslint-log-printf-style
159
- log.debug(() => `Attempting extract from DSM carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
160
-
161
160
  if (carrier == null) return
162
161
 
162
+ // eslint-disable-next-line eslint-rules/eslint-log-printf-style
163
+ log.debug(() => `Attempting extract from DSM carrier: ${JSON.stringify(pickDsm(carrier))}.`)
164
+
163
165
  let ctx
164
- if (CONTEXT_PROPAGATION_KEY_BASE64 in carrier) {
166
+ if (hasDsmBase64(carrier)) {
165
167
  // decode v2 encoding of base64
166
- ctx = decodePathwayContextBase64(carrier[CONTEXT_PROPAGATION_KEY_BASE64])
167
- } else if (CONTEXT_PROPAGATION_KEY in carrier) {
168
- try {
168
+ const encoded = readDsmBase64(carrier)
169
+ if (encoded !== undefined) ctx = decodePathwayContextBase64(encoded)
170
+ } else if (hasDsmBinary(carrier)) {
171
+ const encoded = readDsmBinary(carrier)
172
+ if (Buffer.isBuffer(encoded)) {
169
173
  // decode v1 encoding
170
- ctx = decodePathwayContext(carrier[CONTEXT_PROPAGATION_KEY])
171
- } catch {
172
- // pass
174
+ ctx = decodePathwayContext(encoded)
173
175
  }
174
176
  // cover case where base64 context was received under wrong key
175
- if (!ctx && CONTEXT_PROPAGATION_KEY in carrier) {
176
- ctx = decodePathwayContextBase64(carrier[CONTEXT_PROPAGATION_KEY])
177
+ if (!ctx && encoded !== undefined) {
178
+ ctx = decodePathwayContextBase64(encoded)
177
179
  }
178
180
  }
179
181
 
@@ -182,7 +184,6 @@ const DsmPathwayCodec = {
182
184
  }
183
185
 
184
186
  module.exports = {
185
- CONTEXT_PROPAGATION_KEY_BASE64,
186
187
  computePathwayHash: computeHash,
187
188
  encodePathwayContext,
188
189
  decodePathwayContext,
@@ -8,21 +8,14 @@ const { PATHWAY_HASH, DSM_TRANSACTION_ID, DSM_TRANSACTION_CHECKPOINT } = require
8
8
  const log = require('../log')
9
9
  const processTags = require('../process-tags')
10
10
  const propagationHash = require('../propagation-hash')
11
- const { CONTEXT_PROPAGATION_KEY_BASE64, computePathwayHash } = require('./pathway')
11
+ const { computePathwayHash } = require('./pathway')
12
12
  const { DataStreamsWriter } = require('./writer')
13
- const { getAmqpMessageSize, getHeadersSize, getMessageSize, getSizeOrZero } = require('./size')
13
+ const { getAmqpMessageSize, getHeadersSize, getMessageSize, getSizeOrZero, PATHWAY_FIELD_BYTES } = require('./size')
14
14
  const { SchemaBuilder } = require('./schemas/schema_builder')
15
15
  const { SchemaSampler } = require('./schemas/schema_sampler')
16
16
 
17
17
  const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
18
18
 
19
- // A direction:out checkpoint estimates the size cost of the header the
20
- // producer plugin will inject. The pathway context is always 20 binary
21
- // bytes, encoded as 28 base64 chars; together with the header key and
22
- // JSON framing (matching the prior `JSON.stringify({key: value})` byte
23
- // count minus 1), this is a fixed value.
24
- const PATHWAY_HEADER_BYTES = CONTEXT_PROPAGATION_KEY_BASE64.length + 28 + 6
25
-
26
19
  class StatsPoint {
27
20
  constructor (hash, parentHash, edgeTags) {
28
21
  this.hash = hash.readBigUInt64LE()
@@ -289,10 +282,19 @@ class DataStreamsProcessor {
289
282
  if (this.flushInterval === 0) this.onInterval()
290
283
  }
291
284
 
292
- setCheckpoint (edgeTags, span, ctx, payloadSize = 0) {
285
+ /**
286
+ * @param {string[]} edgeTags Direction tag first.
287
+ * @param {import('../opentracing/span')|null} span
288
+ * @param {object|null|undefined} ctx Parent pathway context.
289
+ * @param {number} [payloadSize] Bytes the caller built, before any propagation context.
290
+ * @param {number} [pathwayContextSize] Bytes the pathway context adds to an outbound payload.
291
+ * Most producers size their payload before this call hands them the context to inject, so they
292
+ * cannot measure it and take the default estimate. Pass `0` to report the payload alone.
293
+ * @returns {object|undefined}
294
+ */
295
+ setCheckpoint (edgeTags, span, ctx, payloadSize = 0, pathwayContextSize = PATHWAY_FIELD_BYTES) {
293
296
  if (!this.enabled) return
294
297
  const nowNs = Date.now() * 1e6
295
- // Callers must place the direction tag at index 0.
296
298
  const direction = edgeTags[0]
297
299
  let pathwayStartNs = nowNs
298
300
  let edgeStartNs = nowNs
@@ -337,14 +339,15 @@ class DataStreamsProcessor {
337
339
  const pathwayLatencyNs = nowNs - pathwayStartNs
338
340
  const dataStreamsContext = {
339
341
  hash,
340
- edgeStartNs,
342
+ // start the next hop's edge clock here so edge latency stays per-hop
343
+ edgeStartNs: nowNs,
341
344
  pathwayStartNs,
342
345
  previousDirection: direction,
343
346
  closestOppositeDirectionHash,
344
347
  closestOppositeDirectionEdgeStart,
345
348
  }
346
349
  if (direction === 'direction:out') {
347
- payloadSize += PATHWAY_HEADER_BYTES
350
+ payloadSize += pathwayContextSize
348
351
  }
349
352
  const checkpoint = {
350
353
  currentTimestamp: nowNs,
@@ -2,6 +2,10 @@
2
2
 
3
3
  const { types } = require('util')
4
4
 
5
+ // Byte cost of the `,"dd-pathway-ctx-base64":"<28 chars>"` field a producer adds to a JSON carrier.
6
+ // A literal so eager `size` consumers don't pull in `./pathway`; `pathway.spec.js` pins it there.
7
+ const PATHWAY_FIELD_BYTES = 55
8
+
5
9
  function getSizeOrZero (obj) {
6
10
  if (typeof obj === 'string') {
7
11
  return Buffer.byteLength(obj, 'utf8')
@@ -54,4 +58,5 @@ module.exports = {
54
58
  getHeadersSize,
55
59
  getSizeOrZero,
56
60
  getAmqpMessageSize,
61
+ PATHWAY_FIELD_BYTES,
57
62
  }
@@ -23,7 +23,6 @@ require('./remote_config')
23
23
 
24
24
  /** @typedef {import('node:inspector').Debugger.EvaluateOnCallFrameReturnType} EvaluateOnCallFrameResult */
25
25
 
26
- // Expression to run on a call frame of the paused thread to get its active trace and span id.
27
26
  const templateExpressionSetupCode = `
28
27
  const $dd_inspect = global.require('node:util').inspect;
29
28
  const $dd_segmentInspectOptions = {
@@ -34,8 +33,10 @@ const templateExpressionSetupCode = `
34
33
  breakLength: Infinity
35
34
  };
36
35
  `
36
+
37
+ // Expression to run on a call frame of the paused thread to get its active trace and span id.
37
38
  const getDDTagsExpression = `(() => {
38
- const context = global.require('dd-trace').scope().active()?.context();
39
+ const context = globalThis._ddtrace.scope().active()?.context();
39
40
  return { trace_id: context?.toTraceId(), span_id: context?.toSpanId() }
40
41
  })()`
41
42