dd-trace 5.104.0 → 5.106.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 (159) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +82 -3
  3. package/package.json +15 -15
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +16 -2
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
  11. package/packages/datadog-instrumentations/src/cucumber.js +390 -157
  12. package/packages/datadog-instrumentations/src/dns.js +54 -18
  13. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  14. package/packages/datadog-instrumentations/src/graphql.js +188 -62
  15. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  18. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  19. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  20. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
  27. package/packages/datadog-instrumentations/src/hono.js +54 -3
  28. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  29. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  30. package/packages/datadog-instrumentations/src/jest.js +360 -150
  31. package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
  32. package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
  33. package/packages/datadog-instrumentations/src/nats.js +182 -0
  34. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  35. package/packages/datadog-instrumentations/src/openai.js +33 -18
  36. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  37. package/packages/datadog-instrumentations/src/pino.js +17 -5
  38. package/packages/datadog-instrumentations/src/playwright.js +515 -292
  39. package/packages/datadog-instrumentations/src/router.js +76 -32
  40. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  41. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  42. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
  43. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
  44. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  45. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  46. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  47. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  48. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  49. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  50. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
  51. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  52. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  53. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  54. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  55. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  56. package/packages/datadog-plugin-http/src/server.js +40 -15
  57. package/packages/datadog-plugin-jest/src/index.js +11 -3
  58. package/packages/datadog-plugin-jest/src/util.js +15 -8
  59. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  60. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
  61. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  62. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  63. package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
  64. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  65. package/packages/datadog-plugin-nats/src/index.js +20 -0
  66. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  67. package/packages/datadog-plugin-nats/src/util.js +33 -0
  68. package/packages/datadog-plugin-next/src/index.js +5 -3
  69. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  70. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  71. package/packages/datadog-plugin-pino/src/index.js +42 -0
  72. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  73. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  74. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  75. package/packages/datadog-plugin-router/src/index.js +33 -44
  76. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  77. package/packages/datadog-plugin-vitest/src/index.js +5 -13
  78. package/packages/datadog-plugin-winston/src/index.js +30 -0
  79. package/packages/datadog-shimmer/src/shimmer.js +33 -40
  80. package/packages/dd-trace/src/aiguard/index.js +1 -1
  81. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  82. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  83. package/packages/dd-trace/src/appsec/index.js +1 -1
  84. package/packages/dd-trace/src/appsec/reporter.js +5 -6
  85. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  86. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  87. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  88. package/packages/dd-trace/src/baggage.js +7 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  90. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  91. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  92. package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
  93. package/packages/dd-trace/src/config/supported-configurations.json +27 -8
  94. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  95. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  96. package/packages/dd-trace/src/encode/0.4.js +124 -108
  97. package/packages/dd-trace/src/encode/0.5.js +114 -26
  98. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
  99. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  100. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  101. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  102. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  103. package/packages/dd-trace/src/id.js +15 -0
  104. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +92 -6
  105. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
  106. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  107. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  108. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  109. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  110. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  111. package/packages/dd-trace/src/llmobs/sdk.js +0 -16
  112. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  113. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  114. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  115. package/packages/dd-trace/src/llmobs/util.js +66 -3
  116. package/packages/dd-trace/src/log/index.js +1 -1
  117. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  118. package/packages/dd-trace/src/msgpack/index.js +96 -2
  119. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  120. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  121. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  122. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  123. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  124. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  125. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +22 -3
  126. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  127. package/packages/dd-trace/src/opentracing/propagation/text_map.js +64 -77
  128. package/packages/dd-trace/src/opentracing/span.js +59 -19
  129. package/packages/dd-trace/src/opentracing/span_context.js +50 -3
  130. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
  131. package/packages/dd-trace/src/plugins/database.js +7 -6
  132. package/packages/dd-trace/src/plugins/index.js +4 -0
  133. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  134. package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
  135. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  136. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  137. package/packages/dd-trace/src/plugins/tracing.js +43 -5
  138. package/packages/dd-trace/src/plugins/util/test.js +236 -13
  139. package/packages/dd-trace/src/plugins/util/web.js +79 -65
  140. package/packages/dd-trace/src/priority_sampler.js +2 -2
  141. package/packages/dd-trace/src/profiling/config.js +10 -23
  142. package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
  143. package/packages/dd-trace/src/profiling/profiler.js +21 -11
  144. package/packages/dd-trace/src/profiling/profilers/wall.js +12 -7
  145. package/packages/dd-trace/src/sampling_rule.js +7 -7
  146. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  147. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  148. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  149. package/packages/dd-trace/src/span_format.js +190 -58
  150. package/packages/dd-trace/src/spanleak.js +1 -1
  151. package/packages/dd-trace/src/standalone/index.js +3 -3
  152. package/packages/dd-trace/src/tagger.js +0 -2
  153. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  154. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  155. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  156. package/vendor/dist/protobufjs/index.js +1 -1
  157. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  158. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  159. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
