dd-trace 5.96.0 → 5.98.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 (173) hide show
  1. package/index.d.ts +60 -2
  2. package/package.json +9 -7
  3. package/packages/datadog-esbuild/index.js +20 -9
  4. package/packages/datadog-instrumentations/src/child_process.js +7 -17
  5. package/packages/datadog-instrumentations/src/crypto.js +1 -2
  6. package/packages/datadog-instrumentations/src/cucumber.js +69 -4
  7. package/packages/datadog-instrumentations/src/cypress-config.js +318 -0
  8. package/packages/datadog-instrumentations/src/cypress.js +86 -4
  9. package/packages/datadog-instrumentations/src/dns.js +1 -2
  10. package/packages/datadog-instrumentations/src/express.js +4 -4
  11. package/packages/datadog-instrumentations/src/fs.js +27 -29
  12. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
  14. package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
  17. package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
  18. package/packages/datadog-instrumentations/src/http/client.js +2 -3
  19. package/packages/datadog-instrumentations/src/http/server.js +2 -5
  20. package/packages/datadog-instrumentations/src/http2/client.js +1 -3
  21. package/packages/datadog-instrumentations/src/http2/server.js +1 -3
  22. package/packages/datadog-instrumentations/src/jest.js +117 -16
  23. package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +12 -1
  25. package/packages/datadog-instrumentations/src/net.js +2 -8
  26. package/packages/datadog-instrumentations/src/pino.js +1 -1
  27. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  28. package/packages/datadog-instrumentations/src/prisma.js +1 -2
  29. package/packages/datadog-instrumentations/src/redis.js +12 -6
  30. package/packages/datadog-instrumentations/src/selenium.js +4 -1
  31. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  32. package/packages/datadog-instrumentations/src/url.js +1 -3
  33. package/packages/datadog-instrumentations/src/vitest.js +5 -1
  34. package/packages/datadog-instrumentations/src/vm.js +1 -3
  35. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -4
  36. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  37. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  38. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  39. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
  40. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  41. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  42. package/packages/datadog-plugin-cucumber/src/index.js +13 -3
  43. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +166 -6
  44. package/packages/datadog-plugin-cypress/src/index.js +59 -2
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
  47. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
  48. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  49. package/packages/datadog-plugin-http/src/client.js +1 -1
  50. package/packages/datadog-plugin-http/src/server.js +10 -2
  51. package/packages/datadog-plugin-http2/src/client.js +1 -1
  52. package/packages/datadog-plugin-http2/src/server.js +10 -2
  53. package/packages/datadog-plugin-jest/src/index.js +4 -2
  54. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
  55. package/packages/datadog-plugin-mocha/src/index.js +5 -2
  56. package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
  57. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  58. package/packages/datadog-plugin-next/src/index.js +10 -16
  59. package/packages/datadog-plugin-openai/src/services.js +1 -0
  60. package/packages/datadog-plugin-pg/src/index.js +1 -1
  61. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  62. package/packages/datadog-plugin-ws/src/close.js +1 -1
  63. package/packages/datadog-plugin-ws/src/receiver.js +1 -1
  64. package/packages/datadog-webpack/index.js +3 -3
  65. package/packages/dd-trace/index.js +12 -10
  66. package/packages/dd-trace/src/agent/url.js +2 -2
  67. package/packages/dd-trace/src/aiguard/sdk.js +26 -22
  68. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
  69. package/packages/dd-trace/src/appsec/blocking.js +64 -33
  70. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  71. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
  72. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  73. package/packages/dd-trace/src/appsec/remote_config.js +1 -0
  74. package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
  75. package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
  76. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
  77. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
  78. package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
  79. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
  80. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  81. package/packages/dd-trace/src/config/defaults.js +315 -146
  82. package/packages/dd-trace/src/config/generated-config-types.d.ts +9 -1
  83. package/packages/dd-trace/src/config/helper.js +59 -10
  84. package/packages/dd-trace/src/config/index.js +587 -1496
  85. package/packages/dd-trace/src/config/parsers.js +256 -0
  86. package/packages/dd-trace/src/config/remote_config.js +59 -2
  87. package/packages/dd-trace/src/config/supported-configurations.json +406 -432
  88. package/packages/dd-trace/src/constants.js +1 -0
  89. package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
  90. package/packages/dd-trace/src/crashtracking/index.js +1 -7
  91. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
  92. package/packages/dd-trace/src/debugger/index.js +1 -1
  93. package/packages/dd-trace/src/dogstatsd.js +12 -9
  94. package/packages/dd-trace/src/encode/0.4.js +8 -7
  95. package/packages/dd-trace/src/encode/span-stats.js +4 -1
  96. package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
  97. package/packages/dd-trace/src/exporters/common/request.js +9 -0
  98. package/packages/dd-trace/src/exporters/common/writer.js +12 -2
  99. package/packages/dd-trace/src/heap_snapshots.js +3 -0
  100. package/packages/dd-trace/src/index.js +5 -2
  101. package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
  102. package/packages/dd-trace/src/llmobs/index.js +4 -1
  103. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
  104. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
  105. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
  106. package/packages/dd-trace/src/llmobs/sdk.js +12 -8
  107. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  108. package/packages/dd-trace/src/llmobs/tagger.js +9 -6
  109. package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
  110. package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
  111. package/packages/dd-trace/src/log/index.js +20 -59
  112. package/packages/dd-trace/src/log/writer.js +7 -19
  113. package/packages/dd-trace/src/noop/proxy.js +8 -0
  114. package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
  115. package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
  116. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  117. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  118. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
  119. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
  120. package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
  121. package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
  122. package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
  123. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
  124. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
  125. package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
  126. package/packages/dd-trace/src/opentracing/tracer.js +9 -4
  127. package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
  128. package/packages/dd-trace/src/plugin_manager.js +8 -6
  129. package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
  130. package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
  131. package/packages/dd-trace/src/plugins/plugin.js +11 -13
  132. package/packages/dd-trace/src/plugins/storage.js +2 -2
  133. package/packages/dd-trace/src/plugins/tracing.js +22 -5
  134. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  135. package/packages/dd-trace/src/plugins/util/web.js +6 -88
  136. package/packages/dd-trace/src/process-tags/index.js +3 -0
  137. package/packages/dd-trace/src/profiler.js +27 -2
  138. package/packages/dd-trace/src/profiling/config.js +73 -241
  139. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
  140. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
  141. package/packages/dd-trace/src/profiling/profiler.js +78 -109
  142. package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
  143. package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
  144. package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
  145. package/packages/dd-trace/src/propagation-hash/index.js +2 -1
  146. package/packages/dd-trace/src/proxy.js +40 -6
  147. package/packages/dd-trace/src/remote_config/index.js +3 -0
  148. package/packages/dd-trace/src/require-package-json.js +8 -4
  149. package/packages/dd-trace/src/ritm.js +58 -26
  150. package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
  151. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +18 -11
  152. package/packages/dd-trace/src/sampler.js +1 -1
  153. package/packages/dd-trace/src/service-naming/index.js +1 -1
  154. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
  155. package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
  156. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
  157. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
  158. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
  159. package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
  160. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
  161. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
  162. package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
  163. package/packages/dd-trace/src/span_stats.js +5 -1
  164. package/packages/dd-trace/src/standalone/index.js +3 -0
  165. package/packages/dd-trace/src/telemetry/index.js +2 -3
  166. package/packages/dd-trace/src/telemetry/send-data.js +5 -19
  167. package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
  168. package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
  169. package/packages/dd-trace/src/tracer.js +2 -2
  170. package/packages/dd-trace/src/util.js +0 -9
  171. package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
  172. package/vendor/dist/protobufjs/index.js +1 -1
  173. package/packages/dd-trace/src/log/utils.js +0 -16
