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
@@ -0,0 +1,143 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('dc-polyfill')
4
+ const log = require('../log')
5
+ const { SpanEnrichmentState } = require('./span-enrichment')
6
+
7
+ const finishCh = channel('dd-trace:span:finish')
8
+
9
+ /**
10
+ * OpenFeature hook that enriches APM spans with feature flag evaluation data.
11
+ *
12
+ * Implements the OpenFeature `finally` hook interface to capture flag evaluations
13
+ * and add span tags for observability. Tags are accumulated during the span's
14
+ * lifetime and applied when the root span finishes.
15
+ *
16
+ * Span tags added:
17
+ * - `ffe_flags_enc`: Base64 delta-varint encoded serial IDs
18
+ * - `ffe_subjects_enc`: JSON dict of SHA256(targeting_key) → encoded serial IDs
19
+ * - `ffe_runtime_defaults`: JSON dict of flag_key → default value string
20
+ */
21
+ class SpanEnrichmentHook {
22
+ #tracer
23
+ /** @type {WeakMap<object, SpanEnrichmentState>} */
24
+ #spanStates = new WeakMap()
25
+
26
+ /**
27
+ * Handler for span finish channel. Applies accumulated tags to the span.
28
+ * Arrow function to preserve `this` binding for channel subscription.
29
+ *
30
+ * @param {object} span - The span that is finishing
31
+ */
32
+ #onSpanFinish = (span) => {
33
+ const state = this.#spanStates.get(span)
34
+ if (!state || !state.hasData()) return
35
+
36
+ try {
37
+ const tags = state.toSpanTags()
38
+
39
+ for (const [key, value] of Object.entries(tags)) {
40
+ if (value) {
41
+ span.setTag(key, value)
42
+ }
43
+ }
44
+ } catch (err) {
45
+ log.warn('SpanEnrichmentHook: error applying span tags: %s', err.message)
46
+ } finally {
47
+ this.#spanStates.delete(span)
48
+ }
49
+ }
50
+
51
+ /**
52
+ * @param {import('../tracer')} tracer - Datadog tracer instance
53
+ */
54
+ constructor (tracer) {
55
+ this.#tracer = tracer
56
+ finishCh.subscribe(this.#onSpanFinish)
57
+ }
58
+
59
+ /**
60
+ * Called by the OpenFeature SDK after every flag evaluation (success or error).
61
+ *
62
+ * @param {object} hookContext - Hook context containing the flag key and evaluation context
63
+ * @param {string} hookContext.flagKey - The flag key being evaluated
64
+ * @param {object} [hookContext.context] - Evaluation context
65
+ * @param {string} [hookContext.context.targetingKey] - Targeting key
66
+ * @param {object} evaluationDetails - Full evaluation details including flag metadata
67
+ * @param {object} [evaluationDetails.flagMetadata] - Metadata from the provider
68
+ * @param {number} [evaluationDetails.flagMetadata.__dd_split_serial_id] - Serial ID from UFC split
69
+ * @param {boolean} [evaluationDetails.flagMetadata.__dd_do_log] - Whether to log subject
70
+ * @param {string} [evaluationDetails.variant] - Variant key if flag was found in UFC
71
+ * @param {boolean|string|number|object} [evaluationDetails.value] - Evaluated value
72
+ * @returns {void}
73
+ */
74
+ finally (hookContext, evaluationDetails) {
75
+ try {
76
+ const rootSpan = this._getRootSpan()
77
+ if (!rootSpan) return
78
+
79
+ const state = this._getOrCreateState(rootSpan)
80
+ const { flagKey, context } = hookContext || {}
81
+ const { flagMetadata, variant, value } = evaluationDetails || {}
82
+
83
+ const serialId = flagMetadata?.__dd_split_serial_id
84
+ const doLog = flagMetadata?.__dd_do_log ?? false
85
+ const targetingKey = context?.targetingKey
86
+
87
+ if (serialId != null) {
88
+ state.addSerialId(serialId)
89
+
90
+ if (doLog && targetingKey) {
91
+ state.addSubject(targetingKey, serialId)
92
+ }
93
+ } else if (variant === undefined) {
94
+ state.addDefault(flagKey, value)
95
+ }
96
+ } catch (err) {
97
+ log.warn('SpanEnrichmentHook: error in finally hook: %s', err.message)
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Get the root span for the current trace context.
103
+ * The root span is always the first span in trace.started since spans
104
+ * are added in creation order and the root is created first.
105
+ *
106
+ * @returns {object|null} The root span, or null if no active span
107
+ * @private
108
+ */
109
+ _getRootSpan () {
110
+ const span = this.#tracer.scope().active()
111
+ if (!span) return null
112
+
113
+ const trace = span.context()._trace
114
+
115
+ return trace?.started?.[0] ?? span
116
+ }
117
+
118
+ /**
119
+ * Get or create enrichment state for a span.
120
+ *
121
+ * @param {object} span - The span to get state for
122
+ * @returns {SpanEnrichmentState} The enrichment state
123
+ * @private
124
+ */
125
+ _getOrCreateState (span) {
126
+ let state = this.#spanStates.get(span)
127
+ if (!state) {
128
+ state = new SpanEnrichmentState()
129
+ this.#spanStates.set(span, state)
130
+ }
131
+ return state
132
+ }
133
+
134
+ /**
135
+ * Cleanup method to unsubscribe from channels.
136
+ * Should be called when the provider is shut down.
137
+ */
138
+ destroy () {
139
+ finishCh.unsubscribe(this.#onSpanFinish)
140
+ }
141
+ }
142
+
143
+ module.exports = SpanEnrichmentHook
@@ -0,0 +1,149 @@
1
+ 'use strict'
2
+
3
+ const log = require('../log')
4
+
5
+ const { encodeDeltaVarint, hashTargetingKey } = require('./encoding')
6
+
7
+ const MAX_SERIAL_IDS = 200
8
+ const MAX_SUBJECTS = 10
9
+ const MAX_EXPERIMENTS_PER_SUBJECT = 20
10
+ const MAX_DEFAULTS = 5
11
+ const MAX_DEFAULT_VALUE_LENGTH = 64
12
+
13
+ /**
14
+ * Manages feature flag enrichment state for a single root span.
15
+ * Accumulates serial IDs, subjects, and defaults throughout the span's lifetime.
16
+ */
17
+ class SpanEnrichmentState {
18
+ constructor () {
19
+ /** @type {Set<number>} */
20
+ this._serialIds = new Set()
21
+
22
+ /** @type {Map<string, Set<number>>} hashed targeting key -> serial IDs */
23
+ this._subjects = new Map()
24
+
25
+ /** @type {Map<string, string>} flag key -> runtime default value */
26
+ this._defaults = new Map()
27
+ }
28
+
29
+ /**
30
+ * Add a serial ID from a flag evaluation.
31
+ *
32
+ * @param {number} serialId - The serial ID to add
33
+ * @returns {boolean} True if added, false if limit reached
34
+ */
35
+ addSerialId (serialId) {
36
+ if (this._serialIds.size >= MAX_SERIAL_IDS) {
37
+ log.debug('SpanEnrichment: MAX_SERIAL_IDS limit (%d) reached, dropping serialId %d', MAX_SERIAL_IDS, serialId)
38
+ return false
39
+ }
40
+ this._serialIds.add(serialId)
41
+ return true
42
+ }
43
+
44
+ /**
45
+ * Add a subject (targeting key) with its associated serial ID.
46
+ * Only called when doLog=true.
47
+ *
48
+ * @param {string} targetingKey - The targeting key (will be hashed)
49
+ * @param {number} serialId - The serial ID associated with this evaluation
50
+ * @returns {boolean} True if added, false if limit reached
51
+ */
52
+ addSubject (targetingKey, serialId) {
53
+ const hashedKey = hashTargetingKey(targetingKey)
54
+
55
+ if (this._subjects.has(hashedKey)) {
56
+ const subjectIds = this._subjects.get(hashedKey)
57
+ if (subjectIds.size >= MAX_EXPERIMENTS_PER_SUBJECT) {
58
+ log.debug('SpanEnrichment: MAX_EXPERIMENTS_PER_SUBJECT limit (%d) reached for subject',
59
+ MAX_EXPERIMENTS_PER_SUBJECT)
60
+ return false
61
+ }
62
+ subjectIds.add(serialId)
63
+ return true
64
+ }
65
+
66
+ if (this._subjects.size >= MAX_SUBJECTS) {
67
+ log.debug('SpanEnrichment: MAX_SUBJECTS limit (%d) reached, dropping subject', MAX_SUBJECTS)
68
+ return false
69
+ }
70
+
71
+ this._subjects.set(hashedKey, new Set([serialId]))
72
+ return true
73
+ }
74
+
75
+ /**
76
+ * Add a default fallback for a flag not found in UFC.
77
+ *
78
+ * @param {string} flagKey - The flag key
79
+ * @param {boolean|string|number|object} defaultValue - The default value used
80
+ * @returns {boolean} True if added, false if limit reached
81
+ */
82
+ addDefault (flagKey, defaultValue) {
83
+ if (this._defaults.has(flagKey)) {
84
+ return true
85
+ }
86
+
87
+ if (this._defaults.size >= MAX_DEFAULTS) {
88
+ log.debug('SpanEnrichment: MAX_DEFAULTS limit (%d) reached, dropping flag %s', MAX_DEFAULTS, flagKey)
89
+ return false
90
+ }
91
+
92
+ let valueStr = typeof defaultValue === 'object' && defaultValue !== null
93
+ ? JSON.stringify(defaultValue)
94
+ : String(defaultValue)
95
+
96
+ if (valueStr.length > MAX_DEFAULT_VALUE_LENGTH) {
97
+ valueStr = valueStr.slice(0, MAX_DEFAULT_VALUE_LENGTH)
98
+ }
99
+
100
+ this._defaults.set(flagKey, valueStr)
101
+ return true
102
+ }
103
+
104
+ /**
105
+ * Check if there is any enrichment data to add to the span.
106
+ * Note: _subjects is not checked because addSubject() is never called without first
107
+ * calling addSerialId(), so _subjects having data necessitates _serialIds having data.
108
+ *
109
+ * @returns {boolean} True if there is data to add
110
+ */
111
+ hasData () {
112
+ return this._serialIds.size > 0 || this._defaults.size > 0
113
+ }
114
+
115
+ /**
116
+ * Convert accumulated state to span tags.
117
+ *
118
+ * @returns {object} Object with ffe_flags_enc, ffe_subjects_enc, and ffe_runtime_defaults tags
119
+ */
120
+ toSpanTags () {
121
+ const tags = {}
122
+
123
+ if (this._serialIds.size > 0) {
124
+ tags.ffe_flags_enc = encodeDeltaVarint(this._serialIds)
125
+ }
126
+
127
+ if (this._subjects.size > 0) {
128
+ const subjectsObj = Object.fromEntries(
129
+ [...this._subjects].map(([key, ids]) => [key, encodeDeltaVarint(ids)])
130
+ )
131
+ tags.ffe_subjects_enc = JSON.stringify(subjectsObj)
132
+ }
133
+
134
+ if (this._defaults.size > 0) {
135
+ tags.ffe_runtime_defaults = JSON.stringify(Object.fromEntries(this._defaults))
136
+ }
137
+
138
+ return tags
139
+ }
140
+ }
141
+
142
+ module.exports = {
143
+ SpanEnrichmentState,
144
+ MAX_SERIAL_IDS,
145
+ MAX_SUBJECTS,
146
+ MAX_EXPERIMENTS_PER_SUBJECT,
147
+ MAX_DEFAULTS,
148
+ MAX_DEFAULT_VALUE_LENGTH,
149
+ }
@@ -7,6 +7,7 @@ const { timeInputToHrTime } = require('../../../../vendor/dist/@opentelemetry/co
7
7
  const { ERROR_MESSAGE, ERROR_STACK, ERROR_TYPE, IGNORE_OTEL_ERROR } = require('../constants')
8
8
  const DatadogSpanContext = require('../opentracing/span_context')
9
9
  const TraceState = require('../opentracing/propagation/tracestate')
10
+ const { DD_MAJOR } = require('../../../../version')
10
11
 
11
12
  const id = require('../id')
12
13
 
@@ -176,8 +177,8 @@ function setOtelAttributes (ddSpan, attributes) {
176
177
  function addOtelLink (ddSpan, link, attrs) {
177
178
  if (!isWritable(ddSpan) || !link) return
178
179
 
179
- // TODO: Drop the (context, attrs) form in v6.0.0.
180
- const { context, attributes } = isOtelLink(link)
180
+ // v5 still accepts the legacy `addLink(context, attrs)` shape; v6 only takes `addLink(otel.Link)`.
181
+ const { context, attributes } = isOtelLink(link) || DD_MAJOR >= 6
181
182
  ? link
182
183
  : { context: link, attributes: attrs ?? {} }
183
184
 
@@ -230,7 +231,7 @@ function recordException (ddSpan, exception, timeInput) {
230
231
  [ERROR_TYPE]: exception.name,
231
232
  [ERROR_MESSAGE]: exception.message,
232
233
  [ERROR_STACK]: exception.stack,
233
- [IGNORE_OTEL_ERROR]: ddSpan.context()._tags[IGNORE_OTEL_ERROR] ?? true,
234
+ [IGNORE_OTEL_ERROR]: ddSpan.context().getTag(IGNORE_OTEL_ERROR) ?? true,
234
235
  })
235
236
 
236
237
  const attributes = {}
@@ -216,7 +216,7 @@ class Span extends BridgeSpanBase {
216
216
  'ptr.hash': ptrHash,
217
217
  'link.kind': 'span-pointer',
218
218
  }
219
- return this.addLink(zeroContext, attributes)
219
+ return this.addLink({ context: zeroContext, attributes })
220
220
  }
221
221
 
222
222
  /**
@@ -16,6 +16,11 @@ const SPAN_KIND_CONSUMER = protoSpanKind.values.SPAN_KIND_CONSUMER
16
16
  // Cached zero Identifier used to detect zero IDs without re-allocating per span.
17
17
  const ZERO_ID = id('0')
18
18
 
19
+ // DD propagation tag carrying the upper 64 bits of a 128-bit trace ID as 16 hex chars.
20
+ // span_format.js#extractChunkTags only copies this onto the first-in-chunk span, so the
21
+ // transformer scans the batch to find it and applies it to every span's traceId.
22
+ const TRACE_ID_128 = '_dd.p.tid'
23
+
19
24
  /**
20
25
  * @typedef {import('../../id').Identifier} Identifier
21
26
  *
@@ -65,6 +70,7 @@ const STATUS_CODE_ERROR = 2
65
70
  const EXCLUDED_META_KEYS = new Set([
66
71
  '_dd.span_links',
67
72
  'span.kind',
73
+ TRACE_ID_128,
68
74
  ])
69
75
 
70
76
  /**
@@ -113,6 +119,18 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
113
119
  * @returns {object[]} Array of scope span objects
114
120
  */
115
121
  #transformScopeSpans (spans) {
122
+ let traceKey
123
+ let traceIdHigh
124
+ const otlpSpans = spans.map((span) => {
125
+ // `_dd.p.tid` lives only on the first-in-chunk span of each trace.
126
+ // Reset at each trace boundary for batching of multiple traces.
127
+ const key = span.trace_id.toString(16)
128
+ if (key !== traceKey) {
129
+ traceKey = key
130
+ traceIdHigh = span.meta?.[TRACE_ID_128]?.toLowerCase()
131
+ }
132
+ return this.#transformSpan(span, traceIdHigh)
133
+ })
116
134
  return [{
117
135
  scope: {
118
136
  name: 'dd-trace-js',
@@ -121,7 +139,7 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
121
139
  droppedAttributesCount: 0,
122
140
  },
123
141
  schemaUrl: '',
124
- spans: spans.map(span => this.#transformSpan(span)),
142
+ spans: otlpSpans,
125
143
  }]
126
144
  }
127
145
 
@@ -129,14 +147,15 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
129
147
  * Transforms a single DD-formatted span to an OTLP Span object.
130
148
  *
131
149
  * @param {DDFormattedSpan} span - DD-formatted span to transform
150
+ * @param {string | undefined} traceIdHigh - 16-char hex of the upper 64 bits of the trace ID
132
151
  * @returns {object} OTLP Span object
133
152
  */
134
- #transformSpan (span) {
153
+ #transformSpan (span, traceIdHigh) {
135
154
  const parentId = span.parent_id
136
155
  const links = this.#extractLinks(span.meta?.['_dd.span_links'])
137
156
 
138
157
  return {
139
- traceId: this.#idToBytes(span.trace_id, 16),
158
+ traceId: span.trace_id.toTraceIdHex(traceIdHigh).padStart(32, '0'),
140
159
  spanId: this.#idToBytes(span.span_id, 8),
141
160
  parentSpanId: (parentId && !parentId.equals(ZERO_ID)) ? this.#idToBytes(parentId, 8) : undefined,
142
161
  name: span.resource,
@@ -11,20 +11,31 @@ class LogPropagator {
11
11
  inject (spanContext, carrier) {
12
12
  if (!carrier) return
13
13
 
14
- carrier.dd = {}
14
+ const dd = {}
15
+ let hasField = false
15
16
 
16
17
  if (spanContext) {
17
- carrier.dd.trace_id = this._config.traceId128BitGenerationEnabled &&
18
+ dd.trace_id = this._config.traceId128BitGenerationEnabled &&
18
19
  this._config.traceId128BitLoggingEnabled && spanContext._trace.tags['_dd.p.tid']
19
20
  ? spanContext.toTraceId(true)
20
21
  : spanContext.toTraceId()
21
-
22
- carrier.dd.span_id = spanContext.toSpanId()
22
+ dd.span_id = spanContext.toSpanId()
23
+ hasField = true
24
+ }
25
+ if (this._config.service) {
26
+ dd.service = this._config.service
27
+ hasField = true
28
+ }
29
+ if (this._config.version) {
30
+ dd.version = this._config.version
31
+ hasField = true
32
+ }
33
+ if (this._config.env) {
34
+ dd.env = this._config.env
35
+ hasField = true
23
36
  }
24
37
 
25
- if (this._config.service) carrier.dd.service = this._config.service
26
- if (this._config.version) carrier.dd.version = this._config.version
27
- if (this._config.env) carrier.dd.env = this._config.env
38
+ if (hasField) carrier.dd = dd
28
39
  }
29
40
 
30
41
  extract (carrier) {