dd-trace 6.11.0 → 6.13.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 (139) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  3. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  4. package/index.d.ts +143 -25
  5. package/package.json +26 -25
  6. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  7. package/packages/datadog-instrumentations/src/cucumber.js +4 -4
  8. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  9. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  12. package/packages/datadog-instrumentations/src/jest.js +140 -18
  13. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  14. package/packages/datadog-instrumentations/src/mariadb.js +7 -0
  15. package/packages/datadog-instrumentations/src/mocha/utils.js +5 -2
  16. package/packages/datadog-instrumentations/src/pino.js +15 -4
  17. package/packages/datadog-instrumentations/src/playwright-reporter.js +149 -1
  18. package/packages/datadog-instrumentations/src/playwright.js +67 -53
  19. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +13 -12
  20. package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
  21. package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
  22. package/packages/datadog-instrumentations/src/winston.js +6 -3
  23. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  24. package/packages/datadog-plugin-http/src/client.js +2 -15
  25. package/packages/datadog-plugin-http2/src/client.js +2 -15
  26. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  27. package/packages/datadog-plugin-playwright/src/index.js +13 -1
  28. package/packages/datadog-plugin-undici/src/index.js +2 -15
  29. package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
  30. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  31. package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
  32. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  33. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  34. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  35. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  36. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  37. package/packages/dd-trace/src/appsec/reporter.js +2 -0
  38. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  39. package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
  40. package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
  41. package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
  42. package/packages/dd-trace/src/carrier.js +107 -48
  43. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
  44. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -0
  45. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +5 -3
  46. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
  47. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -2
  48. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
  49. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
  50. package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
  51. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
  52. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
  53. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  54. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
  55. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +4 -2
  56. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
  57. package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
  58. package/packages/dd-trace/src/config/index.js +2 -1
  59. package/packages/dd-trace/src/config/supported-configurations.json +27 -0
  60. package/packages/dd-trace/src/debugger/constants.js +1 -0
  61. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  62. package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -10
  63. package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
  64. package/packages/dd-trace/src/debugger/index.js +4 -2
  65. package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
  66. package/packages/dd-trace/src/dogstatsd.js +45 -18
  67. package/packages/dd-trace/src/encode/agentless-json.js +5 -4
  68. package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
  69. package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
  70. package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
  71. package/packages/dd-trace/src/evp_proxy/path.js +22 -0
  72. package/packages/dd-trace/src/exporters/agent/index.js +22 -2
  73. package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
  74. package/packages/dd-trace/src/exporters/common/request.js +5 -2
  75. package/packages/dd-trace/src/exporters/common/writer.js +24 -2
  76. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  77. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  78. package/packages/dd-trace/src/flush.js +102 -0
  79. package/packages/dd-trace/src/llmobs/experiments/client.js +10 -2
  80. package/packages/dd-trace/src/llmobs/experiments/dataset.js +301 -14
  81. package/packages/dd-trace/src/llmobs/experiments/experiment.js +440 -112
  82. package/packages/dd-trace/src/llmobs/experiments/index.js +68 -44
  83. package/packages/dd-trace/src/llmobs/experiments/noop.js +103 -12
  84. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  85. package/packages/dd-trace/src/llmobs/experiments/util.js +81 -4
  86. package/packages/dd-trace/src/llmobs/index.js +80 -17
  87. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  88. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  89. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  90. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  91. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  92. package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
  93. package/packages/dd-trace/src/noop/span.js +5 -0
  94. package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
  95. package/packages/dd-trace/src/openfeature/index.js +7 -4
  96. package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
  97. package/packages/dd-trace/src/openfeature/writers/exposures.js +70 -15
  98. package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
  99. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  100. package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
  101. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  102. package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
  103. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  104. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
  105. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
  106. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  107. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  108. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  109. package/packages/dd-trace/src/opentracing/propagation/text_map.js +429 -352
  110. package/packages/dd-trace/src/opentracing/span.js +28 -0
  111. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  112. package/packages/dd-trace/src/plugin_manager.js +2 -0
  113. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  114. package/packages/dd-trace/src/plugins/util/test.js +74 -21
  115. package/packages/dd-trace/src/plugins/util/web.js +2 -65
  116. package/packages/dd-trace/src/profiling/oom.js +4 -3
  117. package/packages/dd-trace/src/profiling/profiler.js +7 -0
  118. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  119. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  120. package/packages/dd-trace/src/proxy.js +36 -8
  121. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  122. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
  123. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
  124. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
  125. package/packages/dd-trace/src/serverless/vercel.js +122 -0
  126. package/packages/dd-trace/src/serverless.js +37 -20
  127. package/packages/dd-trace/src/span_stats.js +40 -3
  128. package/packages/dd-trace/src/telemetry/metrics.js +62 -8
  129. package/packages/dd-trace/src/telemetry/send-data.js +1 -0
  130. package/packages/dd-trace/src/tracer.js +22 -0
  131. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  132. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  133. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  134. package/vendor/dist/https-proxy-agent/LICENSE +22 -0
  135. package/vendor/dist/https-proxy-agent/index.js +4 -0
  136. package/vendor/dist/protobufjs/index.js +1 -1
  137. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  138. package/vendor/dist/proxy-from-env/LICENSE +20 -0
  139. package/vendor/dist/proxy-from-env/index.js +1 -0
@@ -66,15 +66,6 @@ const tagValueExpr = /^[\x20-\x2B\x2D-\x7E]*$/ // ASCII minus commas
66
66
  // https://github.com/nodejs/node/blob/main/lib/_http_common.js
67
67
  const invalidHeaderValueCharExpr = /[^\t\x20-\x7E\x80-\xFF]/
68
68
  const traceparentExpr = /^([a-f0-9]{2})-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})(-.*)?$/i
69
- // Dispatch table for `_extractSpanContext`. `'b3'` resolves to the matching
70
- // single/multi extractor per instance — see `#b3MethodName` — so it is not in
71
- // this table. `'baggage'` is consumed by `_extractBaggageItems`, not the loop.
72
- const EXTRACT_STYLE_METHODS = new Map([
73
- ['datadog', '_extractDatadogContext'],
74
- ['tracecontext', '_extractTraceparentContext'],
75
- ['b3 single header', '_extractB3SingleContext'],
76
- ['b3multi', '_extractB3MultiContext'],
77
- ])
78
69
  // Origin value in tracestate replaces '~', ',' and ';' with '_"
