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
@@ -1,7 +1,39 @@
1
1
  'use strict'
2
2
 
3
3
  const { channel } = require('dc-polyfill')
4
- const pick = require('../../../../datadog-core/src/utils/src/pick')
4
+ const {
5
+ pickTextMap,
6
+ readBaggage,
7
+ readB3,
8
+ readB3Flags,
9
+ readB3Sampled,
10
+ readB3SpanId,
11
+ readB3TraceId,
12
+ readDatadogOrigin,
13
+ readDatadogParentId,
14
+ readDatadogSamplingPriority,
15
+ readDatadogTags,
16
+ readDatadogTraceId,
17
+ readLegacyBaggage,
18
+ readSqsd,
19
+ readTraceparent,
20
+ readTracestate,
21
+ writeBaggage,
22
+ writeB3,
23
+ writeB3Flags,
24
+ writeB3ParentId,
25
+ writeB3Sampled,
26
+ writeB3SpanId,
27
+ writeB3TraceId,
28
+ writeDatadogOrigin,
29
+ writeDatadogParentId,
30
+ writeDatadogSamplingPriority,
31
+ writeDatadogTags,
32
+ writeDatadogTraceId,
33
+ writeLegacyBaggage,
34
+ writeTraceparent,
35
+ writeTracestate,
36
+ } = require('../../carrier')
5
37
  const id = require('../../id')
6
38
  const DatadogSpanContext = require('../span_context')
7
39
  const log = require('../../log')
@@ -20,21 +52,8 @@ const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
20
52
  const injectCh = channel('dd-trace:span:inject')
21
53
  const extractCh = channel('dd-trace:span:extract')
22
54
 
23
- const traceKey = 'x-datadog-trace-id'
24
- const spanKey = 'x-datadog-parent-id'
25
- const originKey = 'x-datadog-origin'
26
- const samplingKey = 'x-datadog-sampling-priority'
27
- const tagsKey = 'x-datadog-tags'
28
- const baggagePrefix = 'ot-baggage-'
29
- const b3TraceKey = 'x-b3-traceid'
30
55
  const b3TraceExpr = /^([0-9a-f]{16}){1,2}$/i
31
- const b3SpanKey = 'x-b3-spanid'
32
56
  const b3SpanExpr = /^[0-9a-f]{16}$/i
33
- const b3ParentKey = 'x-b3-parentspanid'
34
- const b3SampledKey = 'x-b3-sampled'
35
- const b3FlagsKey = 'x-b3-flags'
36
- const b3HeaderKey = 'b3'
37
- const sqsdHeaderHey = 'x-aws-sqsd-attr-_datadog'
38
57
  const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)?|[01d])$/i
39
58
  // W3C Baggage key grammar: key = token (RFC 7230).
40
59
  // Spec (up-to-date): "Propagation format for distributed context: Baggage" §3.3.1
@@ -43,19 +62,10 @@ const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)
43
62
  const baggageTokenExpr = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/
44
63
  const tagKeyExpr = /^_dd\.p\.[\x21-\x2B\x2D-\x7E]+$/ // ASCII minus spaces and commas
45
64
  const tagValueExpr = /^[\x20-\x2B\x2D-\x7E]*$/ // ASCII minus commas
46
- // RFC7230 token (used by HTTP header field-name) and compatible with Node's header name validation.
47
- // See https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
48
- const httpHeaderNameExpr = /^[0-9A-Za-z!#$%&'*+\-.^_`|~]+$/
49
65
  // Compatible with Node's internal header value validation (allows HTAB, SP-~, and \x80-\xFF only)
50
66
  // https://github.com/nodejs/node/blob/main/lib/_http_common.js
51
67
  const invalidHeaderValueCharExpr = /[^\t\x20-\x7E\x80-\xFF]/
52
68
  const traceparentExpr = /^([a-f0-9]{2})-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})(-.*)?$/i
53
- const traceparentKey = 'traceparent'
54
- const tracestateKey = 'tracestate'
55
- const ddKeys = [traceKey, spanKey, samplingKey, originKey]
56
- const b3Keys = [b3TraceKey, b3SpanKey, b3ParentKey, b3SampledKey, b3FlagsKey, b3HeaderKey]
57
- const w3cKeys = [traceparentKey, tracestateKey]
58
- const logKeys = [...ddKeys, ...b3Keys, ...w3cKeys]
59
69
  // Dispatch table for `_extractSpanContext`. `'b3'` resolves to the matching