@@ -35,7 +35,6 @@ const b3FlagsKey = 'x-b3-flags'
35
35
  const b3HeaderKey = 'b3'
36
36
  const sqsdHeaderHey = 'x-aws-sqsd-attr-_datadog'
37
37
  const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)?|[01d])$/i
38
- const baggageExpr = new RegExp(`^${baggagePrefix}(.+)$`)
39
38
  // W3C Baggage key grammar: key = token (RFC 7230).
40
39
  // Spec (up-to-date): "Propagation format for distributed context: Baggage" §3.3.1
41
40
  // https://www.w3.org/TR/baggage/#header-content
@@ -56,6 +55,15 @@ const ddKeys = [traceKey, spanKey, samplingKey, originKey]
56
55
  const b3Keys = [b3TraceKey, b3SpanKey, b3ParentKey, b3SampledKey, b3FlagsKey, b3HeaderKey]
57
56
  const w3cKeys = [traceparentKey, tracestateKey]
58
57
  const logKeys = [...ddKeys, ...b3Keys, ...w3cKeys]
58
+ // Dispatch table for `_extractSpanContext`. `'b3'` resolves to the matching
59
+ // single/multi extractor per instance — see `#b3MethodName` — so it is not in
60
+ // this table. `'baggage'` is consumed by `_extractBaggageItems`, not the loop.
61
+ const EXTRACT_STYLE_METHODS = new Map([
62
+ ['datadog', '_extractDatadogContext'],
63
+ ['tracecontext', '_extractTraceparentContext'],
64
+ ['b3 single header', '_extractB3SingleContext'],
65
+ ['b3multi', '_extractB3MultiContext'],
66
+ ])
59
67
  // Origin value in tracestate replaces '~', ',' and ';' with '_"
60
68
  const tracestateOriginFilter = /[^\x20-\x2B\x2D-\x3A\x3C-\x7D]/g
61
69
  // Tag keys in tracestate replace ' ', ',' and '=' with '_'
@@ -68,27 +76,29 @@ const hex16 = /^[0-9A-Fa-f]{16}$/
68
76
  const percentByte = /%([0-9A-Fa-f]{2})/g
69
77
 