@@ -0,0 +1,332 @@
1
+ 'use strict'
2
+
3
+ const OtlpTransformerBase = require('../otlp/otlp_transformer_base')
4
+ const { getProtobufTypes } = require('../otlp/protobuf_loader')
5
+ const { VERSION } = require('../../../../../version')
6
+
7
+ const { protoSpanKind } = getProtobufTypes()
8
+ const SPAN_KIND_UNSPECIFIED = protoSpanKind.values.SPAN_KIND_UNSPECIFIED
9
+ const SPAN_KIND_INTERNAL = protoSpanKind.values.SPAN_KIND_INTERNAL
10
+ const SPAN_KIND_SERVER = protoSpanKind.values.SPAN_KIND_SERVER
11
+ const SPAN_KIND_CLIENT = protoSpanKind.values.SPAN_KIND_CLIENT
12
+ const SPAN_KIND_PRODUCER = protoSpanKind.values.SPAN_KIND_PRODUCER
13
+ const SPAN_KIND_CONSUMER = protoSpanKind.values.SPAN_KIND_CONSUMER
14
+
15
+ /**
16
+ * @typedef {object} DDFormattedSpan
17
+ * @property {import('../../id')} trace_id - DD Identifier for trace ID
18
+ * @property {import('../../id')} span_id - DD Identifier for span ID
19
+ * @property {import('../../id')} parent_id - DD Identifier for parent span ID
20
+ * @property {string} name - Span operation name
21
+ * @property {string} resource - Resource name
22
+ * @property {string} [service] - Service name
23
+ * @property {string} [type] - Span type
24
+ * @property {number} error - Error flag (0 or 1)
25
+ * @property {{[key: string]: string}} meta - String key-value tags
26
+ * @property {{[key: string]: number}} metrics - Numeric key-value tags
27
+ * @property {number} start - Start time in nanoseconds since epoch
28
+ * @property {number} duration - Duration in nanoseconds
29
+ * @property {object[]} [span_events] - Span events
30
+ */
31
+
32
+ // Map DD span.kind string values to OTLP SpanKind numeric values
33
+ const SPAN_KIND_MAP = {
34
+ internal: SPAN_KIND_INTERNAL,
35
+ server: SPAN_KIND_SERVER,
36
+ client: SPAN_KIND_CLIENT,
37
+ producer: SPAN_KIND_PRODUCER,
38
+ consumer: SPAN_KIND_CONSUMER,
39
+ }
40
+
41
+ // OTLP StatusCode values (from trace.proto Status.StatusCode enum)
42
+ const STATUS_CODE_UNSET = 0
43
+ const STATUS_CODE_ERROR = 2
44
+
45
+ // DD meta keys that are mapped to dedicated OTLP span fields and should not appear as attributes
46
+ const EXCLUDED_META_KEYS = new Set([
47
+ '_dd.span_links',
48
+ 'span.kind',
49
+ ])
50
+
51
+ /**
52
+ * OtlpTraceTransformer transforms DD-formatted spans to OTLP trace JSON format.
53
+ *
54
+ * This implementation follows the OTLP Trace v1.7.0 Data Model specification:
55
+ * https://opentelemetry.io/docs/specs/otlp/#trace-data-model
56
+ *
57
+ * It receives DD-formatted spans (from span_format.js) and produces
58
+ * an ExportTraceServiceRequest serialized as JSON (http/json protocol only).
59
+ *
60
+ * @class OtlpTraceTransformer
61
+ * @augments OtlpTransformerBase
62
+ */
63
+ class OtlpTraceTransformer extends OtlpTransformerBase {
64
+ /**
65
+ * Creates a new OtlpTraceTransformer instance.
66
+ *
67
+ * @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
68
+ */
69
+ constructor (resourceAttributes) {
70
+ super(resourceAttributes, 'http/json', 'traces')
71
+ }
72
+
73
+ /**
74
+ * Transforms DD-formatted spans to OTLP JSON format.
75
+ *
76
+ * @param {DDFormattedSpan[]} spans - Array of DD-formatted spans to transform
77
+ * @returns {Buffer} JSON-encoded trace data
78
+ */
79
+ transformSpans (spans) {
80
+ const traceData = {
81
+ resourceSpans: [{
82
+ resource: this.transformResource(),
83
+ scopeSpans: this.#transformScopeSpans(spans),
84
+ }],
85
+ }
86
+ return this.serializeToJson(traceData)
87
+ }
88
+
89
+ /**
90
+ * Creates scope spans. DD spans do not carry instrumentation scope info,
91
+ * so all spans are placed under a single default scope.
92
+ *
93
+ * @param {DDFormattedSpan[]} spans - Array of DD-formatted spans
94
+ * @returns {object[]} Array of scope span objects
95
+ */
96
+ #transformScopeSpans (spans) {
97
+ return [{
98
+ scope: {
99
+ name: 'dd-trace-js',
100
+ version: VERSION,
101
+ attributes: [],
102
+ droppedAttributesCount: 0,
103
+ },
104
+ schemaUrl: '',
105
+ spans: spans.map(span => this.#transformSpan(span)),
106
+ }]
107
+ }
108
+
109
+ /**
110
+ * Transforms a single DD-formatted span to an OTLP Span object.
111
+ *
112
+ * @param {DDFormattedSpan} span - DD-formatted span to transform
113
+ * @returns {object} OTLP Span object
114
+ */
115
+ #transformSpan (span) {
116
+ const parentId = span.parent_id
117
+ const links = this.#extractLinks(span.meta?.['_dd.span_links'])
118
+
119
+ return {
120
+ traceId: this.#idToBytes(span.trace_id, 16),
121
+ spanId: this.#idToBytes(span.span_id, 8),
122
+ parentSpanId: (parentId && !this.#isZeroId(parentId)) ? this.#idToBytes(parentId, 8) : undefined,
123
+ name: span.resource,
124
+ kind: this.#mapSpanKind(span.meta?.['span.kind']),
125
+ startTimeUnixNano: span.start,
126
+ endTimeUnixNano: span.start + span.duration,
127
+ attributes: this.#buildAttributes(span),
128
+ droppedAttributesCount: 0,
129
+ events: span.span_events?.length ? span.span_events.map(event => this.#transformEvent(event)) : undefined,
130
+ droppedEventsCount: 0,
131
+ links: links.length ? links : undefined,
132
+ droppedLinksCount: 0,
133
+ status: this.#mapStatus(span),
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Builds OTLP attributes from DD span fields.
139
+ * Merges top-level DD fields (service, resource, type), meta (string tags),
140
+ * and metrics (numeric tags) into a single OTLP KeyValue array.
141
+ *
142
+ * @param {DDFormattedSpan} span - DD-formatted span
143
+ * @returns {object[]} Array of OTLP KeyValue objects
144
+ */
145
+ #buildAttributes (span) {
146
+ const attributes = []
147
+
148
+ // Add top-level DD span fields as OTLP attributes
149
+ if (span.service) {
150
+ attributes.push({ key: 'service.name', value: { stringValue: span.service } })
151
+ }
152
+ if (span.name) {
153
+ attributes.push({ key: 'operation.name', value: { stringValue: span.name } })
154
+ }
155
+ if (span.resource) {
156
+ attributes.push({ key: 'resource.name', value: { stringValue: span.resource } })
157
+ }
158
+ if (span.type) {
159
+ attributes.push({ key: 'span.type', value: { stringValue: span.type } })
160
+ }
161
+
162
+ // Add meta string tags, skipping keys that map to dedicated OTLP fields
163
+ if (span.meta) {
164
+ for (const [key, value] of Object.entries(span.meta)) {
165
+ if (EXCLUDED_META_KEYS.has(key)) continue
166
+ attributes.push({ key, value: { stringValue: value } })
167
+ }
168
+ }
169
+
170
+ // Add metrics as numeric attributes
171
+ if (span.metrics) {
172
+ for (const [key, value] of Object.entries(span.metrics)) {
173
+ if (Number.isInteger(value)) {
174
+ attributes.push({ key, value: { intValue: value } })
175
+ } else {
176
+ attributes.push({ key, value: { doubleValue: value } })
177
+ }
178
+ }
179
+ }
180
+
181
+ // Add meta_struct as bytesValue attributes (JSON-serialized, base64-encoded per proto JSON mapping)
182
+ if (span.meta_struct) {
183
+ for (const [key, value] of Object.entries(span.meta_struct)) {
184
+ const bytes = Buffer.from(JSON.stringify(value))
185
+ attributes.push({ key, value: { bytesValue: bytes.toString('base64') } })
186
+ }
187
+ }
188
+
189
+ return attributes
190
+ }
191
+
192
+ /**
193
+ * Maps a DD span.kind string to an OTLP SpanKind enum value.
194
+ *
195
+ * @param {string | undefined} kind - DD span kind string
196
+ * @returns {number} OTLP SpanKind enum value
197
+ */
198
+ #mapSpanKind (kind) {
199
+ if (!kind) return SPAN_KIND_UNSPECIFIED
200
+ return SPAN_KIND_MAP[kind] ?? SPAN_KIND_UNSPECIFIED
201
+ }
202
+
203
+ /**
204
+ * Maps DD span error state to an OTLP Status object.
205
+ *
206
+ * @param {DDFormattedSpan} span - DD-formatted span
207
+ * @returns {object} OTLP Status object with code and message
208
+ */
209
+ #mapStatus (span) {
210
+ if (span.error === 1) {
211
+ return {
212
+ code: STATUS_CODE_ERROR,
213
+ message: span.meta?.['error.message'] || '',
214
+ }
215
+ }
216
+ return { code: STATUS_CODE_UNSET, message: '' }
217
+ }
218
+
219
+ /**
220
+ * Transforms a DD span event to an OTLP Event object.
221
+ *
222
+ * @param {object} event - DD span event with name, time_unix_nano, and attributes
223
+ * @returns {object} OTLP Event object
224
+ */
225
+ #transformEvent (event) {
226
+ return {
227
+ timeUnixNano: event.time_unix_nano,
228
+ name: event.name || '',
229
+ attributes: event.attributes && Object.keys(event.attributes).length > 0
230
+ ? this.transformAttributes(event.attributes)
231
+ : [],
232
+ droppedAttributesCount: 0,
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Extracts and transforms span links from the DD _dd.span_links meta JSON string.
238
+ *
239
+ * @param {string | undefined} spanLinksJson - JSON-encoded array of DD span links
240
+ * @returns {object[]} Array of OTLP Link objects
241
+ */
242
+ #extractLinks (spanLinksJson) {
243
+ if (!spanLinksJson) return []
244
+
245
+ let parsedLinks
246
+ try {
247
+ parsedLinks = JSON.parse(spanLinksJson)
248
+ } catch {
249
+ return []
250
+ }
251
+
252
+ if (!Array.isArray(parsedLinks)) return []
253
+
254
+ return parsedLinks.map(link => this.#transformLink(link))
255
+ }
256
+
257
+ /**
258
+ * Transforms a single DD span link to an OTLP Link object.
259
+ *
260
+ * @param {object} link - DD span link with trace_id, span_id, attributes, flags, tracestate
261
+ * @returns {object} OTLP Link object
262
+ */
263
+ #transformLink (link) {
264
+ return {
265
+ traceId: this.#hexToBytes(link.trace_id, 16),
266
+ spanId: this.#hexToBytes(link.span_id, 8),
267
+ traceState: link.tracestate || '',
268
+ attributes: link.attributes && Object.keys(link.attributes).length > 0
269
+ ? this.transformAttributes(link.attributes)
270
+ : [],
271
+ droppedAttributesCount: 0,
272
+ flags: link.flags,
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Converts a DD Identifier object to a hex-encoded string of the specified byte length.
278
+ * Pads with leading zeros if the identifier buffer is shorter than the target.
279
+ * Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
280
+ *
281
+ * @param {object} identifier - DD Identifier object with toBuffer() method
282
+ * @param {number} targetLength - Target byte length (16 for trace ID, 8 for span ID)
283
+ * @returns {string} Hex-encoded string of the specified length
284
+ */
285
+ #idToBytes (identifier, targetLength) {
286
+ const buffer = identifier.toBuffer()
287
+ if (buffer.length === targetLength) {
288
+ return Buffer.from(buffer).toString('hex')
289
+ }
290
+ if (buffer.length > targetLength) {
291
+ return Buffer.from(buffer.slice(buffer.length - targetLength)).toString('hex')
292
+ }
293
+ // Pad with leading zeros to reach target length
294
+ const result = Buffer.alloc(targetLength)
295
+ const offset = targetLength - buffer.length
296
+ for (let i = 0; i < buffer.length; i++) {
297
+ result[offset + i] = buffer[i]
298
+ }
299
+ return result.toString('hex')
300
+ }
301
+
302
+ /**
303
+ * Checks if a DD Identifier represents a zero ID (all bytes are 0).
304
+ *
305
+ * @param {object} identifier - DD Identifier object with toBuffer() method
306
+ * @returns {boolean} True if the identifier is all zeros
307
+ */
308
+ #isZeroId (identifier) {
309
+ const buffer = identifier.toBuffer()
310
+ for (let i = 0; i < buffer.length; i++) {
311
+ if (buffer[i] !== 0) return false
312
+ }
313
+ return true
314
+ }
315
+
316
+ /**
317
+ * Normalizes a hex string to the specified byte length.
318
+ * Pads with leading zeros if the hex string is shorter than expected.
319
+ * Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
320
+ *
321
+ * @param {string | undefined} hexString - Hex string to normalize
322
+ * @param {number} targetLength - Target byte length
323
+ * @returns {string} Hex-encoded string of the specified length
324
+ */
325
+ #hexToBytes (hexString, targetLength) {
326
+ if (!hexString) return '0'.repeat(targetLength * 2)
327
+ const cleanHex = hexString.startsWith('0x') ? hexString.slice(2) : hexString
328
+ return cleanHex.padStart(targetLength * 2, '0')
329
+ }
330
+ }
331
+
332
+ module.exports = OtlpTraceTransformer
@@ -6,6 +6,7 @@ const id = require('../../id')
6
6
  const DatadogSpanContext = require('../span_context')