79
70
  const tracestateOriginFilter = /[^\x20-\x2B\x2D-\x3A\x3C-\x7D]/g
80
71
  // Tag keys in tracestate replace ' ', ',' and '=' with '_'
@@ -86,35 +77,216 @@ const zeroTraceId = '0000000000000000'
86
77
  const hex16 = /^[0-9A-Fa-f]{16}$/
87
78
  const percentByte = /%([0-9A-Fa-f]{2})/g
88
79
 
80
+ /**
81
+ * @typedef {object} B3Context
82
+ * @property {string} [flags]
83
+ * @property {string} [sampled]
84
+ * @property {string} [spanId]
85
+ * @property {string} [traceId]
86
+ */
87
+
88
+ /**
89
+ * @param {string | undefined} traceId
90
+ * @param {string | undefined} spanId
91
+ * @param {number} radix
92
+ * @returns {DatadogSpanContext | undefined}
93
+ */
94
+ function extractGenericContext (traceId, spanId, radix) {
95
+ if (!traceId || invalidSegment.test(traceId)) return
96
+ if (!spanId) return
97
+
98
+ return new DatadogSpanContext({
99
+ traceId: id(traceId, radix),
100
+ spanId: id(spanId, radix),
101
+ isRemote: true,
102
+ })
103
+ }
104
+
105
+ /**
106
+ * @param {string} traceId
107
+ * @param {DatadogSpanContext} spanContext
108
+ * @returns {void}
109
+ */
110
+ function extract128BitTraceId (traceId, spanContext) {
111
+ const buffer = spanContext._traceId.toBuffer()
112
+
113
+ if (buffer.length !== 16) return
114
+
115
+ const tid = traceId.slice(0, 16)
116
+
117
+ if (tid === zeroTraceId) return
118
+
119
+ spanContext._trace.tags['_dd.p.tid'] = tid
120
+ }
121
+
122
+ /**
123
+ * @param {string | undefined} sampled
124
+ * @param {boolean} debug
125
+ * @returns {import('../../priority_sampler').SamplingPriority | undefined}
126
+ */
127
+ function getB3Priority (sampled, debug) {
128
+ if (debug) {
129
+ return USER_KEEP
130
+ } else if (sampled === '1') {
131
+ return AUTO_KEEP
132
+ } else if (sampled === '0') {
133
+ return AUTO_REJECT
134
+ }
135
+ }
136
+
137
+ /**
138
+ * @param {B3Context} b3
139
+ * @returns {DatadogSpanContext | undefined}
140
+ */
141
+ function extractB3Context (b3) {
142
+ const priority = getB3Priority(b3.sampled, b3.flags === '1')
143
+ const spanContext = extractGenericContext(b3.traceId, b3.spanId, 16)
144
+
145
+ if (priority !== undefined) {
146
+ if (!spanContext) {
147
+ return new DatadogSpanContext({
148
+ traceId: id(),
149
+ spanId: null,
150
+ sampling: { priority },
151
+ isRemote: true,
152
+ })
153
+ }
154
+
155
+ spanContext._sampling.priority = priority
156
+ }
157
+
158
+ if (spanContext && b3.traceId) extract128BitTraceId(b3.traceId, spanContext)
159
+
160
+ return spanContext
161
+ }
162
+
163
+ /**
164
+ * @param {Record<string, unknown>} carrier
165
+ * @returns {B3Context | undefined}
166
+ */
167
+ function extractB3MultipleHeaders (carrier) {
168
+ // Parent ID is intentionally not a standalone signal for B3 extraction.
169
+ const traceId = readB3TraceId(carrier)
170
+ const sampled = readB3Sampled(carrier)
171
+ const flags = readB3Flags(carrier)
172
+
173
+ if (traceId === undefined && sampled === undefined && flags === undefined) return
174
+
175
+ let empty = true
176
+ const b3 = {}
177
+ const spanId = readB3SpanId(carrier)
178
+
179
+ if (traceId && spanId && b3TraceExpr.test(traceId) && b3SpanExpr.test(spanId)) {
180
+ b3.traceId = traceId
181
+ b3.spanId = spanId
182
+ empty = false
183
+ }
184
+
185
+ if (sampled) {
186
+ b3.sampled = sampled
187
+ empty = false
188
+ }
189
+
190
+ if (flags) {
191
+ b3.flags = flags
192
+ empty = false
193
+ }
194
+
195
+ return empty ? undefined : b3
196
+ }
197
+
198
+ /**
199
+ * @param {string} header
200
+ * @returns {B3Context}
201
+ */
202
+ function extractB3SingleHeader (header) {
203
+ const traceIdEnd = header.indexOf('-')
204
+
205
+ if (traceIdEnd === -1) {
206
+ if (header === 'd') {
207
+ return {
208
+ sampled: '1',
209
+ flags: '1',
210
+ }
211
+ }
212
+ return {
213
+ sampled: header,
214
+ }
215
+ }
216
+
217
+ const spanIdStart = traceIdEnd + 1
218
+ const spanIdEnd = spanIdStart + 16
219
+ const b3 = {
220
+ traceId: header.slice(0, traceIdEnd),
221
+ spanId: header.slice(spanIdStart, spanIdEnd),
222
+ }
223
+
224
+ if (header.length > spanIdEnd) {
225
+ const sampled = header[spanIdEnd + 1]
226
+ b3.sampled = sampled === '0' ? '0' : '1'
227
+
228
+ if (sampled === 'd') {
229
+ b3.flags = '1'
230
+ }
231
+ }
232
+
233
+ return b3
234
+ }
235
+
236
+ /**
237
+ * @param {Record<string, unknown>} carrier
238
+ * @returns {DatadogSpanContext | undefined}
239
+ */
240
+ function extractB3MultiContext (carrier) {
241
+ const b3 = extractB3MultipleHeaders(carrier)
242
+ if (b3 === undefined) return
243
+ return extractB3Context(b3)
244
+ }
245
+
246
+ /**
247
+ * @param {Record<string, unknown>} carrier
248
+ * @returns {DatadogSpanContext | undefined}
249
+ */
250
+ function extractB3SingleContext (carrier) {
251
+ // Resolve the value before running the regex on the common header-less path.
252
+ const header = readB3(carrier)
253
+ if (!header || !b3HeaderExpr.test(header)) return
254
+ return extractB3Context(extractB3SingleHeader(header))
255
+ }
256
+
89
257
  class TextMapPropagator {
90
- /** @type {Set<string> | undefined} Cached `Set` view of `_config.baggageTagKeys`. */
258
+ /** @type {Set<string> | undefined} Cached `Set` view of `#config.baggageTagKeys`. */
91
259
  #baggageTagKeysSet
92
260
 
93
261
  /** @type {string[] | undefined} Source array that `#baggageTagKeysSet` was built from. */
94
262
  #baggageTagKeysSetSource
95
263
 
96
- /** @type {'_extractB3SingleContext' | '_extractB3MultiContext'} */
97
- #b3MethodName
264
+ /** @type {import('../../config')} */
265
+ #config
266
+
267
+ /** @type {typeof extractB3SingleContext | typeof extractB3MultiContext} */
268
+ #extractB3Context
98
269
 
270
+ /** @param {import('../../config')} config */
99
271
  constructor (config) {
100
- this._config = config
272
+ this.#config = config
101
273
 
102
- // v6: `'b3'` is always single-header. v5: `OTEL_PROPAGATORS` callers
274
+ // v6+: `'b3'` is always single-header. v5: `OTEL_PROPAGATORS` callers
103
275
  // expect single, legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
104
- /* istanbul ignore else: v5 fallback, master ships 6.0.0-pre */
276
+ /* istanbul ignore else: v5 fallback */
105
277
  if (DD_MAJOR >= 6) {
106
- this.#b3MethodName = '_extractB3SingleContext'
278
+ this.#extractB3Context = extractB3SingleContext
107
279
  } else {
108
280
  const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
109
281
  // eslint-disable-next-line eslint-rules/eslint-env-aliases
110
- this.#b3MethodName = envName === 'OTEL_PROPAGATORS'
111
- ? '_extractB3SingleContext'
112
- : '_extractB3MultiContext'
282
+ this.#extractB3Context = envName === 'OTEL_PROPAGATORS'
283
+ ? extractB3SingleContext
284
+ : extractB3MultiContext
113
285
  }
114
286
  }
115
287
 
116
288
  /**
117
- * Returns a `Set` view of `_config.baggageTagKeys` that is rebuilt only
289
+ * Returns a `Set` view of `#config.baggageTagKeys` that is rebuilt only
118
290
  * when the source array reference changes. Avoids an `O(n)` `Set` alloc
119
291
  * per baggage extract (which is per-request when baggage propagation is
120
292
  * enabled).
@@ -122,7 +294,7 @@ class TextMapPropagator {
122
294
  * @returns {Set<string>}
123
295
  */
124
296
  #getBaggageTagKeysSet () {
125
- const source = this._config.baggageTagKeys
297
+ const source = this.#config.baggageTagKeys
126
298
  if (this.#baggageTagKeysSetSource !== source) {
127
299
  this.#baggageTagKeysSet = new Set(source)
128
300
  this.#baggageTagKeysSetSource = source
@@ -137,18 +309,18 @@ class TextMapPropagator {
137
309
  */
138
310
  inject (spanContext, carrier) {
139
311
  if (carrier === null) return
140
- let injectedCarrier = this._injectBaggageItems(spanContext, carrier)
312
+ let injectedCarrier = this.#injectBaggageItems(spanContext, carrier)
141
313
  if (!spanContext) return injectedCarrier
142
314
 
143
- const injectTraceContext = this._config.apmTracingEnabled !== false ||
315
+ const injectTraceContext = this.#config.apmTracingEnabled !== false ||
144
316
  hasTraceSourcePropagationTag(spanContext._trace.tags)
145
317
  if (injectTraceContext) {
146
- injectedCarrier = this._injectDatadog(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
147
- injectedCarrier = this._injectB3MultipleHeaders(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
148
- injectedCarrier = this._injectB3SingleHeader(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
318
+ injectedCarrier = this.#injectDatadog(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
319
+ injectedCarrier = this.#injectB3MultipleHeaders(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
320
+ injectedCarrier = this.#injectB3SingleHeader(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
149
321
  }
150
322
  injectedCarrier = this
151
- ._injectTraceparent(spanContext, injectedCarrier ?? carrier, injectTraceContext) ?? injectedCarrier
323
+ .#injectTraceparent(spanContext, injectedCarrier ?? carrier, injectTraceContext) ?? injectedCarrier
152
324
 
153
325
  if (injectedCarrier === undefined) return
154
326
 
@@ -161,8 +333,12 @@ class TextMapPropagator {
161
333
  return carrier
162
334
  }
163
335
 
336
+ /**
337
+ * @param {Record<string, unknown>} carrier
338
+ * @returns {DatadogSpanContext | null}
339
+ */
164
340
  extract (carrier) {
165
- const spanContext = this._extractSpanContext(carrier)
341
+ const spanContext = this.#extractSpanContext(carrier)
166
342
  if (spanContext === undefined) return null
167
343
 
168
344
  if (extractCh.hasSubscribers) {
@@ -172,7 +348,7 @@ class TextMapPropagator {
172
348
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
173
349
  log.debug(() => {
174
350
  const keys = JSON.stringify(pickTextMap(carrier))
175
- const styles = this._config.tracePropagationStyle.extract.join(', ')
351
+ const styles = this.#config.tracePropagationStyle.extract.join(', ')
176
352
 
177
353
  return `Extract from carrier (${styles}): ${keys}.`
178
354
  })
@@ -185,34 +361,22 @@ class TextMapPropagator {
185
361
  * @param {Record<string, string>} [carrier]
186
362
  * @returns {Record<string, string> | undefined}
187
363
  */
188
- _injectDatadog (spanContext, carrier) {
189
- if (!this._hasPropagationStyle('inject', 'datadog')) return
364
+ #injectDatadog (spanContext, carrier) {
365
+ if (!this.#hasPropagationStyle('inject', 'datadog')) return
190
366
 
191
367
  carrier ??= {}
192
368
  writeDatadogTraceId(carrier, spanContext.toTraceId())
193
369
  writeDatadogParentId(carrier, spanContext.toSpanId())
194
370
 
195
- this._injectOrigin(spanContext, carrier)
196
- this._injectSamplingPriority(spanContext, carrier)
197
- this._injectTags(spanContext, carrier)
198
-
199
- return carrier
200
- }
201
-
202
- _injectOrigin (spanContext, carrier) {
203
371
  const origin = spanContext._trace.origin
372
+ if (origin) writeDatadogOrigin(carrier, origin)
204
373
 
205
- if (origin) {
206
- writeDatadogOrigin(carrier, origin)
207
- }
208
- }
209
-
210
- _injectSamplingPriority (spanContext, carrier) {
211
374
  const priority = spanContext._sampling.priority
375
+ if (Number.isInteger(priority)) writeDatadogSamplingPriority(carrier, priority.toString())
212
376
 
213
- if (Number.isInteger(priority)) {
214
- writeDatadogSamplingPriority(carrier, priority.toString())
215
- }
377
+ this.#injectTags(spanContext, carrier)
378
+
379
+ return carrier
216
380
  }
217
381
 
218
382
  /**
@@ -220,9 +384,9 @@ class TextMapPropagator {
220
384
  * @param {Record<string, string>} [carrier]
221
385
  * @returns {Record<string, string> | undefined}
222
386
  */
223
- _injectBaggageItems (spanContext, carrier) {
387
+ #injectBaggageItems (spanContext, carrier) {
224
388
  let injectedCarrier
225
- if (this._config.legacyBaggageEnabled) {
389
+ if (this.#config.legacyBaggageEnabled) {
226
390
  const baggageItems = spanContext?._baggageItems
227
391
  if (baggageItems) {
228
392
  for (const key of Object.keys(baggageItems)) {
@@ -241,7 +405,7 @@ class TextMapPropagator {
241
405
  }
242
406
  }
243
407
 
244
- if (this._hasPropagationStyle('inject', 'baggage')) {
408
+ if (this.#hasPropagationStyle('inject', 'baggage')) {
245
409
  let baggage = ''
246
410
  let itemCounter = 0
247
411
  let byteCounter = 0
@@ -259,13 +423,13 @@ class TextMapPropagator {
259
423
  byteCounter += item.length
260
424
 
261
425
  // Check for item count limit exceeded
262
- if (itemCounter > this._config.baggageMaxItems) {
426
+ if (itemCounter > this.#config.baggageMaxItems) {
263
427
  tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_item_count_exceeded']).inc()
264
428
  break
265
429
  }
266
430
 
267
431
  // Check for byte count limit exceeded
268
- if (byteCounter > this._config.baggageMaxBytes) {
432
+ if (byteCounter > this.#config.baggageMaxBytes) {
269
433
  tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_byte_count_exceeded']).inc()
270
434
  break
271
435
  }
@@ -284,30 +448,34 @@ class TextMapPropagator {
284
448
  return injectedCarrier
285
449
  }
286
450
 
287
- _injectTags (spanContext, carrier) {
451
+ /**
452
+ * @param {DatadogSpanContext} spanContext
453
+ * @param {Record<string, string>} carrier
454
+ * @returns {void}
455
+ */
456
+ #injectTags (spanContext, carrier) {
288
457
  const trace = spanContext._trace
289
458
 
290
- if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
459
+ if (this.#config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
291
460
  log.debug('Trace tag propagation is disabled, skipping injection.')
292
461
  return
293
462
  }
294
463
 
295
- const tags = []
464
+ let header = ''
296
465
 
297
466
  for (const key of Object.keys(trace.tags)) {
298
467
  const value = trace.tags[key]
299
468
  if (!value || !key.startsWith('_dd.p.')) continue
300
- if (!this._validateTagKey(key) || !this._validateTagValue(value)) {
469
+ if (!tagKeyExpr.test(key) || !tagValueExpr.test(value)) {
301
470
  log.error('Trace tags from span are invalid, skipping injection.')
302
471
  return
303
472
  }
304
473
 
305
- tags.push(`${key}=${value}`)
474
+ if (header) header += ','
475
+ header += `${key}=${value}`
306
476
  }
307
477
 
308
- const header = tags.join(',')
309
-
310
- if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
478
+ if (header.length > this.#config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
311
479
  log.error('Trace tags from span are too large, skipping injection.')
312
480
  } else if (header) {
313
481
  writeDatadogTags(carrier, header)
@@ -319,10 +487,10 @@ class TextMapPropagator {
319
487
  * @param {Record<string, string>} [carrier]
320
488
  * @returns {Record<string, string> | undefined}
321
489
  */
322
- _injectB3MultipleHeaders (spanContext, carrier) {
323
- // v5 also accepts the legacy `'b3'` spelling for multi; v6 routes `'b3'` to single-header.
324
- const hasB3multi = this._hasPropagationStyle('inject', 'b3multi') ||
325
- (DD_MAJOR < 6 && this._hasPropagationStyle('inject', 'b3'))
490
+ #injectB3MultipleHeaders (spanContext, carrier) {
491
+ // v5 also accepts the legacy `'b3'` spelling for multi; v6+ routes `'b3'` to single-header.
492
+ const hasB3multi = this.#hasPropagationStyle('inject', 'b3multi') ||
493
+ (DD_MAJOR < 6 && this.#hasPropagationStyle('inject', 'b3'))
326
494
  if (!hasB3multi) return
327
495
 
328
496
  carrier ??= {}
@@ -346,10 +514,10 @@ class TextMapPropagator {
346
514
  * @param {Record<string, string>} [carrier]
347
515
  * @returns {Record<string, string> | undefined}
348
516
  */
349
- _injectB3SingleHeader (spanContext, carrier) {
350
- // v6 keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
351
- const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header') ||
352
- (DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
517
+ #injectB3SingleHeader (spanContext, carrier) {
518
+ // v6+ keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
519
+ const hasB3SingleHeader = this.#hasPropagationStyle('inject', 'b3 single header') ||
520
+ (DD_MAJOR >= 6 && this.#hasPropagationStyle('inject', 'b3'))
353
521
  if (!hasB3SingleHeader) return
354
522
 
355
523
  carrier ??= {}
@@ -372,8 +540,8 @@ class TextMapPropagator {
372
540
  * @param {boolean} injectTraceContext
373
541
  * @returns {Record<string, string> | undefined}
374
542
  */
375
- _injectTraceparent (spanContext, carrier, injectTraceContext) {
376
- if (!this._hasPropagationStyle('inject', 'tracecontext')) return
543
+ #injectTraceparent (spanContext, carrier, injectTraceContext) {
544
+ if (!this.#hasPropagationStyle('inject', 'tracecontext')) return
377
545
 
378
546
  if (!injectTraceContext) {
379
547
  const tracestate = TraceState.fromString(spanContext._tracestate?.toString())
@@ -437,53 +605,93 @@ class TextMapPropagator {
437
605
  return carrier
438
606
  }
439
607
 
440
- _hasPropagationStyle (mode, name) {
441
- return this._config.tracePropagationStyle[mode].includes(name)
442
- }
443
-
444
- _hasTraceIdConflict (w3cSpanContext, firstSpanContext) {
445
- return w3cSpanContext !== undefined &&
446
- firstSpanContext.toTraceId(true) === w3cSpanContext.toTraceId(true) &&
447
- firstSpanContext.toSpanId() !== w3cSpanContext.toSpanId()
448
- }
449
-
450
- _hasParentIdInTags (spanContext) {
451
- return tags.DD_PARENT_ID in spanContext._trace.tags
452
- }
453
-
454
- _updateParentIdFromDdHeaders (carrier, firstSpanContext) {
455
- const ddCtx = this._extractDatadogContext(carrier)
456
- if (ddCtx !== undefined) {
457
- firstSpanContext._trace.tags[tags.DD_PARENT_ID] = ddCtx._spanId.toString().padStart(16, '0')
458
- }
608
+ /**
609
+ * @param {'inject' | 'extract'} mode
610
+ * @param {string} name
611
+ * @returns {boolean}
612
+ */
613
+ #hasPropagationStyle (mode, name) {
614
+ return this.#config.tracePropagationStyle[mode].includes(name)
459
615
  }
460
616
 
461
- _resolveTraceContextConflicts (w3cSpanContext, firstSpanContext, carrier) {
462
- if (!this._hasTraceIdConflict(w3cSpanContext, firstSpanContext)) {
617
+ /**
618
+ * @param {DatadogSpanContext | undefined} w3cSpanContext
619
+ * @param {DatadogSpanContext} firstSpanContext
620
+ * @param {Record<string, unknown>} carrier
621
+ * @param {DatadogSpanContext | undefined} datadogContext
622
+ * @returns {DatadogSpanContext}
623
+ */
624
+ #resolveTraceContextConflicts (w3cSpanContext, firstSpanContext, carrier, datadogContext) {
625
+ if (w3cSpanContext === undefined ||
626
+ firstSpanContext.toTraceId(true) !== w3cSpanContext.toTraceId(true) ||
627
+ firstSpanContext.toSpanId() === w3cSpanContext.toSpanId()) {
463
628
  return firstSpanContext
464
629
  }
465
- if (this._hasParentIdInTags(w3cSpanContext)) {
630
+ if (tags.DD_PARENT_ID in w3cSpanContext._trace.tags) {
466
631
  // tracecontext headers contain a p value, ensure this value is sent to backend
467
632
  firstSpanContext._trace.tags[tags.DD_PARENT_ID] = w3cSpanContext._trace.tags[tags.DD_PARENT_ID]
468
633
  } else {
469
634
  // if p value is not present in tracestate, use the parent id from the datadog headers
470
- this._updateParentIdFromDdHeaders(carrier, firstSpanContext)
635
+ datadogContext ||= extractGenericContext(readDatadogTraceId(carrier), readDatadogParentId(carrier), 10)
636
+ if (datadogContext) {
637
+ firstSpanContext._trace.tags[tags.DD_PARENT_ID] = datadogContext._spanId.toString().padStart(16, '0')
638
+ }
471
639
  }
472
640
  // the span_id in tracecontext takes precedence over the first extracted propagation style
473
641
  firstSpanContext._spanId = w3cSpanContext._spanId
474
642
  return firstSpanContext
475
643
  }
476
644
 
477
- _extractSpanContext (carrier) {
645
+ /**
646
+ * @param {Record<string, unknown>} carrier
647
+ * @returns {DatadogSpanContext | undefined}
648
+ */
649
+ #extractSpanContext (carrier) {
478
650
  let context
651
+ let datadogContext
479
652
  let style = ''
480
- for (const extractor of this._config.tracePropagationStyle.extract) {
481
- const method = extractor === 'b3' ? this.#b3MethodName : EXTRACT_STYLE_METHODS.get(extractor)
482
- if (method === undefined) {
483
- if (extractor !== 'baggage') log.warn('Unknown propagation style:', extractor)
484
- continue
653
+ let extractBaggage = false
654
+ let traceContext
655
+ let traceContextExtracted = false
656
+ for (const extractor of this.#config.tracePropagationStyle.extract) {
657
+ let extractedContext
658
+ switch (extractor) {
659
+ case 'datadog':
660
+ datadogContext = this.#extractDatadogContext(carrier)
661
+ extractedContext = datadogContext
662
+ if (extractedContext !== undefined && !this.#config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
663
+ if (!traceContextExtracted) {
664
+ traceContext = this.#extractTraceparentContext(carrier)
665
+ traceContextExtracted = true
666
+ }
667
+ this.#addTraceContextState(extractedContext, traceContext)
668
+ }
669
+ break
670
+ case 'tracecontext':
671
+ if (!traceContextExtracted) {
672
+ traceContext = this.#extractTraceparentContext(carrier)
673
+ traceContextExtracted = true
674
+ }
675
+ extractedContext = traceContext
676
+ break
677
+ case 'b3':
678
+ extractedContext = this.#extractB3Context(carrier)
679
+ break
680
+ case 'b3 single header':
681
+ extractedContext = extractB3SingleContext(carrier)
682
+ break
683
+ case 'b3multi':
684
+ extractedContext = extractB3MultiContext(carrier)
685
+ break
686
+ case 'baggage':
687
+ extractBaggage = true
688
+ continue
689
+ case 'none':
690
+ continue
691
+ default:
692
+ log.warn('Unknown propagation style:', extractor)
693
+ continue
485
694
  }
486
- const extractedContext = this[method](carrier)
487
695
  if (extractedContext === undefined) {
488
696
  continue
489
697
  }
@@ -491,14 +699,14 @@ class TextMapPropagator {
491
699
  if (context === undefined) {
492
700
  context = extractedContext
493
701
  style = extractor
494
- if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
702
+ if (this.#config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
495
703
  break
496
704
  }
497
705
  } else {
498
706
  // If extractor is tracecontext, add tracecontext specific information to the context
499
707
  if (extractor === 'tracecontext') {
500
- context = this._resolveTraceContextConflicts(
501
- this._extractTraceparentContext(carrier), context, carrier)
708
+ context = this.#resolveTraceContextConflicts(
709
+ extractedContext, context, carrier, datadogContext)
502
710
  }
503
711
  if (extractedContext._traceId && extractedContext._spanId &&
504
712
  extractedContext.toTraceId(true) !== context.toTraceId(true)) {
@@ -511,10 +719,14 @@ class TextMapPropagator {
511
719
  }
512
720
  }
513
721
 
514
- if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'ignore') {
722
+ if (context && (style === 'datadog' || style === 'tracecontext')) {
723
+ this.#extractLegacyBaggageItems(carrier, context)
724
+ }
725
+
726
+ if (this.#config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'ignore') {
515
727
  if (context !== undefined) context._links = []
516
728
  } else {
517
- if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'restart' && context) {
729
+ if (this.#config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'restart' && context) {
518
730
  context._links = []
519
731
  context._links.push({
520
732
  context,
@@ -524,77 +736,47 @@ class TextMapPropagator {
524
736
  },
525
737
  })
526
738
  }
527
- this._extractBaggageItems(carrier, context)
739
+ if (!extractBaggage && this.#config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
740
+ extractBaggage = this.#hasPropagationStyle('extract', 'baggage')
741
+ }
742
+ this.#extractBaggageItems(carrier, context, extractBaggage)
528
743
  }
529
744
 
530
- return context || this._extractSqsdContext(carrier)
745
+ return context || this.#extractSqsdContext(carrier)
531
746
  }
532
747
 
533
- _extractDatadogContext (carrier) {
748
+ /**
749
+ * @param {Record<string, unknown>} carrier
750
+ * @returns {DatadogSpanContext | undefined}
751
+ */
752
+ #extractDatadogContext (carrier) {
534
753
  if (!carrier) return
535
- const spanContext = this._extractGenericContext(
536
- readDatadogTraceId(carrier),
537
- readDatadogParentId(carrier),
538
- 10
539
- )
754
+ const traceId = readDatadogTraceId(carrier)
755
+ if (!traceId) return
756
+ const spanContext = extractGenericContext(traceId, readDatadogParentId(carrier), 10)
540
757
 
541
758
  if (!spanContext) return spanContext
542
759
 
543
- this._extractOrigin(carrier, spanContext)
544
- this._extractLegacyBaggageItems(carrier, spanContext)
545
- this._extractSamplingPriority(carrier, spanContext)
546
- this._extractTags(carrier, spanContext)
547
-
548
- if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) return spanContext
549
-
550
- const tc = this._extractTraceparentContext(carrier)
551
-
552
- if (tc && spanContext._traceId.equals(tc._traceId)) {
553
- spanContext._traceparent = tc._traceparent
554
- spanContext._tracestate = tc._tracestate
555
- }
556
-
557
- return spanContext
558
- }
559
-
560
- _extractB3MultiContext (carrier) {
561
- const b3 = this._extractB3MultipleHeaders(carrier)
562
- if (b3 === undefined) return
563
- return this._extractB3Context(b3)
564
- }
565
-
566
- _extractB3SingleContext (carrier) {
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))
571
- }
572
-
573
- _extractB3Context (b3) {
574
- const debug = b3.flags === '1'
575
- const priority = this._getPriority(b3.sampled, debug)
576
- const spanContext = this._extractGenericContext(b3.traceId, b3.spanId, 16)
577
-
578
- if (priority !== undefined) {
579
- if (!spanContext) {
580
- // B3 can force a sampling decision without providing IDs
581
- return new DatadogSpanContext({
582
- traceId: id(),
583
- spanId: null,
584
- sampling: { priority },
585
- isRemote: true,
586
- })
587
- }
760
+ const origin = readDatadogOrigin(carrier)
761
+ if (typeof origin === 'string') spanContext._trace.origin = origin
588
762
 
589
- spanContext._sampling.priority = priority
763
+ const header = readDatadogSamplingPriority(carrier)
764
+ if (header !== undefined) {
765
+ const priority = Number.parseInt(header, 10)
766
+ if (Number.isInteger(priority)) spanContext._sampling.priority = priority
590
767
  }
591
768
 
592
- this._extract128BitTraceId(b3.traceId, spanContext)
769
+ const traceTags = this.#extractTags(carrier)
770
+ if (traceTags) spanContext._trace.tags = traceTags
593
771
 
594
772
  return spanContext
595
773
  }
596
774
 
597
- _extractSqsdContext (carrier) {
775
+ /**
776
+ * @param {Record<string, unknown>} carrier
777
+ * @returns {DatadogSpanContext | undefined}
778
+ */
779
+ #extractSqsdContext (carrier) {
598
780
  const headerValue = readSqsd(carrier)
599
781
  if (!headerValue) return
600
782
  let parsed
@@ -603,10 +785,32 @@ class TextMapPropagator {
603
785
  } catch {
604
786
  return
605
787
  }
606
- return this._extractDatadogContext(parsed)
788
+ const spanContext = this.#extractDatadogContext(parsed)
789
+ if (!spanContext) return
790
+
791
+ this.#extractLegacyBaggageItems(parsed, spanContext)
792
+ if (this.#config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) return spanContext
793
+
794
+ this.#addTraceContextState(spanContext, this.#extractTraceparentContext(parsed))
795
+ return spanContext
607
796
  }
608
797
 
609
- _extractTraceparentContext (carrier) {
798
+ /**
799
+ * @param {DatadogSpanContext} datadogContext
800
+ * @param {DatadogSpanContext | undefined} traceContext
801
+ */
802
+ #addTraceContextState (datadogContext, traceContext) {
803
+ if (traceContext && datadogContext._traceId.equals(traceContext._traceId)) {
804
+ datadogContext._traceparent = traceContext._traceparent
805
+ datadogContext._tracestate = traceContext._tracestate
806
+ }
807
+ }
808
+
809
+ /**
810
+ * @param {Record<string, unknown>} carrier
811
+ * @returns {DatadogSpanContext | undefined}
812
+ */
813
+ #extractTraceparentContext (carrier) {
610
814
  const headerValue = readTraceparent(carrier)
611
815
  if (!headerValue) return
612
816
  const matches = headerValue.trim().match(traceparentExpr)
@@ -633,7 +837,7 @@ class TextMapPropagator {
633
837
  tracestate,
634
838
  })
635
839
 
636
- this._extract128BitTraceId(traceId, spanContext)
840
+ extract128BitTraceId(traceId, spanContext)
637
841
 
638
842
  tracestate.forVendor('dd', state => {
639
843
  for (const [key, value] of state.entries()) {
@@ -682,147 +886,78 @@ class TextMapPropagator {
682
886
  }
683
887
  })
684
888
 
685
- this._extractLegacyBaggageItems(carrier, spanContext)
686
889
  return spanContext
687
890
  }
688
891
  }
689
892
 
690
893
  /**
691
- * @param {string | undefined} traceId
692
- * @param {string | undefined} spanId
693
- * @param {number} radix
694
- * @returns {DatadogSpanContext | undefined}
894
+ * @param {Record<string, unknown>} carrier
895
+ * @param {DatadogSpanContext} spanContext
896
+ * @returns {void}
695
897
  */
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
- })
705
- }
706
-
707
- _extractB3MultipleHeaders (carrier) {
708
- // The parent-id field is intentionally absent: this method never consults it,
709
- // so a parent-id-only carrier should bail with the rest.
710
- const traceId = readB3TraceId(carrier)
711
- const sampled = readB3Sampled(carrier)
712
- const flags = readB3Flags(carrier)
713
-
714
- if (traceId === undefined && sampled === undefined && flags === undefined) {
715
- return
716
- }
717
-
718
- let empty = true
719
- const b3 = {}
720
- const spanId = readB3SpanId(carrier)
721
-
722
- if (traceId && spanId && b3TraceExpr.test(traceId) && b3SpanExpr.test(spanId)) {
723
- b3.traceId = traceId
724
- b3.spanId = spanId
725
- empty = false
726
- }
727
-
728
- if (sampled) {
729
- b3.sampled = sampled
730
- empty = false
731
- }
732
-
733
- if (flags) {
734
- b3.flags = flags
735
- empty = false
736
- }
737
-
738
- return empty ? undefined : b3
739
- }
740
-
741
- /** @param {string} header */
742
- _extractB3SingleHeader (header) {
743
- const parts = header.split('-')
744
-
745
- if (parts[0] === 'd') {
746
- return {
747
- sampled: '1',
748
- flags: '1',
749
- }
750
- } else if (parts.length === 1) {
751
- return {
752
- sampled: parts[0],
753
- }
754
- }
755
- const b3 = {
756
- traceId: parts[0],
757
- spanId: parts[1],
758
- }
759
-
760
- if (parts[2]) {
761
- b3.sampled = parts[2] === '0' ? '0' : '1'
762
-
763
- if (parts[2] === 'd') {
764
- b3.flags = '1'
765
- }
766
- }
767
-
768
- return b3
769
- }
770
-
771
- _extractOrigin (carrier, spanContext) {
772
- const origin = readDatadogOrigin(carrier)
773
-
774
- if (typeof origin === 'string') {
775
- spanContext._trace.origin = origin
776
- }
777
- }
778
-
779
- _extractLegacyBaggageItems (carrier, spanContext) {
780
- if (!this._config.legacyBaggageEnabled) return
898
+ #extractLegacyBaggageItems (carrier, spanContext) {
899
+ if (!this.#config.legacyBaggageEnabled) return
781
900
  readLegacyBaggage(carrier, spanContext._baggageItems)
782
901
  }
783
902
 
784
- _extractBaggageItems (carrier, spanContext) {
903
+ /**
904
+ * @param {Record<string, unknown> | undefined} carrier
905
+ * @param {DatadogSpanContext | undefined} spanContext
906
+ * @param {boolean} extractBaggage
907
+ * @returns {void}
908
+ */
909
+ #extractBaggageItems (carrier, spanContext, extractBaggage) {
785
910
  removeAllBaggageItems()
786
- if (!carrier || !this._hasPropagationStyle('extract', 'baggage')) return
911
+ if (!carrier || !extractBaggage) return
787
912
  const header = readBaggage(carrier)
788
913
  if (!header) return
789
914
 
790
- const baggages = header.split(',')
791
915
  const baggageTagKeys = this.#getBaggageTagKeysSet()
792
916
  const tagAllKeys = baggageTagKeys.has('*')
793
917
  /** @type {Record<string, string> | undefined} */
794
918
  let items
795
919
  let itemCount = 0
796
920
  let byteCount = 0
921
+ let start = 0
797
922
 
798
- for (const keyValue of baggages) {
799
- if (itemCount >= this._config.baggageMaxItems) {
923
+ while (start <= header.length) {
924
+ if (itemCount >= this.#config.baggageMaxItems) {
800
925
  tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_item_count_exceeded']).inc()
801
926
  break
802
927
  }
928
+
929
+ const memberStart = start
930
+ const commaIndex = header.indexOf(',', memberStart)
931
+ const end = commaIndex === -1 ? header.length : commaIndex
932
+
803
933
  // Charge the comma slot before the empty-entry skip so a `,,,,,foo=bar` can't iterate for free.
804
- byteCount += keyValue.length + 1
805
- if (byteCount > this._config.baggageMaxBytes) {
934
+ byteCount += end - memberStart + 1
935
+ if (byteCount > this.#config.baggageMaxBytes) {
806
936
  tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_byte_count_exceeded']).inc()
807
937
  break
808
938
  }
809
- if (!keyValue) continue
939
+ if (memberStart === end) {
940
+ start = end + 1
941
+ continue
942
+ }
810
943
 
811
944
  // Per W3C baggage, list-members can contain optional properties after `;`.
812
945
  // Example: key=value;prop=1;prop2
813
946
  // https://www.w3.org/TR/baggage/#header-content
814
- const semicolonIdx = keyValue.indexOf(';')
815
- const member = (semicolonIdx === -1 ? keyValue : keyValue.slice(0, semicolonIdx)).trim()
816
- if (!member) continue
817
-
818
- const eqIdx = member.indexOf('=')
819
- if (eqIdx === -1) {
947
+ let memberEnd = header.indexOf(';', memberStart)
948
+ if (memberEnd === -1 || memberEnd > end) memberEnd = end
949
+ const equalsIndex = header.indexOf('=', memberStart)
950
+ start = end + 1
951
+
952
+ if (equalsIndex === -1 || equalsIndex >= memberEnd) {
953
+ const member = header.slice(memberStart, memberEnd).trim()
954
+ if (!member) continue
820
955
  tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
821
956
  return
822
957
  }
823
958
 
824
- const key = member.slice(0, eqIdx).trim()
825
- let value = member.slice(eqIdx + 1).trim()
959
+ const key = header.slice(memberStart, equalsIndex).trim()
960
+ let value = header.slice(equalsIndex + 1, memberEnd).trim()
826
961
 
827
962
  if (!baggageTokenExpr.test(key) || !value) {
828
963
  tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
@@ -855,104 +990,46 @@ class TextMapPropagator {
855
990
  }
856
991
  }
857
992
 
858
- _extractSamplingPriority (carrier, spanContext) {
859
- const header = readDatadogSamplingPriority(carrier)
860
- if (header === undefined) return
861
- const priority = Number.parseInt(header, 10)
862
-
863
- if (Number.isInteger(priority)) {
864
- spanContext._sampling.priority = priority
865
- }
866
- }
867
-
868
- _extractTags (carrier, spanContext) {
993
+ /**
994
+ * @param {Record<string, unknown>} carrier
995
+ * @returns {Record<string, string> | undefined}
996
+ */
997
+ #extractTags (carrier) {
869
998
  const header = readDatadogTags(carrier)
870
999
  if (!header) return
871
1000
 
872
- const trace = spanContext._trace
873
-
874
- if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
1001
+ if (this.#config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
875
1002
  log.debug('Trace tag propagation is disabled, skipping extraction.')
876
- } else if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
1003
+ } else if (header.length > this.#config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
877
1004
  log.error('Trace tags from carrier are too large, skipping extraction.')
878
1005
  } else {
879
- const pairs = header.split(',')
880
1006
  const tags = {}
1007
+ let start = 0
1008
+ let separator = header.indexOf('=')
881
1009
 
882
- for (const pair of pairs) {
883
- const [key, ...rest] = pair.split('=')
884
- const value = rest.join('=')
1010
+ while (start <= header.length) {
1011
+ const comma = header.indexOf(',', start)
1012
+ const end = comma === -1 ? header.length : comma
1013
+ const hasSeparator = separator !== -1 && separator < end
1014
+ const key = header.slice(start, hasSeparator ? separator : end)
1015
+ const value = hasSeparator ? header.slice(separator + 1, end) : ''
885
1016
 
886
- if (!this._validateTagKey(key) || !this._validateTagValue(value)) {
1017
+ if (!tagKeyExpr.test(key) || !tagValueExpr.test(value)) {
887
1018
  log.error('Trace tags from carrier are invalid, skipping extraction.')
888
1019
  return
889
1020
  }
890
- // Check if value is a valid 16 character lower-case hexadecimal encoded number as per spec
891
- if (key === '_dd.p.tid' && !(hex16.test(value))) {
1021
+ if (key === '_dd.p.tid' && !hex16.test(value)) {
892
1022
  log.error('Invalid _dd.p.tid tag %s, skipping', value)
893
- continue
1023
+ } else {
1024
+ tags[key] = value
894
1025
  }
895
- tags[key] = value
896
- }
897
-
898
- Object.assign(trace.tags, tags)
899
- }
900
- }
901
-
902
- _extract128BitTraceId (traceId, spanContext) {
903
- if (!spanContext) return
904
-
905
- const buffer = spanContext._traceId.toBuffer()
906
-
907
- if (buffer.length !== 16) return
908
-
909
- const tid = traceId.slice(0, 16)
910
-
911
- if (tid === zeroTraceId) return
912
1026
 
913
- spanContext._trace.tags['_dd.p.tid'] = tid
914
- }
915
-
916
- _validateTagKey (key) {
917
- return tagKeyExpr.test(key)
918
- }
919
-
920
- _validateTagValue (value) {
921
- return tagValueExpr.test(value)
922
- }
923
-
924
- _getPriority (sampled, debug) {
925
- if (debug) {
926
- return USER_KEEP
927
- } else if (sampled === '1') {
928
- return AUTO_KEEP
929
- } else if (sampled === '0') {
930
- return AUTO_REJECT
931
- }
932
- }
933
-
934
- /**
935
- * @param {number} traceparentSampled
936
- * @param {number|undefined} tracestateSamplingPriority
937
- * @param {string|null} origin
938
- * @returns {import('../../priority_sampler').SamplingPriority}
939
- */
940
- static _getSamplingPriority (traceparentSampled, tracestateSamplingPriority, origin) {
941
- const fromRumWithoutPriority = !tracestateSamplingPriority && origin === 'rum'
942
-
943
- let samplingPriority =
944
- /** @type {import('../../priority_sampler').SamplingPriority} */ (tracestateSamplingPriority ?? AUTO_KEEP)
945
- if (!fromRumWithoutPriority) {
946
- if (traceparentSampled === 0 &&
947
- (!tracestateSamplingPriority || tracestateSamplingPriority >= 0)) {
948
- samplingPriority = AUTO_REJECT
949
- } else if (traceparentSampled === 1 &&
950
- (!tracestateSamplingPriority || tracestateSamplingPriority < 0)) {
951
- samplingPriority = AUTO_KEEP
1027
+ start = end + 1
1028
+ if (hasSeparator) separator = header.indexOf('=', start)
952
1029
  }
953
- }
954
1030
 
955
- return samplingPriority
1031
+ return tags
1032
+ }
956
1033
  }
957
1034
  }
958
1035