dd-trace 5.109.0 → 5.111.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 (208) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +25 -5
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +79 -24
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +154 -47
  8. package/package.json +20 -16
  9. package/packages/datadog-esbuild/index.js +26 -0
  10. package/packages/datadog-esbuild/src/utils.js +46 -3
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  13. package/packages/datadog-instrumentations/src/fs.js +8 -6
  14. package/packages/datadog-instrumentations/src/graphql.js +26 -484
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  17. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  19. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  23. package/packages/datadog-instrumentations/src/koa.js +3 -2
  24. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  25. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  26. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  27. package/packages/datadog-instrumentations/src/next.js +10 -10
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  29. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  30. package/packages/datadog-instrumentations/src/restify.js +4 -3
  31. package/packages/datadog-instrumentations/src/router.js +18 -5
  32. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  33. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  34. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  35. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  36. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  37. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  38. package/packages/datadog-instrumentations/src/vitest.js +11 -1598
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  45. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  46. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
  48. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  49. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  50. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  52. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  53. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  54. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  55. package/packages/datadog-plugin-http/src/client.js +8 -3
  56. package/packages/datadog-plugin-http/src/index.js +6 -8
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-jest/src/index.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  60. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  61. package/packages/datadog-plugin-openai/src/services.js +2 -2
  62. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  63. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  64. package/packages/datadog-plugin-undici/src/index.js +5 -2
  65. package/packages/datadog-plugin-vitest/src/index.js +76 -64
  66. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  67. package/packages/datadog-webpack/index.js +17 -1
  68. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  69. package/packages/dd-trace/src/aiguard/sdk.js +15 -3
  70. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  71. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  72. package/packages/dd-trace/src/appsec/channels.js +3 -1
  73. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  74. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  75. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  76. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  77. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  78. package/packages/dd-trace/src/appsec/index.js +20 -7
  79. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  80. package/packages/dd-trace/src/appsec/reporter.js +50 -10
  81. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  82. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
  83. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  84. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  85. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  86. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  90. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  91. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  92. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  94. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  95. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  96. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  97. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  98. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  99. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  100. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  101. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  102. package/packages/dd-trace/src/config/defaults.js +25 -5
  103. package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
  104. package/packages/dd-trace/src/config/helper.js +134 -33
  105. package/packages/dd-trace/src/config/index.js +39 -23
  106. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  107. package/packages/dd-trace/src/config/parsers.js +19 -1
  108. package/packages/dd-trace/src/config/remote_config.js +1 -1
  109. package/packages/dd-trace/src/config/supported-configurations.json +109 -57
  110. package/packages/dd-trace/src/constants.js +7 -0
  111. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  112. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  114. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  116. package/packages/dd-trace/src/debugger/index.js +5 -0
  117. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  118. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  119. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  120. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  122. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  123. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  124. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  125. package/packages/dd-trace/src/guardrails/index.js +8 -5
  126. package/packages/dd-trace/src/index.js +6 -4
  127. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  128. package/packages/dd-trace/src/llmobs/index.js +17 -6
  129. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  130. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  131. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/sdk.js +8 -6
  134. package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
  135. package/packages/dd-trace/src/llmobs/tagger.js +58 -3
  136. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  137. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  138. package/packages/dd-trace/src/log/index.js +18 -20
  139. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  140. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  141. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  142. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  143. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  145. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  146. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  147. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  148. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  149. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  150. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  151. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  152. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
  153. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  154. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  155. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  156. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  157. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  158. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  159. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  161. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  162. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  163. package/packages/dd-trace/src/plugin_manager.js +14 -8
  164. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
  165. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  166. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +91 -9
  168. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  169. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  170. package/packages/dd-trace/src/priority_sampler.js +1 -13
  171. package/packages/dd-trace/src/profiling/config.js +129 -198
  172. package/packages/dd-trace/src/profiling/constants.js +4 -7
  173. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  174. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  175. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  176. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  177. package/packages/dd-trace/src/profiling/oom.js +71 -0
  178. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  179. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  180. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  182. package/packages/dd-trace/src/proxy.js +8 -8
  183. package/packages/dd-trace/src/ritm.js +4 -2
  184. package/packages/dd-trace/src/serverless.js +1 -2
  185. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  186. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  187. package/packages/dd-trace/src/span_format.js +2 -1
  188. package/packages/dd-trace/src/span_processor.js +11 -6
  189. package/packages/dd-trace/src/span_stats.js +96 -78
  190. package/packages/dd-trace/src/startup-log.js +2 -1
  191. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  192. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  193. package/packages/dd-trace/src/telemetry/index.js +2 -2
  194. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  195. package/packages/dd-trace/src/telemetry/send-data.js +20 -18
  196. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  197. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  198. package/packages/dd-trace/src/tracer.js +15 -1
  199. package/packages/dd-trace/src/util.js +14 -0
  200. package/register.js +58 -1
  201. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  202. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  203. package/vendor/dist/pprof-format/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
  208. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -1,11 +1,14 @@