7
7
  const log = require('../../log')
8
8
  const tags = require('../../../../../ext/tags')
9
+ const { getConfiguredEnvName } = require('../../config/helper')
9
10
  const { setBaggageItem, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
10
11
  const telemetryMetrics = require('../../telemetry/metrics')
11
12
 
@@ -65,8 +66,15 @@ const zeroTraceId = '0000000000000000'
65
66
  const hex16 = /^[0-9A-Fa-f]{16}$/
66
67
 
67
68
  class TextMapPropagator {
69
+ #extractB3Context
70
+
68
71
  constructor (config) {
69
72
  this._config = config
73
+
74
+ // TODO: should match "b3 single header" in next major
75
+ const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
76
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
77
+ this.#extractB3Context = envName === 'OTEL_PROPAGATORS' ? this._extractB3SingleContext : this._extractB3MultiContext
70
78
  }
71
79
 
72
80
  inject (spanContext, carrier) {
@@ -363,10 +371,7 @@ class TextMapPropagator {
363
371
  extractedContext = this._extractB3SingleContext(carrier)
364
372
  break
365
373
  case 'b3':
366
- extractedContext = this._config.tracePropagationStyle.otelPropagators
367
- // TODO: should match "b3 single header" in next major
368
- ? this._extractB3SingleContext(carrier)
369
- : this._extractB3MultiContext(carrier)
374
+ extractedContext = this.#extractB3Context(carrier)
370
375
  break
371
376
  case 'b3multi':
372
377
  extractedContext = this._extractB3MultiContext(carrier)
@@ -20,9 +20,7 @@ const REFERENCE_CHILD_OF = 'child_of'
20
20
  const REFERENCE_FOLLOWS_FROM = 'follows_from'
21
21
 
22
22
  class DatadogTracer {
23
- constructor (config, prioritySampler) {
24
- const Exporter = getExporter(config.experimental.exporter)
25
-
23
+ constructor (config, prioritySampler, exporter) {
26
24
  this._config = config
27
25
  this._service = config.service
28
26
  this._version = config.version
@@ -30,7 +28,14 @@ class DatadogTracer {
30
28
  this._logInjection = config.logInjection
31
29
  this._debug = config.debug
32
30
  this._prioritySampler = prioritySampler ?? new PrioritySampler(config.env, config.sampler)
33
- this._exporter = new Exporter(config, this._prioritySampler)
31
+
32
+ if (exporter) {
33
+ this._exporter = exporter
34
+ } else {
35
+ const Exporter = getExporter(config.experimental.exporter)
36
+ this._exporter = new Exporter(config, this._prioritySampler)
37
+ }
38
+
34
39
  this._processor = new SpanProcessor(this._exporter, this._prioritySampler, config)
35
40
  this._url = this._exporter._url
36
41
  this._enableGetRumData = config.experimental.enableGetRumData
@@ -3,16 +3,17 @@
3
3
  const aws = require('./aws.json')
4
4
  const sdks = { aws }
5
5
 
6
+ /** @typedef {Record<string, { request: string[], response: string[], expand: string[] }>} SDKRules */
6
7
  /**
7
8
  * Builds rules per service for a given SDK, appending user-provided rules.
8
9
  *
9
- * @param {Record<string, { request: string[], response: string[], expand: string[] }>} sdk
10
+ * @param {SDKRules} sdk
10
11
  * @param {string[]} requestInput
11
12
  * @param {string[]} responseInput
12
- * @returns {Record<string, { request: string[], response: string[], expand: string[] }>}
13
+ * @returns {SDKRules}
13
14
  */
14
15
  function getSDKRules (sdk, requestInput, responseInput) {
15
- const sdkServiceRules = {}
16
+ const sdkServiceRules = /** @type {SDKRules} */ ({})
16
17
  for (const [service, serviceRules] of Object.entries(sdk)) {
17
18
  sdkServiceRules[service] = {
18
19
  // Make a copy. Otherwise calling the function multiple times would append
@@ -31,10 +32,10 @@ function getSDKRules (sdk, requestInput, responseInput) {
31
32
  *
32
33
  * @param {string[]} [requestInput=[]]
33
34
  * @param {string[]} [responseInput=[]]
34
- * @returns {Record<string, Record<string, { request: string[], response: string[], expand: string[] }>>}
35
+ * @returns {Record<string, SDKRules>}
35
36
  */
36
37
  function appendRules (requestInput = [], responseInput = []) {
37
- const sdkRules = {}
38
+ const sdkRules = /** @type {Record<string, SDKRules>} */ ({})
38
39
  for (const [name, sdk] of Object.entries(sdks)) {
39
40
  sdkRules[name] = getSDKRules(sdk, requestInput, responseInput)
40
41
  }
@@ -67,7 +67,6 @@ function getEnabled (Plugin) {
67
67
  module.exports = class PluginManager {
68
68
  constructor (tracer) {
69
69
  this._tracer = tracer
70
- this._tracerConfig = null
71
70
  this._pluginsByName = {}
72
71
  this._configsByName = {}
73
72
 
@@ -104,7 +103,7 @@ module.exports = class PluginManager {
104
103
 
105
104
  // extracts predetermined configuration from tracer and combines it with plugin-specific config
106
105
  this._pluginsByName[name].configure({
107
- ...this._getSharedConfig(name),
106
+ ...this.#getSharedConfig(name),
108
107
  ...pluginConfig,
109
108
  })
110
109
  }
@@ -121,8 +120,11 @@ module.exports = class PluginManager {
121
120
  this.loadPlugin(name)
122
121
  }
123
122
 
124
- // like instrumenter.enable()
125
- configure (config = {}) {
123
+ /**
124
+ * Like instrumenter.enable()
125
+ * @param {import('./config/config-base')} config - Tracer configuration
126
+ */
127
+ configure (config) {
126
128
  this._tracerConfig = config
127
129
  this._tracer._nomenclature.configure(config)
128
130
 
@@ -148,7 +150,7 @@ module.exports = class PluginManager {
148
150
  }
149
151
 
150
152
  // TODO: figure out a better way to handle this
151
- _getSharedConfig (name) {
153
+ #getSharedConfig (name) {
152
154
  const {
153
155
  logInjection,
154
156
  serviceMapping,
@@ -172,7 +174,7 @@ module.exports = class PluginManager {
172
174
  traceWebsocketMessagesSeparateTraces,
173
175
  experimental,
174
176
  resourceRenamingEnabled,
175
- } = this._tracerConfig
177
+ } = /** @type {import('./config/config-base')} */ (this._tracerConfig)
176
178
 
177
179
  const sharedConfig = {
178
180
  codeOriginForSpans,
@@ -469,6 +469,10 @@ module.exports = class CiPlugin extends Plugin {
469
469
  return getSessionRequestErrorTags(this.testSessionSpan)
470
470
  }
471
471
 
472
+ /**
473
+ * @param {import('../config/config-base')} config - Tracer configuration
474
+ * @param {boolean} shouldGetEnvironmentData - Whether to get environment data
475
+ */
472
476
  configure (config, shouldGetEnvironmentData = true) {
473
477
  super.configure(config)
474
478
 
@@ -13,6 +13,9 @@ function messageProxy (message, holder) {
13
13
 
14
14
  return target[key]
15
15
  },
16
+ set (target, key, value) {
17
+ return Reflect.set(target, key, value)
18
+ },
16
19
  ownKeys (target) {
17
20
  const ownKeys = Reflect.ownKeys(target)
18
21
  if (!Object.hasOwn(target, 'dd') && Reflect.isExtensible(target)) {
@@ -72,7 +72,7 @@ module.exports = class Plugin {
72
72
  * Create a new plugin instance.
73
73
  *
74
74
  * @param {object} tracer Tracer instance or wrapper containing it under `_tracer`.
75
- * @param {object} tracerConfig Global tracer configuration object.
75
+ * @param {import('../config/config-base')} tracerConfig Global tracer configuration object.
76
76
  */
77
77
  constructor (tracer, tracerConfig) {
78
78
  this._subscriptions = []
@@ -80,6 +80,8 @@ module.exports = class Plugin {
80
80
  this._enabled = false
81
81
  this._tracer = tracer
82
82
  this.config = {} // plugin-specific configuration, unset until .configure() is called
83
+
84
+ /** @type {import('../config/config-base')} */
83
85
  this._tracerConfig = tracerConfig // global tracer configuration
84
86
  }
85
87
 
@@ -104,12 +106,6 @@ module.exports = class Plugin {
104
106
  storage('legacy').enterWith({ ...store, span })
105
107
  }
106
108
 
107
- // TODO: Implement filters on resource name for all plugins.
108
- /** Prevents creation of spans here and for all async descendants. */
109
- skip () {
110
- storage('legacy').enterWith({ noop: true })
111
- }
112
-
113
109
  /**
114
110
  * Subscribe to a diagnostic channel with automatic error handling and enable/disable lifecycle.
115
111
  *
@@ -155,21 +151,23 @@ module.exports = class Plugin {
155
151
 
156
152
  if (!store || !store.span) return
157
153
 
158
- if (!store.span._spanContext._tags.error) {
159
- store.span.setTag('error', error || 1)
154
+ const span = /** @type {import('../opentracing/span')} */ (store.span)
155
+ if (!span._spanContext._tags.error) {
156
+ span.setTag('error', error || 1)
160
157
  }
161
158
  }
162
159
 
163
160
  /**
164
161
  * Enable or disable the plugin and (re)apply its configuration.
165
162
  *
166
- * @param {boolean|object} config Either a boolean to enable/disable or a configuration object
167
- * containing at least `{ enabled: boolean }`.
168
- * @returns {void}
163
+ * TODO: Remove the overloading with `enabled` and use the config object directly.
164
+ *
165
+ * @param {boolean | import('../config/config-base') & {enabled: boolean}} config Either a boolean to
166
+ * enable/disable or a configuration object containing at least `{ enabled: boolean }`.
169
167
  */
170
168
  configure (config) {
171
169
  if (typeof config === 'boolean') {
172
- config = { enabled: config }
170
+ config = /** @type {import('../config/config-base') & {enabled: boolean}} */ ({ enabled: config })
173
171
  }
174
172
  this.config = config
175
173
  if (config.enabled) {
@@ -12,8 +12,8 @@ class StoragePlugin extends ClientPlugin {
12
12
  }
13
13
 
14
14
  startSpan (name, options, ctx) {
15
- if (!options.service && this.system) {
16
- options.service = `${this.tracer._service}-${this.system}`
15
+ if (!options.service?.name && this.system) {
16
+ options.service = { name: `${this.tracer._service}-${this.system}`, source: this.system }
17
17
  }
18
18
 
19
19
  return super.startSpan(name, options, ctx)
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { storage } = require('../../../datadog-core')
4
4
  const analyticsSampler = require('../analytics_sampler')
5
- const { COMPONENT } = require('../constants')
5
+ const { COMPONENT, SVC_SRC_KEY } = require('../constants')
6
6
  const Plugin = require('./plugin')
7
7
 
8
8
  class TracingPlugin extends Plugin {
@@ -26,7 +26,7 @@ class TracingPlugin extends Plugin {
26
26
  * @param {string} [opts.type]
27
27
  * @param {string} [opts.id]
28
28
  * @param {string} [opts.kind]
29
- * @returns {string}
29
+ * @returns {{ name: string, source: string | undefined }}
30
30
  */
31
31
  serviceName (opts = {}) {
32
32
  const {
@@ -157,7 +157,8 @@ class TracingPlugin extends Plugin {
157
157
  * @param {string} [options.kind] - The kind of the span.
158
158
  * @param {object} [options.meta] - The meta data for the span.
159
159
  * @param {object} [options.metrics] - The metrics for the span.
160
- * @param {string} [options.service] - The service name.
160
+ * @param {string | { name: string, source?: string }} [options.service] - The service name, or an object with
161
+ * name and source.
161
162
  * @param {number} [options.startTime] - The start time of the span.
162
163
  * @param {string} [options.resource] - The resource name.
163
164
  * @param {string} [options.type] - The type of the span.
@@ -180,24 +181,40 @@ class TracingPlugin extends Plugin {
180
181
  resource,
181
182
  type,
182
183
  } = options
183
-
184
+ let serviceSource
184
185
  const tracer = options.tracer || this.tracer
185
186
  const config = options.config || this.config
186
187
 
188
+ if (service && typeof service === 'object') {
189
+ serviceSource = service.source
190
+ service = service.name
191
+ } else if (service !== undefined) {
192
+ // service is a plain value returned by service naming/config logic
193
+ serviceSource = service ? 'opt.plugin' : undefined
194
+ }
195
+
187
196
  const store = storage('legacy').getStore()
188
197
  if (store && childOf === undefined) {
189
198
  childOf = /** @type {import('../opentracing/span') | undefined} */ (store.span)
190
199
  }
191
200
 
201
+ // clear service source if service is the same as tracer._service
202
+ const serviceName = service || meta?.service
203
+
204
+ if (!serviceName || serviceName === tracer._service) {
205
+ serviceSource = undefined
206
+ }
207
+
192
208
  const span = tracer.startSpan(name, {
193
209
  startTime,
194
210
  childOf,
195
211
  tags: {
196
212
  [COMPONENT]: component,
197
- 'service.name': service || meta?.service || tracer._service,
213
+ 'service.name': serviceName || tracer._service,
198
214
  'resource.name': resource,
199
215
  'span.kind': kind,
200
216
  'span.type': type,
217
+ ...(serviceSource === undefined ? undefined : { [SVC_SRC_KEY]: serviceSource }),
201
218
  ...meta,
202
219
  ...metrics,
203
220
  },
@@ -137,6 +137,7 @@ const JEST_WORKER_TRACE_PAYLOAD_CODE = 60
137
137
  const JEST_WORKER_COVERAGE_PAYLOAD_CODE = 61
138
138
  const JEST_WORKER_LOGS_PAYLOAD_CODE = 62
139
139
  const JEST_WORKER_TELEMETRY_PAYLOAD_CODE = 63
140
+ const JEST_WORKER_QUARANTINE_PAYLOAD_CODE = 64
140
141
 
141
142
  // cucumber worker variables
142
143
  const CUCUMBER_WORKER_TRACE_PAYLOAD_CODE = 70
@@ -262,6 +263,7 @@ module.exports = {
262
263
  JEST_WORKER_COVERAGE_PAYLOAD_CODE,
263
264
  JEST_WORKER_LOGS_PAYLOAD_CODE,
264
265
  JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
266
+ JEST_WORKER_QUARANTINE_PAYLOAD_CODE,
265
267
  CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
266
268
  MOCHA_WORKER_TRACE_PAYLOAD_CODE,
267
269
  PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,