70
78
  class TextMapPropagator {
71
- #extractB3Context
72
-
73
79
  /** @type {Set<string> | undefined} Cached `Set` view of `_config.baggageTagKeys`. */
74
80
  #baggageTagKeysSet
75
81
 
76
82
  /** @type {string[] | undefined} Source array that `#baggageTagKeysSet` was built from. */
77
83
  #baggageTagKeysSetSource
78
84
 
85
+ /** @type {'_extractB3SingleContext' | '_extractB3MultiContext'} */
86
+ #b3MethodName
87
+
79
88
  constructor (config) {
80
89
  this._config = config
81
90
 
82
- // v6: `'b3'` is always single-header. v5: env-name decides — OTEL_PROPAGATORS callers expect
83
- // single, the legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
91
+ // v6: `'b3'` is always single-header. v5: `OTEL_PROPAGATORS` callers
92
+ // expect single, legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
93
+ /* istanbul ignore else: v5 fallback, master ships 6.0.0-pre */
84
94
  if (DD_MAJOR >= 6) {
85
- this.#extractB3Context = this._extractB3SingleContext
95
+ this.#b3MethodName = '_extractB3SingleContext'
86
96
  } else {
87
97
  const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
88
98
  // eslint-disable-next-line eslint-rules/eslint-env-aliases
89
- this.#extractB3Context = envName === 'OTEL_PROPAGATORS'
90
- ? this._extractB3SingleContext
91
- : this._extractB3MultiContext
99
+ this.#b3MethodName = envName === 'OTEL_PROPAGATORS'
100
+ ? '_extractB3SingleContext'
101
+ : '_extractB3MultiContext'
92
102
  }
93
103
  }
94
104
 
@@ -129,8 +139,7 @@ class TextMapPropagator {
129
139
 
130
140
  extract (carrier) {
131
141
  const spanContext = this._extractSpanContext(carrier)
132
-
133
- if (!spanContext) return spanContext
142
+ if (spanContext === undefined) return null
134
143
 
135
144
  if (extractCh.hasSubscribers) {
136
145
  extractCh.publish({ spanContext, carrier })
@@ -275,7 +284,7 @@ class TextMapPropagator {
275
284
  (DD_MAJOR < 6 && this._hasPropagationStyle('inject', 'b3'))
276
285
  if (!hasB3multi) return
277
286
 
278
- carrier[b3TraceKey] = this._getB3TraceId(spanContext)
287
+ carrier[b3TraceKey] = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
279
288
  carrier[b3SpanKey] = spanContext._spanId.toString(16)
280
289
  carrier[b3SampledKey] = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
281
290
 
@@ -292,9 +301,9 @@ class TextMapPropagator {
292
301
  // v6 keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
293
302
  const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header') ||
294
303
  (DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
295
- if (!hasB3SingleHeader) return null
304
+ if (!hasB3SingleHeader) return
296
305
 
297
- const traceId = this._getB3TraceId(spanContext)
306
+ const traceId = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
298
307
  const spanId = spanContext._spanId.toString(16)
299
308
  const sampled = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
300
309
 
@@ -361,7 +370,7 @@ class TextMapPropagator {
361
370
  }
362
371
 
363
372
  _hasTraceIdConflict (w3cSpanContext, firstSpanContext) {
364
- return w3cSpanContext !== null &&
373
+ return w3cSpanContext !== undefined &&
365
374
  firstSpanContext.toTraceId(true) === w3cSpanContext.toTraceId(true) &&
366
375
  firstSpanContext.toSpanId() !== w3cSpanContext.toSpanId()
367
376
  }
@@ -372,7 +381,7 @@ class TextMapPropagator {
372
381
 
373
382
  _updateParentIdFromDdHeaders (carrier, firstSpanContext) {
374
383
  const ddCtx = this._extractDatadogContext(carrier)
375
- if (ddCtx !== null) {
384
+ if (ddCtx !== undefined) {
376
385
  firstSpanContext._trace.tags[tags.DD_PARENT_ID] = ddCtx._spanId.toString().padStart(16, '0')
377
386
  }
378
387
  }
@@ -394,35 +403,20 @@ class TextMapPropagator {
394
403
  }
395
404
 
396
405
  _extractSpanContext (carrier) {
397
- let context = null
406
+ let context
398
407
  let style = ''
399
408
  for (const extractor of this._config.tracePropagationStyle.extract) {
400
- let extractedContext = null
401
- switch (extractor) {
402
- case 'datadog':
403
- extractedContext = this._extractDatadogContext(carrier)
404
- break
405
- case 'tracecontext':
406
- extractedContext = this._extractTraceparentContext(carrier)
407
- break
408
- case 'b3 single header':
409
- extractedContext = this._extractB3SingleContext(carrier)
410
- break
411
- case 'b3':
412
- extractedContext = this.#extractB3Context(carrier)
413
- break
414
- case 'b3multi':
415
- extractedContext = this._extractB3MultiContext(carrier)
416
- break
417
- default:
418
- if (extractor !== 'baggage') log.warn('Unknown propagation style:', extractor)
409
+ const method = extractor === 'b3' ? this.#b3MethodName : EXTRACT_STYLE_METHODS.get(extractor)
410
+ if (method === undefined) {
411
+ if (extractor !== 'baggage') log.warn('Unknown propagation style:', extractor)
412
+ continue
419
413
  }
420
-
421
- if (extractedContext === null) { // If the current extractor was invalid, continue to the next extractor
414
+ const extractedContext = this[method](carrier)
415
+ if (extractedContext === undefined) {
422
416
  continue
423
417
  }
424
418
 
425
- if (context === null) {
419
+ if (context === undefined) {
426
420
  context = extractedContext
427
421
  style = extractor
428
422
  if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
@@ -446,9 +440,7 @@ class TextMapPropagator {
446
440
  }
447
441
 
448
442
  if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'ignore') {
449
- // `context` is null when no extractor matched; the fallback below picks up
450
- // the SQSD context if present, otherwise the request runs untraced.
451
- if (context) context._links = []
443
+ if (context !== undefined) context._links = []
452
444
  } else {
453
445
  if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'restart' && context) {
454
446
  context._links = []
@@ -490,14 +482,17 @@ class TextMapPropagator {
490
482
 
491
483
  _extractB3MultiContext (carrier) {
492
484
  const b3 = this._extractB3MultipleHeaders(carrier)
493
- if (!b3) return null
485
+ if (b3 === undefined) return
494
486
  return this._extractB3Context(b3)
495
487
  }
496
488
 
497
489
  _extractB3SingleContext (carrier) {
498
- if (!b3HeaderExpr.test(carrier[b3HeaderKey])) return null
490
+ // `typeof === 'string'` first; otherwise the regex coerces `undefined` to
491
+ // `'undefined'` and runs on every header-less request.
492
+ const header = carrier[b3HeaderKey]
493
+ if (typeof header !== 'string' || !b3HeaderExpr.test(header)) return
499
494
  const b3 = this._extractB3SingleHeader(carrier)
500
- if (!b3) return null
495
+ if (b3 === undefined) return
501
496
  return this._extractB3Context(b3)
502
497
  }
503
498
 
@@ -527,23 +522,19 @@ class TextMapPropagator {
527
522
 
528
523
  _extractSqsdContext (carrier) {
529
524
  const headerValue = carrier[sqsdHeaderHey]
530
- if (!headerValue) {
531
- return null
532
- }
525
+ if (!headerValue) return
533
526
  let parsed
534
527
  try {
535
528
  parsed = JSON.parse(headerValue)
536
529
  } catch {
537
- return null
530
+ return
538
531
  }
539
532
  return this._extractDatadogContext(parsed)
540
533
  }
541
534
 
542
535
  _extractTraceparentContext (carrier) {
543
536
  const headerValue = carrier[traceparentKey]
544
- if (typeof headerValue !== 'string') {
545
- return null
546
- }
537
+ if (typeof headerValue !== 'string') return
547
538
  const matches = headerValue.trim().match(traceparentExpr)
548
539
  if (matches !== null) {
549
540
  const [, version, traceId, spanId, flags, tail] = matches
@@ -554,14 +545,14 @@ class TextMapPropagator {
554
545
  ? carrier.tracestate.filter(item => typeof item === 'string').join(',')
555
546
  : carrier.tracestate
556
547
  const tracestate = TraceState.fromString(rawTracestate)
557
- if (invalidSegment.test(traceId)) return null
558
- if (invalidSegment.test(spanId)) return null
548
+ if (invalidSegment.test(traceId)) return
549
+ if (invalidSegment.test(spanId)) return
559
550
 
560
551
  // Version ff is considered invalid
561
- if (version === 'ff') return null
552
+ if (version === 'ff') return
562
553
 
563
554
  // Version 00 should have no tail, but future versions may
564
- if (tail && version === '00') return null
555
+ if (tail && version === '00') return
565
556
 
566
557
  const spanContext = new DatadogSpanContext({
567
558
  traceId: id(traceId, 16),
@@ -624,12 +615,11 @@ class TextMapPropagator {
624
615
  this._extractLegacyBaggageItems(carrier, spanContext)
625
616
  return spanContext
626
617
  }
627
- return null
628
618
  }
629
619
 
630
620
  _extractGenericContext (carrier, traceKey, spanKey, radix) {
631
621
  if (carrier && carrier[traceKey] && carrier[spanKey]) {
632
- if (invalidSegment.test(carrier[traceKey])) return null
622
+ if (invalidSegment.test(carrier[traceKey])) return
633
623
 
634
624
  return new DatadogSpanContext({
635
625
  traceId: id(carrier[traceKey], radix),
@@ -637,11 +627,17 @@ class TextMapPropagator {
637
627
  isRemote: true,
638
628
  })
639
629
  }
640
-
641
- return null
642
630
  }
643
631
 
644
632
  _extractB3MultipleHeaders (carrier) {
633
+ // `b3ParentKey` is intentionally absent: this method never consults it,
634
+ // so a parent-id-only carrier should bail with the rest.
635
+ if (carrier[b3TraceKey] === undefined &&
636
+ carrier[b3SampledKey] === undefined &&
637
+ carrier[b3FlagsKey] === undefined) {
638
+ return
639
+ }
640
+
645
641
  let empty = true
646
642
  const b3 = {}
647
643
 
@@ -661,12 +657,12 @@ class TextMapPropagator {
661
657
  empty = false
662
658
  }
663
659
 
664
- return empty ? null : b3
660
+ return empty ? undefined : b3
665
661
  }
666
662
 
667
663
  _extractB3SingleHeader (carrier) {
668
664
  const header = carrier[b3HeaderKey]
669
- if (!header) return null
665
+ if (!header) return
670
666
 
671
667
  const parts = header.split('-')
672
668
 
@@ -705,13 +701,12 @@ class TextMapPropagator {
705
701
  }
706
702
 
707
703
  _extractLegacyBaggageItems (carrier, spanContext) {
708
- if (this._config.legacyBaggageEnabled) {
709
- for (const key of Object.keys(carrier)) {
710
- const match = key.match(baggageExpr)
711
-
712
- if (match) {
713
- spanContext._baggageItems[match[1]] = carrier[key]
714
- }
704
+ if (!this._config.legacyBaggageEnabled) return
705
+ for (const key of Object.keys(carrier)) {
706
+ if (!key.startsWith(baggagePrefix)) continue
707
+ const baggageKey = key.slice(baggagePrefix.length)
708
+ if (baggageKey) {
709
+ spanContext._baggageItems[baggageKey] = carrier[key]
715
710
  }
716
711
  }
717
712
  }
@@ -864,14 +859,6 @@ class TextMapPropagator {
864
859
  }
865
860
  }
866
861
 
867
- _getB3TraceId (spanContext) {
868
- if (spanContext._traceId.toBuffer().length <= 8 && spanContext._trace.tags['_dd.p.tid']) {
869
- return spanContext._trace.tags['_dd.p.tid'] + spanContext._traceId.toString(16)
870
- }
871
-
872
- return spanContext._traceId.toString(16)
873
- }
874
-
875
862
  /**
876
863
  * @param {number} traceparentSampled
877
864
  * @param {number|undefined} tracestateSamplingPriority
@@ -10,7 +10,10 @@ const tagger = require('../tagger')
10
10
  const runtimeMetrics = require('../runtime_metrics')
11
11
  const log = require('../log')
12
12
  const { storage } = require('../../../datadog-core')
13
+ const { resolveServiceSource } = require('../service-naming/source-resolver')
13
14
  const telemetryMetrics = require('../telemetry/metrics')
15
+ const { MANUAL_DROP, MANUAL_KEEP, SAMPLING_PRIORITY } = require('../../../../ext/tags')
16
+ const { DD_MAJOR } = require('../../../../version')
14
17
  const SpanContext = require('./span_context')
15
18
 
16
19
  const dateNow = Date.now
@@ -103,7 +106,7 @@ class DatadogSpan {
103
106
 
104
107
  this._spanContext = this._createContext(parent, fields)
105
108
  this._spanContext._name = operationName
106
- this._spanContext._tags = tags
109
+ Object.assign(this._spanContext.getTags(), tags)
107
110
  this._spanContext._hostname = hostname
108
111
 
109
112
  this._spanContext._trace.started.push(this)
@@ -145,7 +148,7 @@ class DatadogSpan {
145
148
 
146
149
  toString () {
147
150
  const spanContext = this.context()
148
- const resourceName = spanContext._tags['resource.name'] || ''
151
+ const resourceName = spanContext.getTag('resource.name') || ''
149
152
  const resource = resourceName.length > 100
150
153
  ? `${resourceName.slice(0, 97)}...`
151
154
  : resourceName
@@ -153,7 +156,7 @@ class DatadogSpan {
153
156
  traceId: spanContext._traceId,
154
157
  spanId: spanContext._spanId,
155
158
  parentId: spanContext._parentId,
156
- service: spanContext._tags['service.name'],
159
+ service: spanContext.getTag('service.name'),
157
160
  name: spanContext._name,
158
161
  resource,
159
162
  })
@@ -199,12 +202,52 @@ class DatadogSpan {
199
202
  }
200
203
 
201
204
  setTag (key, value) {
202
- this._addTags({ [key]: value })
205
+ this._spanContext.setTag(key, value)
206
+
207
+ if (isSamplingPriorityTag(key) && this._spanContext._sampling.priority === undefined) {
208
+ this._prioritySampler.sample(this, false)
209
+ }
210
+
211
+ if (tagsUpdateCh.hasSubscribers) {
212
+ tagsUpdateCh.publish(this)
213
+ }
214
+
203
215
  return this
204
216
  }
205
217
 
206
218
  addTags (keyValueMap) {
207
- this._addTags(keyValueMap)
219
+ // v6 hot path: `Object.assign` straight onto the live tag map. The
220
+ // string and array shapes never appeared in the public TypeScript
221
+ // surface, and no internal v6 caller passes one (see MIGRATING.md).
222
+ // v5 still accepts both via `tagger.add` for `config.tags` /
223
+ // `options.tags` callers that pass `'key:val,key:val'` strings.
224
+ const tags = this._spanContext.getTags()
225
+ let mayChangeSamplingPriority
226
+
227
+ if (keyValueMap !== null && typeof keyValueMap === 'object' && !Array.isArray(keyValueMap)) {
228
+ Object.assign(tags, keyValueMap)
229
+ mayChangeSamplingPriority =
230
+ MANUAL_KEEP in keyValueMap ||
231
+ MANUAL_DROP in keyValueMap ||
232
+ SAMPLING_PRIORITY in keyValueMap
233
+ } else {
234
+ /* istanbul ignore if: v5 fallback, master ships 6.0.0-pre */
235
+ if (DD_MAJOR < 6 && (typeof keyValueMap === 'string' || Array.isArray(keyValueMap))) {
236
+ tagger.add(tags, keyValueMap)
237
+ mayChangeSamplingPriority = true
238
+ } else {
239
+ return this
240
+ }
241
+ }
242
+
243
+ if (mayChangeSamplingPriority && this._spanContext._sampling.priority === undefined) {
244
+ this._prioritySampler.sample(this, false)
245
+ }
246
+
247
+ if (tagsUpdateCh.hasSubscribers) {
248
+ tagsUpdateCh.publish(this)
249
+ }
250
+
208
251
  return this
209
252
  }
210
253
 
@@ -215,8 +258,9 @@ class DatadogSpan {
215
258
  logEvent () {}
216
259
 
217
260
  addLink (link, attrs) {
218
- // TODO: Remove this once we remove addLink(context, attrs) in v6.0.0
219
- if (link instanceof SpanContext) {
261
+ // v5 still accepts the legacy `addLink(spanContext, attrs)` shape; v6 only takes
262
+ // `addLink({ context, attributes })`.
263
+ if (DD_MAJOR < 6 && link instanceof SpanContext) {
220
264
  link = { context: link, attributes: attrs ?? {} }
221
265
  }
222
266
 
@@ -267,12 +311,14 @@ class DatadogSpan {
267
311
  return
268
312
  }
269
313
 
270
- if (this.#parentTracer._config.DD_TRACE_EXPERIMENTAL_STATE_TRACKING && !this._spanContext._tags['service.name']) {
314
+ if (this.#parentTracer._config.DD_TRACE_EXPERIMENTAL_STATE_TRACKING && !this._spanContext.getTag('service.name')) {
271
315
  log.error('Finishing invalid span: %s', this)
272
316
  }
273
317
 
274
318
  getIntegrationCounter('spans_finished', this._integrationName).inc()
275
- this._spanContext._tags['_dd.integration'] = this._integrationName
319
+ this._spanContext.setTag('_dd.integration', this._integrationName)
320
+
321
+ resolveServiceSource(this, this.#parentTracer._service)
276
322
 
277
323
  if (this.#parentTracer._config.DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
278
324
  runtimeMetrics.decrement('runtime.node.spans.unfinished')
@@ -404,16 +450,6 @@ class DatadogSpan {
404
450
 
405
451
  return startTime + now() - ticks
406
452
  }
407
-
408
- _addTags (keyValuePairs) {
409
- tagger.add(this._spanContext._tags, keyValuePairs)
410
-
411
- this._prioritySampler.sample(this, false)
412
-
413
- if (tagsUpdateCh.hasSubscribers) {
414
- tagsUpdateCh.publish(this)
415
- }
416
- }
417
453
  }
418
454
 
419
455
  function createRegistry (type) {
@@ -423,4 +459,8 @@ function createRegistry (type) {
423
459
  })
424
460
  }
425
461
 
462
+ function isSamplingPriorityTag (key) {
463
+ return key === MANUAL_KEEP || key === MANUAL_DROP || key === SAMPLING_PRIORITY
464
+ }
465
+
426
466
  module.exports = DatadogSpan
@@ -46,9 +46,7 @@ class DatadogSpanContext {
46
46
 
47
47
  toTraceId (get128bitId = false) {
48
48
  if (get128bitId) {
49
- return this._traceId.toBuffer().length <= 8 && this._trace.tags[TRACE_ID_128]
50
- ? this._trace.tags[TRACE_ID_128] + this._traceId.toString(16).padStart(16, '0')
51
- : this._traceId.toString(16).padStart(32, '0')
49
+ return this._traceId.toTraceIdHex(this._trace.tags[TRACE_ID_128]).padStart(32, '0')
52
50
  }
53
51
  return this._traceId.toString(10)
54
52
  }
@@ -71,6 +69,55 @@ class DatadogSpanContext {
71
69
  const version = (this._traceparent && this._traceparent.version) || '00'
72
70
  return `${version}-${traceId}-${spanId}-${flags}`
73
71
  }
72
+
73
+ /**
74
+ * Set a tag value.
75
+ * @param {string} key - Tag key
76
+ * @param {unknown} value - Tag value
77
+ */
78
+ setTag (key, value) {
79
+ this._tags[key] = value
80
+ }
81
+
82
+ /**
83
+ * Get a tag value.
84
+ * @param {string} key - Tag key
85
+ * @returns {unknown} Tag value or undefined
86
+ */
87
+ getTag (key) {
88
+ return this._tags[key]
89
+ }
90
+
91
+ /**
92
+ * Check if a tag exists.
93
+ * @param {string} key - Tag key
94
+ * @returns {boolean}
95
+ */
96
+ hasTag (key) { return Object.hasOwn(this._tags, key) }
97
+
98
+ /**
99
+ * Delete a tag.
100
+ * @param {string} key - Tag key
101
+ */
102
+ deleteTag (key) { delete this._tags[key] }
103
+
104
+ /**
105
+ * Get the live internal tags map. The returned reference is mutable;
106
+ * callers may assign or delete keys directly (e.g.
107
+ * `Object.assign(getTags(), tags)` in span.js). Subclasses may have
108
+ * additional sync side effects on the individual `setTag` / `deleteTag`
109
+ * setters; mutating the returned map bypasses those.
110
+ *
111
+ * @returns {object}
112
+ */
113
+ getTags () {
114
+ return this._tags
115
+ }
116
+
117
+ /**
118
+ * Clear all tags.
119
+ */
120
+ clearTags () { this._tags = Object.create(null) }
74
121
  }
75
122
 
76
123
  module.exports = DatadogSpanContext
@@ -52,7 +52,6 @@ const {
52
52
  TEST_ITR_SKIPPING_ENABLED,
53
53
  ITR_CORRELATION_ID,
54
54
  TEST_SOURCE_FILE,
55
- TEST_LEVEL_EVENT_TYPES,
56
55
  TEST_SUITE,
57
56
  getFileAndLineNumberFromError,
58
57
  DI_ERROR_DEBUG_INFO_CAPTURED,
@@ -128,7 +127,6 @@ function getTestSuiteLevelVisibilityTags (testSuiteSpan, testFramework) {
128
127
  const suiteTags = {
129
128
  [TEST_SUITE_ID]: testSuiteSpanContext.toSpanId(),
130
129
  [TEST_SESSION_ID]: testSuiteSpanContext.toTraceId(),
131
- [TEST_COMMAND]: testSuiteSpanContext._tags[TEST_COMMAND],
132
130
  [TEST_MODULE]: testFramework,
133
131
  }
134
132
 
@@ -174,7 +172,7 @@ module.exports = class CiPlugin extends Plugin {
174
172
  },
175
173
  }
176
174
  this.tracer._exporter.addMetadataTags(metadataTags)
177
- onDone({ err, libraryConfig, requestErrorTags })
175
+ onDone({ err, libraryConfig, repositoryRoot: this.repositoryRoot, requestErrorTags })
178
176
  })
179
177
  })
180
178
 
@@ -186,15 +184,22 @@ module.exports = class CiPlugin extends Plugin {
186
184
  if (!this.tracer._exporter?.getSkippableSuites) {
187
185
  return onDone({ err: new Error('Test optimization was not initialized correctly') })
188
186
  }
189
- this.tracer._exporter.getSkippableSuites(this.testConfiguration, (err, skippableSuites, itrCorrelationId) => {
190
- if (err) {
191
- log.error('Skippable suites could not be fetched. %s', err.message)
192
- this._addRequestErrorTag(DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, err)
193
- } else {
194
- this.itrCorrelationId = itrCorrelationId
187
+ this.tracer._exporter.getSkippableSuites(
188
+ {
189
+ ...this.testConfiguration,
190
+ isCoverageReportUploadEnabled: this.libraryConfig?.isCoverageReportUploadEnabled,
191
+ },
192
+ (err, skippableSuites, itrCorrelationId, skippableSuitesCoverage) => {
193
+ if (err) {
194
+ log.error('Skippable suites could not be fetched. %s', err.message)
195
+ this._addRequestErrorTag(DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, err)
196
+ } else {
197
+ this.itrCorrelationId = itrCorrelationId
198
+ this.skippableSuitesCoverage = skippableSuitesCoverage
199
+ }
200
+ onDone({ err, skippableSuites, itrCorrelationId, skippableSuitesCoverage })
195
201
  }
196
- onDone({ err, skippableSuites, itrCorrelationId })
197
- })
202
+ )
198
203
  })
199
204
 
200
205
  this.addSub(`ci:${this.constructor.id}:session:start`, ({ command, frameworkVersion, rootDir }) => {
@@ -213,12 +218,7 @@ module.exports = class CiPlugin extends Plugin {
213
218
  this.testEnvironmentMetadata
214
219
  )
215
220
 
216
- const metadataTags = {}
217
- for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
218
- metadataTags[testLevel] = {
219
- [TEST_SESSION_NAME]: testSessionName,
220
- }
221
- }
221
+ const metadataTags = { '*': { [TEST_COMMAND]: command, [TEST_SESSION_NAME]: testSessionName } }
222
222
  // tracer might not be initialized correctly
223
223
  if (this.tracer._exporter.addMetadataTags) {
224
224
  this.tracer._exporter.addMetadataTags(metadataTags)
@@ -255,7 +255,7 @@ module.exports = class CiPlugin extends Plugin {
255
255
  })
256
256
 
257
257
  this.addSub(`ci:${this.constructor.id}:itr:skipped-suites`, ({ skippedSuites, frameworkVersion }) => {
258
- const testCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
258
+ const testCommand = this.command
259
259
  for (const testSuite of skippedSuites) {
260
260
  const testSuiteMetadata = {
261
261
  ...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, this.constructor.id),
@@ -615,7 +615,7 @@ module.exports = class CiPlugin extends Plugin {
615
615
  const suiteTags = {
616
616
  [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(),
617
617
  [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
618
- [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
618
+ [TEST_COMMAND]: testSuiteSpan.context().getTag(TEST_COMMAND),
619
619
  [TEST_MODULE]: this.constructor.id,
620
620
  ...getSessionRequestErrorTags(this.testSessionSpan),
621
621
  }
@@ -808,7 +808,7 @@ module.exports = class CiPlugin extends Plugin {
808
808
  }
809
809
 
810
810
  getTestTelemetryTags (testSpan) {
811
- const activeSpanTags = testSpan.context()._tags
811
+ const activeSpanTags = testSpan.context().getTags()
812
812
  return {
813
813
  hasCodeOwners: !!activeSpanTags[TEST_CODE_OWNERS] || undefined,
814
814
  isNew: activeSpanTags[TEST_IS_NEW] === 'true' || undefined,
@@ -49,7 +49,7 @@ class DatabasePlugin extends StoragePlugin {
49
49
  * @returns {string}
50
50
  */
51
51
  #createDBMPropagationCommentService (serviceName, span, peerData) {
52
- const spanTags = span.context()._tags
52
+ const spanTags = span.context().getTags()
53
53
  const dddb = spanTags['db.name']
54
54
  const ddh = spanTags['out.host']
55
55
  const cacheKey = `${dddb ?? ''}\0${ddh ?? ''}\0${serviceName ?? ''}`
@@ -91,23 +91,24 @@ class DatabasePlugin extends StoragePlugin {
91
91
  return null
92
92
  }
93
93
 
94
- const peerData = this.getPeerService(span.context()._tags)
94
+ const peerData = this.getPeerService(span.context().getTags())
95
95
  const dbmService = this.#getDbmServiceName(serviceName, peerData)
96
96
  const servicePropagation = this.#createDBMPropagationCommentService(dbmService, span, peerData)
97
97
 
98
98
  let dbmComment = servicePropagation
99
99
 
100
- // Add propagation hash if both process tags and SQL base hash injection are enabled
101
- if (propagationHash.isEnabled() && this.config['dbm.injectSqlBaseHash']) {
100
+ // Add propagation hash if process tags are enabled and either SQL base hash injection is enabled
101
+ // or dynamic_service mode implicitly enables it
102
+ if (propagationHash.isEnabled() && (this.config['dbm.injectSqlBaseHash'] || mode === 'dynamic_service')) {
102
103
  const hashBase64 = propagationHash.getHashBase64()
103
104
  if (hashBase64) {
104
105
  dbmComment += `,ddsh='${hashBase64}'`
105
106
  // Add hash to span meta as a tag
106
- span.setTag('_dd.dbm.propagation_hash', hashBase64)
107
+ span.setTag('_dd.propagated_hash', hashBase64)
107
108
  }
108
109
  }
109
110
 
110
- if (disableFullMode || mode === 'service') {
111
+ if (disableFullMode || mode === 'service' || mode === 'dynamic_service') {
111
112
  return dbmComment
112
113
  } else if (mode === 'full') {
113
114
  span.setTag('_dd.dbm_trace_injected', 'true')