60
70
  // single/multi extractor per instance — see `#b3MethodName` — so it is not in
61
71
  // this table. `'baggage'` is consumed by `_extractBaggageItems`, not the loop.
@@ -146,7 +156,7 @@ class TextMapPropagator {
146
156
  if (injectCh.hasSubscribers) injectCh.publish({ spanContext, carrier })
147
157
 
148
158
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
149
- log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
159
+ log.debug(() => `Inject into carrier: ${JSON.stringify(pickTextMap(carrier))}.`)
150
160
 
151
161
  return carrier
152
162
  }
@@ -161,7 +171,7 @@ class TextMapPropagator {
161
171
 
162
172
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
163
173
  log.debug(() => {
164
- const keys = JSON.stringify(pick(carrier, logKeys))
174
+ const keys = JSON.stringify(pickTextMap(carrier))
165
175
  const styles = this._config.tracePropagationStyle.extract.join(', ')
166
176
 
167
177
  return `Extract from carrier (${styles}): ${keys}.`
@@ -179,8 +189,8 @@ class TextMapPropagator {
179
189
  if (!this._hasPropagationStyle('inject', 'datadog')) return
180
190
 
181
191
  carrier ??= {}
182
- carrier[traceKey] = spanContext.toTraceId()
183
- carrier[spanKey] = spanContext.toSpanId()
192
+ writeDatadogTraceId(carrier, spanContext.toTraceId())
193
+ writeDatadogParentId(carrier, spanContext.toSpanId())
184
194
 
185
195
  this._injectOrigin(spanContext, carrier)
186
196
  this._injectSamplingPriority(spanContext, carrier)
@@ -193,7 +203,7 @@ class TextMapPropagator {
193
203
  const origin = spanContext._trace.origin
194
204
 
195
205
  if (origin) {
196
- carrier[originKey] = origin
206
+ writeDatadogOrigin(carrier, origin)
197
207
  }
198
208
  }
199
209
 
@@ -201,7 +211,7 @@ class TextMapPropagator {
201
211
  const priority = spanContext._sampling.priority
202
212
 
203
213
  if (Number.isInteger(priority)) {
204
- carrier[samplingKey] = priority.toString()
214
+ writeDatadogSamplingPriority(carrier, priority.toString())
205
215
  }
206
216
  }
207
217
 
@@ -216,22 +226,17 @@ class TextMapPropagator {
216
226
  const baggageItems = spanContext?._baggageItems
217
227
  if (baggageItems) {
218
228
  for (const key of Object.keys(baggageItems)) {
219
- const headerName = baggagePrefix + key
220
-
221
- // Legacy OpenTracing baggage is propagated as individual headers (ot-baggage-*),
222
- // so it must always be representable as a valid HTTP header name.
223
- if (!httpHeaderNameExpr.test(headerName)) {
224
- tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
225
- continue
226
- }
227
-
228
229
  let headerValue = String(baggageItems[key])
229
230
  // Avoid Node throwing ERR_INVALID_CHAR when setting header values (e.g. newline from decoded OTEL baggage).
230
231
  if (invalidHeaderValueCharExpr.test(headerValue)) {
231
232
  headerValue = encodeURIComponent(headerValue)
232
233
  }
233
- injectedCarrier ??= carrier ?? {}
234
- injectedCarrier[headerName] = headerValue
234
+ const nextCarrier = writeLegacyBaggage(injectedCarrier ?? carrier, key, headerValue)
235
+ if (nextCarrier === undefined) {
236
+ tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
237
+ continue
238
+ }
239
+ injectedCarrier = nextCarrier
235
240
  }
236
241
  }
237
242
  }
@@ -271,7 +276,7 @@ class TextMapPropagator {
271
276
  baggage = baggage.slice(0, -1)
272
277
  if (baggage) {
273
278
  injectedCarrier ??= carrier ?? {}
274
- injectedCarrier.baggage = baggage
279
+ writeBaggage(injectedCarrier, baggage)
275
280
  tracerMetrics.count('context_header_style.injected', ['header_style:baggage']).inc()
276
281
  }
277
282
  }
@@ -305,7 +310,7 @@ class TextMapPropagator {
305
310
  if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
306
311
  log.error('Trace tags from span are too large, skipping injection.')
307
312
  } else if (header) {
308
- carrier[tagsKey] = header
313
+ writeDatadogTags(carrier, header)
309
314
  }
310
315
  }
311
316
 
@@ -321,16 +326,16 @@ class TextMapPropagator {
321
326
  if (!hasB3multi) return
322
327
 
323
328
  carrier ??= {}
324
- carrier[b3TraceKey] = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
325
- carrier[b3SpanKey] = spanContext._spanId.toString(16)
326
- carrier[b3SampledKey] = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
329
+ writeB3TraceId(carrier, spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid']))
330
+ writeB3SpanId(carrier, spanContext._spanId.toString(16))
331
+ writeB3Sampled(carrier, spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0')
327
332
 
328
333
  if (spanContext._sampling.priority > AUTO_KEEP) {
329
- carrier[b3FlagsKey] = '1'
334
+ writeB3Flags(carrier, '1')
330
335
  }
331
336
 
332
337
  if (spanContext._parentId) {
333
- carrier[b3ParentKey] = spanContext._parentId.toString(16)
338
+ writeB3ParentId(carrier, spanContext._parentId.toString(16))
334
339
  }
335
340
 
336
341
  return carrier
@@ -352,10 +357,11 @@ class TextMapPropagator {
352
357
  const spanId = spanContext._spanId.toString(16)
353
358
  const sampled = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
354
359
 
355
- carrier[b3HeaderKey] = `${traceId}-${spanId}-${sampled}`
360
+ let header = `${traceId}-${spanId}-${sampled}`
356
361
  if (spanContext._parentId) {
357
- carrier[b3HeaderKey] += '-' + spanContext._parentId.toString(16)
362
+ header += '-' + spanContext._parentId.toString(16)
358
363
  }
364
+ writeB3(carrier, header)
359
365
 
360
366
  return carrier
361
367
  }
@@ -375,7 +381,7 @@ class TextMapPropagator {
375
381
  const header = tracestate.toString()
376
382
  if (!header) return
377
383
  carrier ??= {}
378
- carrier.tracestate = header
384
+ writeTracestate(carrier, header)
379
385
  return carrier
380
386
  }
381
387
 
@@ -386,7 +392,7 @@ class TextMapPropagator {
386
392
  _trace: { origin, tags: traceTags },
387
393
  } = spanContext
388
394
 
389
- carrier[traceparentKey] = spanContext.toTraceparent()
395
+ writeTraceparent(carrier, spanContext.toTraceparent())
390
396
 
391
397
  ts.forVendor('dd', state => {
392
398
  if (!spanContext._isRemote) {
@@ -426,7 +432,7 @@ class TextMapPropagator {
426
432
  }
427
433
  })
428
434
 
429
- carrier.tracestate = ts.toString()
435
+ writeTracestate(carrier, ts.toString())
430
436
 
431
437
  return carrier
432
438
  }
@@ -525,7 +531,12 @@ class TextMapPropagator {
525
531
  }
526
532
 
527
533
  _extractDatadogContext (carrier) {
528
- const spanContext = this._extractGenericContext(carrier, traceKey, spanKey, 10)
534
+ if (!carrier) return
535
+ const spanContext = this._extractGenericContext(
536
+ readDatadogTraceId(carrier),
537
+ readDatadogParentId(carrier),
538
+ 10
539
+ )
529
540
 
530
541
  if (!spanContext) return spanContext
531
542
 
@@ -553,19 +564,16 @@ class TextMapPropagator {
553
564
  }
554
565
 
555
566
  _extractB3SingleContext (carrier) {
556
- // `typeof === 'string'` first; otherwise the regex coerces `undefined` to
557
- // `'undefined'` and runs on every header-less request.
558
- const header = carrier[b3HeaderKey]
559
- if (typeof header !== 'string' || !b3HeaderExpr.test(header)) return
560
- const b3 = this._extractB3SingleHeader(carrier)
561
- if (b3 === undefined) return
562
- return this._extractB3Context(b3)
567
+ // Check the resolved value first so the regex does not run on header-less requests.
568
+ const header = readB3(carrier)
569
+ if (!header || !b3HeaderExpr.test(header)) return
570
+ return this._extractB3Context(this._extractB3SingleHeader(header))
563
571
  }
564
572
 
565
573
  _extractB3Context (b3) {
566
- const debug = b3[b3FlagsKey] === '1'
567
- const priority = this._getPriority(b3[b3SampledKey], debug)
568
- const spanContext = this._extractGenericContext(b3, b3TraceKey, b3SpanKey, 16)
574
+ const debug = b3.flags === '1'
575
+ const priority = this._getPriority(b3.sampled, debug)
576
+ const spanContext = this._extractGenericContext(b3.traceId, b3.spanId, 16)
569
577
 
570
578
  if (priority !== undefined) {
571
579
  if (!spanContext) {
@@ -581,13 +589,13 @@ class TextMapPropagator {
581
589
  spanContext._sampling.priority = priority
582
590
  }
583
591
 
584
- this._extract128BitTraceId(b3[b3TraceKey], spanContext)
592
+ this._extract128BitTraceId(b3.traceId, spanContext)
585
593
 
586
594
  return spanContext
587
595
  }
588
596
 
589
597
  _extractSqsdContext (carrier) {
590
- const headerValue = carrier[sqsdHeaderHey]
598
+ const headerValue = readSqsd(carrier)
591
599
  if (!headerValue) return
592
600
  let parsed
593
601
  try {
@@ -599,18 +607,14 @@ class TextMapPropagator {
599
607
  }
600
608
 
601
609
  _extractTraceparentContext (carrier) {
602
- const headerValue = carrier[traceparentKey]
603
- if (typeof headerValue !== 'string') return
610
+ const headerValue = readTraceparent(carrier)
611
+ if (!headerValue) return
604
612
  const matches = headerValue.trim().match(traceparentExpr)
605
613
  if (matches !== null) {
606
614
  const [, version, traceId, spanId, flags, tail] = matches
607
615
  const traceparent = { version }
608
616
  // W3C Trace Context §3.3.1.1: multiple tracestate fields MUST be combined per RFC 7230 §3.2.2.
609
- // `filter` drops non-string members (Symbol, throwing-toString) that would crash `join`.
610
- const rawTracestate = Array.isArray(carrier.tracestate)
611
- ? carrier.tracestate.filter(item => typeof item === 'string').join(',')
612
- : carrier.tracestate
613
- const tracestate = TraceState.fromString(rawTracestate)
617
+ const tracestate = TraceState.fromString(readTracestate(carrier))
614
618
  if (invalidSegment.test(traceId)) return
615
619
  if (invalidSegment.test(spanId)) return
616
620
 
@@ -683,75 +687,81 @@ class TextMapPropagator {
683
687
  }
684
688
  }
685
689
 
686
- _extractGenericContext (carrier, traceKey, spanKey, radix) {
687
- if (carrier && carrier[traceKey] && carrier[spanKey]) {
688
- if (invalidSegment.test(carrier[traceKey])) return
689
-
690
- return new DatadogSpanContext({
691
- traceId: id(carrier[traceKey], radix),
692
- spanId: id(carrier[spanKey], radix),
693
- isRemote: true,
694
- })
695
- }
690
+ /**
691
+ * @param {string | undefined} traceId
692
+ * @param {string | undefined} spanId
693
+ * @param {number} radix
694
+ * @returns {DatadogSpanContext | undefined}
695
+ */
696
+ _extractGenericContext (traceId, spanId, radix) {
697
+ if (!traceId || invalidSegment.test(traceId)) return
698
+ if (!spanId) return
699
+
700
+ return new DatadogSpanContext({
701
+ traceId: id(traceId, radix),
702
+ spanId: id(spanId, radix),
703
+ isRemote: true,
704
+ })
696
705
  }
697
706
 
698
707
  _extractB3MultipleHeaders (carrier) {
699
- // `b3ParentKey` is intentionally absent: this method never consults it,
708
+ // The parent-id field is intentionally absent: this method never consults it,
700
709
  // so a parent-id-only carrier should bail with the rest.
701
- if (carrier[b3TraceKey] === undefined &&
702
- carrier[b3SampledKey] === undefined &&
703
- carrier[b3FlagsKey] === undefined) {
710
+ const traceId = readB3TraceId(carrier)
711
+ const sampled = readB3Sampled(carrier)
712
+ const flags = readB3Flags(carrier)
713
+
714
+ if (traceId === undefined && sampled === undefined && flags === undefined) {
704
715
  return
705
716
  }
706
717
 
707
718
  let empty = true
708
719
  const b3 = {}
720
+ const spanId = readB3SpanId(carrier)
709
721
 
710
- if (b3TraceExpr.test(carrier[b3TraceKey]) && b3SpanExpr.test(carrier[b3SpanKey])) {
711
- b3[b3TraceKey] = carrier[b3TraceKey]
712
- b3[b3SpanKey] = carrier[b3SpanKey]
722
+ if (traceId && spanId && b3TraceExpr.test(traceId) && b3SpanExpr.test(spanId)) {
723
+ b3.traceId = traceId
724
+ b3.spanId = spanId
713
725
  empty = false
714
726
  }
715
727
 
716
- if (carrier[b3SampledKey]) {
717
- b3[b3SampledKey] = carrier[b3SampledKey]
728
+ if (sampled) {
729
+ b3.sampled = sampled
718
730
  empty = false
719
731
  }
720
732
 
721
- if (carrier[b3FlagsKey]) {
722
- b3[b3FlagsKey] = carrier[b3FlagsKey]
733
+ if (flags) {
734
+ b3.flags = flags
723
735
  empty = false
724
736
  }
725
737
 
726
738
  return empty ? undefined : b3
727
739
  }
728
740
 
729
- _extractB3SingleHeader (carrier) {
730
- const header = carrier[b3HeaderKey]
731
- if (!header) return
732
-
741
+ /** @param {string} header */
742
+ _extractB3SingleHeader (header) {
733
743
  const parts = header.split('-')
734
744
 
735
745
  if (parts[0] === 'd') {
736
746
  return {
737
- [b3SampledKey]: '1',
738
- [b3FlagsKey]: '1',
747
+ sampled: '1',
748
+ flags: '1',
739
749
  }
740
750
  } else if (parts.length === 1) {
741
751
  return {
742
- [b3SampledKey]: parts[0],
752
+ sampled: parts[0],
743
753
  }
744
754
  }
745
755
  const b3 = {
746
- [b3TraceKey]: parts[0],
747
- [b3SpanKey]: parts[1],
756
+ traceId: parts[0],
757
+ spanId: parts[1],
748
758
  }
749
759
 
750
760
  if (parts[2]) {
751
- b3[b3SampledKey] = parts[2] === '0' ? '0' : '1'
761
+ b3.sampled = parts[2] === '0' ? '0' : '1'
752
762
 
753
763
  if (parts[2] === 'd') {
754
- b3[b3FlagsKey] = '1'
764
+ b3.flags = '1'
755
765
  }
756
766
  }
757
767
 
@@ -759,29 +769,22 @@ class TextMapPropagator {
759
769
  }
760
770
 
761
771
  _extractOrigin (carrier, spanContext) {
762
- const origin = carrier[originKey]
772
+ const origin = readDatadogOrigin(carrier)
763
773
 
764
- if (typeof carrier[originKey] === 'string') {
774
+ if (typeof origin === 'string') {
765
775
  spanContext._trace.origin = origin
766
776
  }
767
777
  }
768
778
 
769
779
  _extractLegacyBaggageItems (carrier, spanContext) {
770
780
  if (!this._config.legacyBaggageEnabled) return
771
- for (const key of Object.keys(carrier)) {
772
- if (!key.startsWith(baggagePrefix)) continue
773
- const baggageKey = key.slice(baggagePrefix.length)
774
- if (baggageKey) {
775
- spanContext._baggageItems[baggageKey] = carrier[key]
776
- }
777
- }
781
+ readLegacyBaggage(carrier, spanContext._baggageItems)
778
782
  }
779
783
 
780
784
  _extractBaggageItems (carrier, spanContext) {
781
785
  removeAllBaggageItems()
782
- if (!this._hasPropagationStyle('extract', 'baggage')) return
783
- const baggageHeader = carrier?.baggage
784
- const header = Array.isArray(baggageHeader) ? baggageHeader.join(',') : baggageHeader
786
+ if (!carrier || !this._hasPropagationStyle('extract', 'baggage')) return
787
+ const header = readBaggage(carrier)
785
788
  if (!header) return
786
789
 
787
790
  const baggages = header.split(',')
@@ -853,7 +856,9 @@ class TextMapPropagator {
853
856
  }
854
857
 
855
858
  _extractSamplingPriority (carrier, spanContext) {
856
- const priority = Number.parseInt(carrier[samplingKey], 10)
859
+ const header = readDatadogSamplingPriority(carrier)
860
+ if (header === undefined) return
861
+ const priority = Number.parseInt(header, 10)
857
862
 
858
863
  if (Number.isInteger(priority)) {
859
864
  spanContext._sampling.priority = priority
@@ -861,16 +866,17 @@ class TextMapPropagator {
861
866
  }
862
867
 
863
868
  _extractTags (carrier, spanContext) {
864
- if (!carrier[tagsKey]) return
869
+ const header = readDatadogTags(carrier)
870
+ if (!header) return
865
871
 
866
872
  const trace = spanContext._trace
867
873
 
868
874
  if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
869
875
  log.debug('Trace tag propagation is disabled, skipping extraction.')
870
- } else if (carrier[tagsKey].length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
876
+ } else if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
871
877
  log.error('Trace tags from carrier are too large, skipping extraction.')
872
878
  } else {
873
- const pairs = carrier[tagsKey].split(',')
879
+ const pairs = header.split(',')
874
880
  const tags = {}
875
881
 
876
882
  for (const pair of pairs) {
@@ -1,13 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const pick = require('../../../../datadog-core/src/utils/src/pick')
3
+ const { pickDsm } = require('../../carrier')
4
4
  const log = require('../../log')
5
5
 
6
6
  const { DsmPathwayCodec } = require('../../datastreams')
7
7
 
8
- const base64Key = 'dd-pathway-ctx-base64'
9
- const logKeys = [base64Key]
10
-
11
8
  class DSMTextMapPropagator {
12
9
  constructor (config) {
13
10
  this.config = config
@@ -24,11 +21,10 @@ class DSMTextMapPropagator {
24
21
  const injectedCarrier = DsmPathwayCodec.encode(ctx, carrier)
25
22
  if (injectedCarrier === undefined) return
26
23
 
27
- carrier = injectedCarrier
28
24
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
29
- log.debug(() => `Inject into carrier (DSM): ${JSON.stringify(pick(carrier, logKeys))}.`)
25
+ log.debug(() => `Inject into carrier (DSM): ${JSON.stringify(pickDsm(injectedCarrier))}.`)
30
26
 
31
- return carrier
27
+ return injectedCarrier
32
28
  }
33
29
 
34
30
  extract (carrier) {
@@ -39,7 +35,7 @@ class DSMTextMapPropagator {
39
35
  if (!dsmContext) return dsmContext
40
36
 
41
37
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
42
- log.debug(() => `Extract from carrier (DSM): ${JSON.stringify(pick(carrier, logKeys))}.`)
38
+ log.debug(() => `Extract from carrier (DSM): ${JSON.stringify(pickDsm(carrier))}.`)
43
39
  return dsmContext
44
40
  }
45
41
  }
@@ -36,8 +36,11 @@ class DatadogTracer {
36
36
  // (test_session/test_module/ test_suite/test) belong on the citestcycle
37
37
  // endpoint, not on an OTLP traces endpoint — otherwise users with OTEL_*
38
38
  // vars set in their environment (e.g. for a separate telemetry integration)
39
- // silently lose all test spans.
40
- if (config.OTEL_TRACES_EXPORTER === 'otlp' && !config.isCiVisibility) {
39
+ // silently lose all test spans. The same applies to the Electron exporter:
40
+ // spans must reach the Electron SDK's IPC bridge, not an OTLP endpoint,
41
+ // even when OTEL_* vars are set for unrelated telemetry.
42
+ if (config.OTEL_TRACES_EXPORTER === 'otlp' && !config.isCiVisibility &&
43
+ config.experimental.exporter !== 'electron') {
41
44
  const { createOtlpTraceExporter } = require('../opentelemetry/trace')
42
45
  this._exporter = createOtlpTraceExporter(config)
43
46
  } else {