1
1
  'use strict'
2
2
 
3
- const http = require('http')
4
- const { URL } = require('url')
3
+ const http = require('node:http')
4
+ const https = require('node:https')
5
+ const { URL } = require('node:url')
6
+ const { storage } = require('../../../../datadog-core')
5
7
  const log = require('../../log')
6
8
  const telemetryMetrics = require('../../telemetry/metrics')
7
9
 
8
10
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
11
+ const legacyStorage = storage('legacy')
9
12
 
10
13
  /**
11
14
  * Base class for OTLP HTTP exporters.
@@ -16,6 +19,8 @@ const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
16
19
  * @class OtlpHttpExporterBase
17
20
  */
18
21
  class OtlpHttpExporterBase {
22
+ #transport = https
23
+
19
24
  /**
20
25
  * Creates a new OtlpHttpExporterBase instance.
21
26
  *
@@ -32,20 +37,22 @@ class OtlpHttpExporterBase {
32
37
 
33
38
  const isJson = protocol === 'http/json'
34
39
 
35
- // Initialize fields setUrl doesn't touch; it fills in hostname/port/path below.
40
+ const parsedUrl = new URL(url)
41
+ this.#transport = parsedUrl.protocol === 'http:' ? http : https
36
42
  this.options = {
37
43
  method: 'POST',
38
44
  timeout,
45
+ hostname: parsedUrl.hostname,
46
+ port: parsedUrl.port,
47
+ path: parsedUrl.pathname + parsedUrl.search,
39
48
  headers: {
40
49
  'Content-Type': isJson ? 'application/json' : 'application/x-protobuf',
41
50
  ...headers,
42
51
  },
43
52
  }
44
53
 
45
- this.setUrl(url)
46
-
47
54
  this.telemetryTags = [
48
- 'protocol:http',
55
+ `protocol:${this.#transport === https ? 'https' : 'http'}`,
49
56
  `encoding:${isJson ? 'json' : 'protobuf'}`,
50
57
  ]
51
58
  }
@@ -81,55 +88,54 @@ class OtlpHttpExporterBase {
81
88
  },
82
89
  }
83
90
 
84
- const req = http.request(options, (res) => {
85
- let data = ''
86
-
87
- res.on('data', (chunk) => {
88
- data += chunk
91
+ legacyStorage.run({ noop: true }, () => {
92
+ const req = this.#transport.request(options, (res) => {
93
+ let data = ''
94
+
95
+ res.on('data', (chunk) => {
96
+ data += chunk
97
+ })
98
+
99
+ res.once('end', () => {
100
+ // @ts-expect-error - res.statusCode can be undefined
101
+ if (res.statusCode >= 200 && res.statusCode < 300) {
102
+ resultCallback({ code: 0 })
103
+ } else {
104
+ const error = new Error(`HTTP ${res.statusCode}: ${data}`)
105
+ resultCallback({ code: 1, error })
106
+ }
107
+ })
89
108
  })
90
109
 
91
- res.once('end', () => {
92
- // @ts-expect-error - res.statusCode can be undefined
93
- if (res.statusCode >= 200 && res.statusCode < 300) {
94
- resultCallback({ code: 0 })
95
- } else {
96
- const error = new Error(`HTTP ${res.statusCode}: ${data}`)
97
- resultCallback({ code: 1, error })
98
- }
110
+ req.on('error', (error) => {
111
+ log.error('Error sending OTLP %s:', this.signalType, error)
112
+ resultCallback({ code: 1, error })
99
113
  })
100
- })
101
114
 
102
- req.on('error', (error) => {
103
- log.error('Error sending OTLP %s:', this.signalType, error)
104
- resultCallback({ code: 1, error })
105
- })
115
+ req.once('timeout', () => {
116
+ req.destroy()
117
+ const error = new Error('Request timeout')
118
+ resultCallback({ code: 1, error })
119
+ })
106
120
 
107
- req.once('timeout', () => {
108
- req.destroy()
109
- const error = new Error('Request timeout')
110
- resultCallback({ code: 1, error })
121
+ req.write(payload)
122
+ req.end()
111
123
  })
112
-
113
- req.write(payload)
114
- req.end()
115
124
  }
116
125
 
117
126
  /**
118
- * Updates the target URL used by this exporter. The URL is used as-is per the OTel spec: the
119
- * caller is responsible for including the signal-specific path (`/v1/traces` etc.).
120
- * @param {string} url - New OTLP endpoint URL
127
+ * Re-targets the exporter to a different URL, updating transport, hostname, port, and path.
128
+ * @param {string} url
121
129
  */
122
130
  setUrl (url) {
123
131
  const parsedUrl = new URL(url)
132
+ this.#transport = parsedUrl.protocol === 'http:' ? http : https
124
133
  this.options.hostname = parsedUrl.hostname
125
134
  this.options.port = parsedUrl.port
126
135
  this.options.path = parsedUrl.pathname + parsedUrl.search
136
+ this.telemetryTags[0] = `protocol:${this.#transport === https ? 'https' : 'http'}`
127
137
  }
128
138
 
129
- /**
130
- * Shuts down the exporter.
131
- * Subclasses can override to add cleanup logic.
132
- */
133
139
  shutdown () {}
134
140
  }
135
141
 
@@ -137,14 +137,17 @@ function normalizeLinkContext (context) {
137
137
  * Mirrors `http.response.status_code` onto `http.status_code` (DD's special tag used by APM
138
138
  * trace metrics and client-side stats); both names end up on the span.
139
139
  *
140
+ * When OTel trace semantics are enabled, the behavior is disabled.
141
+ *
140
142
  * @param {import('../opentracing/span')} ddSpan
141
143
  * @param {string} key
142
144
  * @param {unknown} value
145
+ * @param {boolean} [otelTraceSemanticsEnabled]
143
146
  */
144
- function setOtelAttribute (ddSpan, key, value) {
147
+ function setOtelAttribute (ddSpan, key, value, otelTraceSemanticsEnabled) {
145
148
  if (!isWritable(ddSpan)) return
146
149
 
147
- if (key === 'http.response.status_code') {
150
+ if (!otelTraceSemanticsEnabled && key === 'http.response.status_code') {
148
151
  ddSpan.setTag('http.status_code', String(value))
149
152
  }
150
153
 
@@ -155,14 +158,17 @@ function setOtelAttribute (ddSpan, key, value) {
155
158
  * Same `http.status_code` mirror as `setOtelAttribute`; does not mutate the caller's
156
159
  * `attributes` object.
157
160
  *
161
+ * When OTel trace semantics are enabled, the behavior is disabled.
162
+ *
158
163
  * @param {import('../opentracing/span')} ddSpan
159
164
  * @param {Record<string, unknown>} attributes
165
+ * @param {boolean} [otelTraceSemanticsEnabled]
160
166
  */
161
- function setOtelAttributes (ddSpan, attributes) {
167
+ function setOtelAttributes (ddSpan, attributes, otelTraceSemanticsEnabled) {
162
168
  if (!isWritable(ddSpan)) return
163
169
 
164
170
  ddSpan.addTags(attributes)
165
- if ('http.response.status_code' in attributes) {
171
+ if (!otelTraceSemanticsEnabled && 'http.response.status_code' in attributes) {
166
172
  ddSpan.setTag('http.status_code', String(attributes['http.response.status_code']))
167
173
  }
168
174
  }
@@ -223,16 +229,19 @@ function addOtelEvent (ddSpan, name, attributesOrStartTime, startTime) {
223
229
  * @param {import('../opentracing/span')} ddSpan
224
230
  * @param {ExceptionLike} exception
225
231
  * @param {TimeInput} [timeInput]
232
+ * @param {boolean} [otelTraceSemanticsEnabled]
226
233
  */
227
- function recordException (ddSpan, exception, timeInput) {
234
+ function recordException (ddSpan, exception, timeInput, otelTraceSemanticsEnabled) {
228
235
  if (!isWritable(ddSpan)) return
229
236
 
230
- ddSpan.addTags({
231
- [ERROR_TYPE]: exception.name,
232
- [ERROR_MESSAGE]: exception.message,
233
- [ERROR_STACK]: exception.stack,
234
- [IGNORE_OTEL_ERROR]: ddSpan.context().getTag(IGNORE_OTEL_ERROR) ?? true,
235
- })
237
+ if (!otelTraceSemanticsEnabled) {
238
+ ddSpan.addTags({
239
+ [ERROR_TYPE]: exception.name,
240
+ [ERROR_MESSAGE]: exception.message,
241
+ [ERROR_STACK]: exception.stack,
242
+ [IGNORE_OTEL_ERROR]: ddSpan.context().getTag(IGNORE_OTEL_ERROR) ?? true,
243
+ })
244
+ }
236
245
 
237
246
  const attributes = {}
238
247
  if (exception.message) attributes['exception.message'] = exception.message
@@ -251,13 +260,20 @@ function recordException (ddSpan, exception, timeInput) {
251
260
  * @param {import('../opentracing/span')} ddSpan
252
261
  * @param {number} currentCode 0 = UNSET, 1 = OK, 2 = ERROR.
253
262
  * @param {{ code?: number, message?: string }} [status]
263
+ * @param {boolean} [otelTraceSemanticsEnabled]
254
264
  * @returns {number} The new status code to track on the caller.
255
265
  */
256
- function applyOtelStatus (ddSpan, currentCode, status) {
266
+ function applyOtelStatus (ddSpan, currentCode, status, otelTraceSemanticsEnabled) {
257
267
  if (!isWritable(ddSpan)) return currentCode
258
268
 
259
269
  const code = status?.code
260
- if (!code || currentCode === 1) return currentCode
270
+ if (!code || currentCode === 1) {
271
+ if (otelTraceSemanticsEnabled) {
272
+ ddSpan.context().deleteTag(ERROR_MESSAGE)
273
+ ddSpan.context().deleteTag(IGNORE_OTEL_ERROR)
274
+ }
275
+ return currentCode
276
+ }
261
277
 
262
278
  if (code === 2) {
263
279
  ddSpan.addTags({
@@ -15,7 +15,7 @@ const kinds = require('../../../../ext/kinds')
15
15
  const id = require('../id')
16
16
  const BridgeSpanBase = require('./bridge-span-base')
17
17
  const SpanContext = require('./span_context')
18
- const { setOtelOperationName } = require('./span-helpers')
18
+ const { setOtelOperationName, setOtelResource } = require('./span-helpers')
19
19
 
20
20
  const spanKindNames = {
21
21
  [api.SpanKind.INTERNAL]: kinds.INTERNAL,
@@ -118,6 +118,8 @@ function spanNameMapper (spanName, kind, attributes) {
118
118
  * surface; the underlying DD span carries the lifecycle.
119
119
  */
120
120
  class Span extends BridgeSpanBase {
121
+ #otelName
122
+
121
123
  /**
122
124
  * @param {import('./tracer')} parentTracer
123
125
  * @param {import('@opentelemetry/api').Context} context
@@ -159,12 +161,15 @@ class Span extends BridgeSpanBase {
159
161
 
160
162
  super(ddSpan)
161
163
 
164
+ this._otelTraceSemanticsEnabled = _tracer._config.DD_TRACE_OTEL_SEMANTICS_ENABLED
165
+
162
166
  if (attributes) {
163
167
  this.setAttributes(attributes)
164
168
  }
165
169
 
166
170
  this._parentTracer = parentTracer
167
171
  this._context = context
172
+ this.#otelName = spanName || this._ddSpan.context()._name
168
173
 
169
174
  // NOTE: Need to grab the value before setting it on the span because the
170
175
  // math for computing opentracing timestamps is apparently lossy...
@@ -192,7 +197,7 @@ class Span extends BridgeSpanBase {
192
197
  }
193
198
 
194
199
  get name () {
195
- return this._ddSpan.context()._name
200
+ return this.#otelName
196
201
  }
197
202
 
198
203
  spanContext () {
@@ -223,7 +228,13 @@ class Span extends BridgeSpanBase {
223
228
  * @param {string} name
224
229
  */
225
230
  updateName (name) {
226
- setOtelOperationName(this._ddSpan, name)
231
+ if (this.ended) return this
232
+ this.#otelName = name
233
+ if (this._otelTraceSemanticsEnabled) {
234
+ setOtelResource(this._ddSpan, name)
235
+ } else {
236
+ setOtelOperationName(this._ddSpan, name)
237
+ }
227
238
  return this
228
239
  }
229
240
 
@@ -32,6 +32,7 @@ class SpanContext {
32
32
  }
33
33
 
34
34
  get traceFlags () {
35
+ this._ddContext._ensureSamplingPriority()
35
36
  return this._ddContext._sampling.priority >= AUTO_KEEP ? 1 : 0
36
37
  }
37
38
 
@@ -45,6 +45,10 @@ function buildResourceAttributes (config) {
45
45
  const { service, version, env, ...filteredTags } = config.tags
46
46
  Object.assign(resourceAttributes, filteredTags)
47
47
 
48
+ if (config.OTEL_TRACES_SPAN_METRICS_ENABLED) {
49
+ resourceAttributes['_dd.stats_computed'] = 'true'
50
+ }
51
+
48
52
  return resourceAttributes
49
53
  }
50
54
 
@@ -59,7 +63,8 @@ function createOtlpTraceExporter (config) {
59
63
  config.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
60
64
  config.OTEL_EXPORTER_OTLP_TRACES_HEADERS,
61
65
  config.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
62
- buildResourceAttributes(config)
66
+ buildResourceAttributes(config),
67
+ config.DD_TRACE_OTEL_SEMANTICS_ENABLED
63
68
  )
64
69
  }
65
70
 
@@ -36,10 +36,11 @@ class OtlpHttpTraceExporter extends OtlpHttpExporterBase {
36
36
  * corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
37
37
  * @param {number} timeout - Request timeout in milliseconds
38
38
  * @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
39
+ * @param {boolean} otelTraceSemanticsEnabled - When true, do not emit Datadog-only attributes as span attributes
39
40
  */
40
- constructor (url, headers, timeout, resourceAttributes) {
41
+ constructor (url, headers, timeout, resourceAttributes, otelTraceSemanticsEnabled) {
41
42
  super(url, headers, timeout, 'http/json', 'traces')
42
- this.#transformer = new OtlpTraceTransformer(resourceAttributes)
43
+ this.#transformer = new OtlpTraceTransformer(resourceAttributes, otelTraceSemanticsEnabled)
43
44
  }
44
45
 
45
46
  /**
@@ -74,6 +74,9 @@ const EXCLUDED_META_KEYS = new Set([
74
74
  TRACE_ID_128,
75
75
  ])
76
76
 
77
+ // DD-only error tags that should not appear as attributes when OTel trace semantics are enabled.
78
+ const DD_ERROR_META_KEYS = new Set(['error.message'])
79
+
77
80
  /**
78
81
  * OtlpTraceTransformer transforms DD-formatted spans to OTLP trace JSON format.
79
82
  *
@@ -87,13 +90,17 @@ const EXCLUDED_META_KEYS = new Set([
87
90
  * @augments OtlpTransformerBase
88
91
  */
89
92
  class OtlpTraceTransformer extends OtlpTransformerBase {
93
+ #otelTraceSemanticsEnabled
94
+
90
95
  /**
91
96
  * Creates a new OtlpTraceTransformer instance.
92
97
  *
93
98
  * @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
99
+ * @param {boolean} [otelTraceSemanticsEnabled] - When true, do not emit Datadog-only attributes as span attributes
94
100
  */
95
- constructor (resourceAttributes) {
101
+ constructor (resourceAttributes, otelTraceSemanticsEnabled) {
96
102
  super(resourceAttributes, 'http/json', 'traces')
103
+ this.#otelTraceSemanticsEnabled = otelTraceSemanticsEnabled
97
104
  }
98
105
 
99
106
  /**
@@ -184,24 +191,30 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
184
191
  #buildAttributes (span) {
185
192
  const attributes = []
186
193
 
187
- // Add top-level DD span fields as OTLP attributes
188
- if (span.service) {
189
- attributes.push({ key: 'service.name', value: { stringValue: span.service } })
190
- }
191
- if (span.name) {
192
- attributes.push({ key: 'operation.name', value: { stringValue: span.name } })
193
- }
194
- if (span.resource) {
195
- attributes.push({ key: 'resource.name', value: { stringValue: span.resource } })
196
- }
197
- if (span.type) {
198
- attributes.push({ key: 'span.type', value: { stringValue: span.type } })
194
+ // Add top-level DD span fields as OTLP attributes.
195
+ // When OTel trace semantics are enabled, these Datadog-only concepts
196
+ // are not added to the span attributes so the output conforms to pure
197
+ // OpenTelemetry semantics.
198
+ if (!this.#otelTraceSemanticsEnabled) {
199
+ if (span.service) {
200
+ attributes.push({ key: 'service.name', value: { stringValue: span.service } })
201
+ }
202
+ if (span.name) {
203
+ attributes.push({ key: 'operation.name', value: { stringValue: span.name } })
204
+ }
205
+ if (span.resource) {
206
+ attributes.push({ key: 'resource.name', value: { stringValue: span.resource } })
207
+ }
208
+ if (span.type) {
209
+ attributes.push({ key: 'span.type', value: { stringValue: span.type } })
210
+ }
199
211
  }
200
212
 
201
213
  // Add meta string tags, skipping keys that map to dedicated OTLP fields
202
214
  if (span.meta) {
203
215
  for (const [key, value] of Object.entries(span.meta)) {
204
216
  if (EXCLUDED_META_KEYS.has(key)) continue
217
+ if (this.#otelTraceSemanticsEnabled && DD_ERROR_META_KEYS.has(key)) continue
205
218
  attributes.push({ key, value: { stringValue: value } })
206
219
  }
207
220
  }
@@ -68,7 +68,7 @@ class TracerProvider {
68
68
  return Promise.reject(new Error('Not started'))
69
69
  }
70
70
 
71
- exporter._writer.flush()
71
+ exporter._writer?.flush()
72
72
  return this._activeProcessor.forceFlush()
73
73
  }
74
74
 
@@ -371,8 +371,8 @@ class TextMapPropagator {
371
371
 
372
372
  _hasTraceIdConflict (w3cSpanContext, firstSpanContext) {
373
373
  return w3cSpanContext !== undefined &&
374
- firstSpanContext.toTraceId(true) === w3cSpanContext.toTraceId(true) &&
375
- firstSpanContext.toSpanId() !== w3cSpanContext.toSpanId()
374
+ firstSpanContext.toTraceId(true) === w3cSpanContext.toTraceId(true) &&
375
+ firstSpanContext.toSpanId() !== w3cSpanContext.toSpanId()
376
376
  }
377
377
 
378
378
  _hasParentIdInTags (spanContext) {
@@ -875,7 +875,7 @@ class TextMapPropagator {
875
875
  (!tracestateSamplingPriority || tracestateSamplingPriority >= 0)) {
876
876
  samplingPriority = AUTO_REJECT
877
877
  } else if (traceparentSampled === 1 &&
878
- (!tracestateSamplingPriority || tracestateSamplingPriority < 0)) {
878
+ (!tracestateSamplingPriority || tracestateSamplingPriority < 0)) {
879
879
  samplingPriority = AUTO_KEEP
880
880
  }
881
881
  }
@@ -63,6 +63,7 @@ class DatadogSpanContext {
63
63
  }
64
64
 
65
65
  toTraceparent () {
66
+ this._ensureSamplingPriority()
66
67
  const flags = this._sampling.priority >= AUTO_KEEP ? '01' : '00'
67
68
  const traceId = this.toTraceId(true)
68
69
  const spanId = this.toSpanId(true)
@@ -70,6 +71,22 @@ class DatadogSpanContext {
70
71
  return `${version}-${traceId}-${spanId}-${flags}`
71
72
  }
72
73
 
74
+ /**
75
+ * Materialize the lazy priority-sampling decision for this trace, the same
76
+ * way {@link DatadogTracer#inject} does before propagation. The auto priority
77
+ * is otherwise computed at flush time, so the W3C sampled flag reads "drop"
78
+ * for a freshly started, not-yet-flushed span — see
79
+ * https://github.com/DataDog/dd-trace-js/issues/2547.
80
+ *
81
+ * The root span is only used to reach the priority sampler; `this` is passed
82
+ * to `sample()` so a manual sampling tag set directly on this context is
83
+ * honored, matching what `inject(this)` would decide.
84
+ */
85
+ _ensureSamplingPriority () {
86
+ if (this._sampling.priority !== undefined) return
87
+ this._trace.started[0]?._prioritySampler?.sample(this)
88
+ }
89
+
73
90
  /**
74
91
  * Set a tag value.
75
92
  * @param {string} key - Tag key
@@ -45,7 +45,12 @@ class DatadogTracer {
45
45
  this._exporter = new Exporter(config, this._prioritySampler)
46
46
  }
47
47
 
48
- this._processor = new SpanProcessor(this._exporter, this._prioritySampler, config)
48
+ let otlpStatsExporter
49
+ if (config.OTEL_TRACES_SPAN_METRICS_ENABLED) {
50
+ const { createOtlpSpanStatsExporter } = require('../opentelemetry/metrics')
51
+ otlpStatsExporter = createOtlpSpanStatsExporter(config)
52
+ }
53
+ this._processor = new SpanProcessor(this._exporter, this._prioritySampler, config, otlpStatsExporter)
49
54
  this._url = this._exporter._url
50
55
  this._enableGetRumData = config.experimental.enableGetRumData
51
56
  this._traceId128BitGenerationEnabled = config.traceId128BitGenerationEnabled
@@ -60,7 +60,11 @@ function maybeEnable (Plugin) {
60
60
 
61
61
  function getEnabled (Plugin) {
62
62
  const envName = `DD_TRACE_${Plugin.id.toUpperCase()}_ENABLED`
63
- return getValueFromEnvSources(normalizePluginEnvName(envName))
63
+ // skipDefault: only an explicitly configured value should drive enablement here. A registered
64
+ // default of `false` (e.g. an experimental plugin like `nats`) must not be read as an explicit
65
+ // "disabled via configuration option" — that path both logs a misleading line and nulls the
66
+ // plugin class, bypassing the experimental opt-in handled by `loadPlugin`.
67
+ return getValueFromEnvSources(normalizePluginEnvName(envName), true)
64
68
  }
65
69
 
66
70
  // TODO this must always be a singleton.
@@ -154,7 +158,7 @@ module.exports = class PluginManager {
154
158
  const {
155
159
  logInjection,
156
160
  serviceMapping,
157
- queryStringObfuscation,
161
+ DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP,
158
162
  site,
159
163
  url,
160
164
  headerTags,
@@ -164,16 +168,17 @@ module.exports = class PluginManager {
164
168
  clientIpEnabled,
165
169
  clientIpHeader,
166
170
  DD_TRACE_MEMCACHED_COMMAND_ENABLED,
171
+ DD_TRACE_OTEL_SEMANTICS_ENABLED,
167
172
  DD_TEST_SESSION_NAME,
168
173
  DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
169
- isTestDynamicInstrumentationEnabled,
174
+ testOptimization,
170
175
  isServiceUserProvided,
171
176
  middlewareTracingEnabled,
172
177
  traceWebsocketMessagesEnabled,
173
178
  traceWebsocketMessagesInheritSampling,
174
179
  traceWebsocketMessagesSeparateTraces,
175
180
  experimental,
176
- resourceRenamingEnabled,
181
+ DD_TRACE_RESOURCE_RENAMING_ENABLED,
177
182
  } = /** @type {import('./config/config-base')} */ (this._tracerConfig)
178
183
 
179
184
  const sharedConfig = {
@@ -181,27 +186,28 @@ module.exports = class PluginManager {
181
186
  dbmPropagationMode,
182
187
  dsmEnabled,
183
188
  DD_TRACE_MEMCACHED_COMMAND_ENABLED,
189
+ DD_TRACE_OTEL_SEMANTICS_ENABLED,
184
190
  site,
185
191
  url,
186
192
  headers: headerTags || [],
187
193
  clientIpHeader,
188
194
  DD_TEST_SESSION_NAME,
189
195
  DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
190
- isTestDynamicInstrumentationEnabled,
196
+ isTestDynamicInstrumentationEnabled: testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED,
191
197
  isServiceUserProvided,
192
198
  traceWebsocketMessagesEnabled,
193
199
  traceWebsocketMessagesInheritSampling,
194
200
  traceWebsocketMessagesSeparateTraces,
195
201
  experimental,
196
- resourceRenamingEnabled,
202
+ resourceRenamingEnabled: DD_TRACE_RESOURCE_RENAMING_ENABLED,
197
203
  }
198
204
 
199
205
  if (logInjection !== undefined) {
200
206
  sharedConfig.logInjection = logInjection
201
207
  }
202
208
 
203
- if (queryStringObfuscation !== undefined) {
204
- sharedConfig.queryStringObfuscation = queryStringObfuscation
209
+ if (DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP !== undefined) {
210
+ sharedConfig.queryStringObfuscation = DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP
205
211
  }
206
212
 
207
213
  if (serviceMapping && serviceMapping[name]) {
@@ -31,6 +31,7 @@ const Plugin = require('./plugin')
31
31
  const { getRepositoryRoot } = require('./util/git')
32
32
  const {
33
33
  getTestEnvironmentMetadata,
34
+ getTestLevelsMetadataTags,
34
35
  getTestSessionName,
35
36
  getCodeOwnersFileEntries,
36
37
  getTestParentSpan,
@@ -44,6 +45,7 @@ const {
44
45
  TEST_MODULE_ID,
45
46
  TEST_SESSION_ID,
46
47
  TEST_COMMAND,
48
+ TEST_LEVELS_METADATA,
47
49
  TEST_MODULE,
48
50
  TEST_SESSION_NAME,
49
51
  getTestSuiteCommonTags,
@@ -59,7 +61,7 @@ const {
59
61
  DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX,
60
62
  DI_DEBUG_ERROR_FILE_SUFFIX,
61
63
  DI_DEBUG_ERROR_LINE_SUFFIX,
62
- getLibraryCapabilitiesTags,
64
+ getLibraryCapabilitiesTags: getDefaultLibraryCapabilitiesTags,
63
65
  getPullRequestDiff,
64
66
  getModifiedFilesFromDiff,
65
67
  getPullRequestBaseBranch,
@@ -107,7 +109,7 @@ const TEST_FRAMEWORKS_TO_SKIP_GIT_METADATA_EXTRACTION = new Set([
107
109
  ])
108
110
 
109
111
  function setItrSkippingEnabledTagFromLibraryConfig (plugin, frameworkVersion) {
110
- const libraryCapabilitiesTags = getLibraryCapabilitiesTags(plugin.constructor.id, frameworkVersion)
112
+ const libraryCapabilitiesTags = getDefaultLibraryCapabilitiesTags(plugin.constructor.id, frameworkVersion)
111
113
 
112
114
  if (!libraryCapabilitiesTags[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS] ||
113
115
  !plugin.libraryConfig ||
@@ -166,7 +168,7 @@ module.exports = class CiPlugin extends Plugin {
166
168
  ? getSessionRequestErrorTags(this.testSessionSpan)
167
169
  : Object.fromEntries(this._pendingRequestErrorTags.map(({ tag, value }) => [tag, value]))
168
170
 
169
- const libraryCapabilitiesTags = getLibraryCapabilitiesTags(this.constructor.id, frameworkVersion)
171
+ const libraryCapabilitiesTags = this.getLibraryCapabilitiesTags(frameworkVersion, ctx)
170
172
  const metadataTags = {
171
173
  test: {
172
174
  ...libraryCapabilitiesTags,
@@ -219,7 +221,13 @@ module.exports = class CiPlugin extends Plugin {
219
221
  this.testEnvironmentMetadata
220
222
  )
221
223
 
222
- const metadataTags = { '*': { [TEST_COMMAND]: command, [TEST_SESSION_NAME]: testSessionName } }
224
+ const metadataTags = {
225
+ [TEST_LEVELS_METADATA]: {
226
+ [TEST_COMMAND]: command,
227
+ [TEST_SESSION_NAME]: testSessionName,
228
+ ...getTestLevelsMetadataTags(this.testEnvironmentMetadata),
229
+ },
230
+ }
223
231
  // tracer might not be initialized correctly
224
232
  if (this.tracer._exporter.addMetadataTags) {
225
233
  this.tracer._exporter.addMetadataTags(metadataTags)
@@ -423,6 +431,15 @@ module.exports = class CiPlugin extends Plugin {
423
431
  }
424
432
  }
425
433
 
434
+ /**
435
+ * Returns library capability metadata tags for this test framework.
436
+ * @param {string} frameworkVersion - The test framework version.
437
+ * @returns {Record<string, string|undefined>}
438
+ */
439
+ getLibraryCapabilitiesTags (frameworkVersion) {
440
+ return getDefaultLibraryCapabilitiesTags(this.constructor.id, frameworkVersion)
441
+ }
442
+
426
443
  /**
427
444
  * Adds a hidden _dd tag to the test session span when a test-optimization request fails.
428
445
  * If the session span does not exist yet (e.g. library-configuration failed before session:start),