dd-trace 6.12.0 → 6.14.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 (251) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  8. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  9. package/ci/test-optimization-validation/package-check.js +1 -1
  10. package/ci/test-optimization-validation/report-writer.js +7 -5
  11. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  12. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  13. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  14. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  15. package/index.d.ts +127 -26
  16. package/package.json +22 -19
  17. package/packages/datadog-esbuild/index.js +3 -1
  18. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  19. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  20. package/packages/datadog-instrumentations/src/fs.js +24 -20
  21. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  22. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  23. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  24. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  25. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  26. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  34. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  35. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
  36. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
  37. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  38. package/packages/datadog-instrumentations/src/jest.js +165 -38
  39. package/packages/datadog-instrumentations/src/knex.js +76 -0
  40. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  41. package/packages/datadog-instrumentations/src/mariadb.js +13 -0
  42. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  43. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  44. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  45. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  46. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  47. package/packages/datadog-instrumentations/src/net.js +14 -17
  48. package/packages/datadog-instrumentations/src/next.js +212 -62
  49. package/packages/datadog-instrumentations/src/openai.js +42 -40
  50. package/packages/datadog-instrumentations/src/pg.js +15 -0
  51. package/packages/datadog-instrumentations/src/pino.js +15 -4
  52. package/packages/datadog-instrumentations/src/playwright.js +137 -27
  53. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  54. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  55. package/packages/datadog-instrumentations/src/undici.js +53 -4
  56. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
  57. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  58. package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
  59. package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
  60. package/packages/datadog-instrumentations/src/winston.js +6 -3
  61. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  62. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  63. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  64. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  65. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  66. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  67. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  68. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  69. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  70. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  71. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  72. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  73. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  74. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  75. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  76. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  77. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  78. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  79. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  80. package/packages/datadog-plugin-http/src/client.js +2 -15
  81. package/packages/datadog-plugin-http2/src/client.js +2 -15
  82. package/packages/datadog-plugin-jest/src/index.js +0 -6
  83. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  84. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  85. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  86. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  87. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  88. package/packages/datadog-plugin-next/src/index.js +63 -29
  89. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  90. package/packages/datadog-plugin-playwright/src/index.js +176 -28
  91. package/packages/datadog-plugin-undici/src/index.js +29 -56
  92. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  93. package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
  94. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  95. package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
  96. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  97. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  98. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  99. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  100. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  101. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  102. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  103. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  104. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  107. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  108. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  109. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  110. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  111. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  112. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  113. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  114. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  115. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  116. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  117. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
  118. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  119. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
  122. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  124. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  125. package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
  126. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  127. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  128. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  129. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  130. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  131. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  132. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  133. package/packages/dd-trace/src/config/index.js +75 -14
  134. package/packages/dd-trace/src/config/parsers.js +11 -1
  135. package/packages/dd-trace/src/config/stable.js +3 -3
  136. package/packages/dd-trace/src/config/supported-configurations.json +47 -2
  137. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  138. package/packages/dd-trace/src/debugger/config.js +12 -1
  139. package/packages/dd-trace/src/debugger/constants.js +1 -0
  140. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  141. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  142. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
  143. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  144. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  145. package/packages/dd-trace/src/debugger/index.js +23 -3
  146. package/packages/dd-trace/src/dogstatsd.js +532 -71
  147. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  148. package/packages/dd-trace/src/encode/0.4.js +5 -5
  149. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  150. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  151. package/packages/dd-trace/src/exporters/agent/index.js +24 -3
  152. package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
  153. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  154. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  155. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  156. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  157. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  158. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  159. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  160. package/packages/dd-trace/src/exporters/common/writer.js +31 -7
  161. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  162. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  163. package/packages/dd-trace/src/flush.js +105 -0
  164. package/packages/dd-trace/src/id.js +14 -0
  165. package/packages/dd-trace/src/index.js +5 -2
  166. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
  167. package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
  168. package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
  169. package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
  170. package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
  171. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  172. package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
  173. package/packages/dd-trace/src/llmobs/index.js +111 -48
  174. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  175. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  176. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  177. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  178. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  180. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  181. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  182. package/packages/dd-trace/src/llmobs/util.js +0 -33
  183. package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
  184. package/packages/dd-trace/src/log/index.js +7 -1
  185. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
  186. package/packages/dd-trace/src/noop/span.js +5 -0
  187. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  188. package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
  189. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  190. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  191. package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
  192. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  193. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  194. package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
  195. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  196. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  199. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  200. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  201. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  202. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  203. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  204. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  205. package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
  206. package/packages/dd-trace/src/opentracing/span.js +28 -0
  207. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  208. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  209. package/packages/dd-trace/src/plugin_manager.js +2 -0
  210. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  211. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  212. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  213. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  214. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  215. package/packages/dd-trace/src/plugins/util/test.js +157 -41
  216. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  217. package/packages/dd-trace/src/plugins/util/web.js +6 -74
  218. package/packages/dd-trace/src/profiling/config.js +1 -0
  219. package/packages/dd-trace/src/profiling/oom.js +11 -5
  220. package/packages/dd-trace/src/profiling/profiler.js +30 -13
  221. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  222. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  223. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  224. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  225. package/packages/dd-trace/src/proxy.js +108 -10
  226. package/packages/dd-trace/src/remote_config/index.js +319 -101
  227. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  228. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  229. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  230. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
  231. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
  232. package/packages/dd-trace/src/sampling_rule.js +36 -9
  233. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
  234. package/packages/dd-trace/src/serverless/vercel.js +155 -0
  235. package/packages/dd-trace/src/serverless.js +45 -21
  236. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  237. package/packages/dd-trace/src/span_stats.js +64 -31
  238. package/packages/dd-trace/src/startup-log.js +3 -2
  239. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  240. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  241. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  242. package/packages/dd-trace/src/tracer.js +26 -5
  243. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  244. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  245. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  246. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  247. package/vendor/dist/protobufjs/index.js +1 -1
  248. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  249. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  250. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
  251. package/packages/dd-trace/src/encode/agentless-json.js +0 -209
@@ -15,6 +15,7 @@ const {
15
15
  } = require('../../../ext/tags')
16
16
  const { ORIGIN_KEY, TOP_LEVEL_KEY, SVC_SRC_KEY, GRPC_STATUS_NAMES } = require('./constants')
17
17
  const id = require('./id')
18
+ const log = require('./log')
18
19
 
19
20
  const GRPC_STATUS_CODE_MAP = Object.fromEntries(GRPC_STATUS_NAMES.map((name, i) => [name, String(i)]))
20
21
  const ZERO_ID = id('0')
@@ -135,19 +136,8 @@ class SpanAggKey {
135
136
  }
136
137
 
137
138
  toString () {
138
- return [
139
- this.name,
140
- this.service,
141
- this.resource,
142
- this.type,
143
- this.statusCode,
144
- this.synthetics,
145
- this.method,
146
- this.endpoint,
147
- this.srvSrc,
148
- this.spanKind,
149
- this.rpcStatusCode,
150
- ].join(',')
139
+ return `${this.name},${this.service},${this.resource},${this.type},${this.statusCode},${this.synthetics},` +
140
+ `${this.method},${this.endpoint},${this.srvSrc},${this.spanKind},${this.rpcStatusCode}`
151
141
  }
152
142
  }
153
143
 
@@ -200,19 +190,26 @@ class TimeBuckets extends Map {
200
190
  }
201
191
 
202
192
  class SpanStatsProcessor {
203
- constructor ({
204
- stats: {
205
- DD_TRACE_STATS_COMPUTATION_ENABLED: enabled = false,
206
- interval = 10,
207
- } = {},
208
- hostname,
209
- port,
210
- url,
211
- env,
212
- tags,
213
- version: appVersion,
214
- _DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL: flushIntervalMs,
215
- } = {}, otlpExporter) {
193
+ #config
194
+
195
+ /**
196
+ * @param {import('./config/config-base')} config
197
+ * @param {import('./opentelemetry/metrics/otlp_span_stats_exporter').OtlpStatsExporter} [otlpExporter]
198
+ */
199
+ constructor (config, otlpExporter) {
200
+ const {
201
+ stats: {
202
+ DD_TRACE_STATS_COMPUTATION_ENABLED: enabled = false,
203
+ interval = 10,
204
+ } = {},
205
+ hostname,
206
+ port,
207
+ url,
208
+ env,
209
+ tags,
210
+ version: appVersion,
211
+ _DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL: flushIntervalMs,
212
+ } = config
216
213
  if (!otlpExporter) {
217
214
  this.exporter = new SpanStatsExporter({ hostname, port, tags, url })
218
215
  }
@@ -224,7 +221,7 @@ class SpanStatsProcessor {
224
221
  this.enabled = enabled
225
222
  this.otlpExporter = otlpExporter || null
226
223
  this.env = env
227
- this.tags = tags || {}
224
+ this.#config = config
228
225
  this.sequence = 0
229
226
  this.version = appVersion
230
227
 
@@ -235,6 +232,18 @@ class SpanStatsProcessor {
235
232
  }
236
233
 
237
234
  onInterval () {
235
+ this.#flush()
236
+ }
237
+
238
+ /**
239
+ * Drains pending span statistics and waits for their export.
240
+ * @param {Function} [done]
241
+ */
242
+ forceFlush (done) {
243
+ this.#flush(done)
244
+ }
245
+
246
+ #flush (done) {
238
247
  const drained = this.#drainBuckets()
239
248
 
240
249
  if (this.enabled && !this.otlpExporter) {
@@ -245,13 +254,37 @@ class SpanStatsProcessor {
245
254
  Stats: this.#toV06Payload(drained),
246
255
  Lang: 'javascript',
247
256
  TracerVersion: pkg.version,
248
- RuntimeID: this.tags['runtime-id'],
257
+ RuntimeID: this.#config.tags['runtime-id'],
249
258
  Sequence: ++this.sequence,
250
259
  ProcessTags: processTags.serialized,
251
- })
260
+ }, done)
252
261
  } else if (this.otlpExporter && drained.length > 0) {
253
- this.otlpExporter.export(drained, this.bucketSizeNs)
254
- }
262
+ if (typeof this.otlpExporter.flush === 'function' && done) {
263
+ // Snapshot requests already in flight before starting this boundary
264
+ // export, so a later invocation cannot extend this lifecycle barrier.
265
+ let pending = 2
266
+ const complete = () => {
267
+ if (--pending === 0) done()
268
+ }
269
+ try {
270
+ this.otlpExporter.flush(complete)
271
+ } catch (error) {
272
+ log.error('Failed to flush OTLP span stats:', error)
273
+ complete()
274
+ }
275
+ try {
276
+ this.otlpExporter.export(drained, this.bucketSizeNs, complete)
277
+ } catch (error) {
278
+ log.error('Failed to export OTLP span stats:', error)
279
+ complete()
280
+ }
281
+ } else {
282
+ this.otlpExporter.export(drained, this.bucketSizeNs, done)
283
+ }
284
+ } else if (this.otlpExporter) {
285
+ if (typeof this.otlpExporter.flush === 'function') this.otlpExporter.flush(done)
286
+ else done?.()
287
+ } else done?.()
255
288
  }
256
289
 
257
290
  onSpanFinished (span) {
@@ -78,7 +78,7 @@ function configInfo () {
78
78
  const url = config.url
79
79
  const profilingEnabled = config.profiling.DD_PROFILING_ENABLED
80
80
 
81
- return {
81
+ const startupLog = {
82
82
  [inspect.custom] () {
83
83
  return String(this)
84
84
  },
@@ -102,7 +102,6 @@ function configInfo () {
102
102
  sample_rate: config.sampler.sampleRate,
103
103
  sampling_rules: samplingRules,
104
104
  tags: config.tags,
105
- ...(config.tags && config.tags.version && { dd_version: config.tags.version }),
106
105
  log_injection_enabled: !!config.logInjection,
107
106
  runtime_metrics_enabled: !!config.runtimeMetrics,
108
107
  profiling_enabled: profilingEnabled === 'true' || profilingEnabled === 'auto',
@@ -112,6 +111,8 @@ function configInfo () {
112
111
  otlp_metrics_export_enabled: !!config.DD_METRICS_OTEL_ENABLED,
113
112
  otlp_logs_export_enabled: !!config.DD_LOGS_OTEL_ENABLED,
114
113
  }
114
+ if (config.tags?.version) startupLog.dd_version = config.tags.version
115
+ return startupLog
115
116
  }
116
117
 
117
118
  /**
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const { createSiteUrl } = require('../exporters/common/url')
4
+
5
+ const STAGING_SITE = 'datad0g.com'
6
+
7
+ let cachedSite
8
+ let cachedUrl
9
+
10
+ /**
11
+ * @param {string} site
12
+ * @returns {URL}
13
+ */
14
+ function getAgentlessTelemetryUrl (site) {
15
+ if (site === cachedSite && cachedUrl !== undefined) return cachedUrl
16
+
17
+ const intake = site === STAGING_SITE
18
+ ? 'all-http-intake.logs'
19
+ : 'instrumentation-telemetry-intake'
20
+ const url = createSiteUrl(site, intake)
21
+ if (url === undefined) throw new Error(`Invalid DD_SITE for agentless telemetry: ${site}`)
22
+
23
+ cachedSite = site
24
+ cachedUrl = url
25
+ return url
26
+ }
27
+
28
+ module.exports = getAgentlessTelemetryUrl
@@ -2,6 +2,7 @@
2
2
 
3
3
  const request = require('../exporters/common/request')
4
4
  const log = require('../log')
5
+ const getAgentlessTelemetryUrl = require('./agentless-url')
5
6
 
6
7
  /**
7
8
  * @typedef {Record<string, unknown>} TelemetryPayloadObject
@@ -69,6 +70,7 @@ const log = require('../log')
69
70
  */
70
71
 
71
72
  let agentTelemetry = true
73
+ let getTestOptimizationAgent
72
74
 
73
75
  /**
74
76
  * @param {import('../config/config-base')} config
@@ -98,16 +100,6 @@ function getHeaders (config, application, reqType) {
98
100
  return headers
99
101
  }
100
102
 
101
- /**
102
- * @param {string | undefined} site
103
- */
104
- function getAgentlessTelemetryEndpoint (site) {
105
- if (site === 'datad0g.com') { // staging
106
- return 'https://all-http-intake.logs.datad0g.com'
107
- }
108
- return `https://instrumentation-telemetry-intake.${site}`
109
- }
110
-
111
103
  let seqId = 0
112
104
 
113
105
  /**
@@ -144,10 +136,18 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
144
136
  let url = config.url
145
137
 
146
138
  const isCiVisibilityAgentlessMode = isCiVisibility && testOptimization.DD_CIVISIBILITY_AGENTLESS_ENABLED
139
+ const isApmTracingAgentlessMode = config.experimental?.exporter === 'agentless'
140
+ const isAgentlessMode = isCiVisibilityAgentlessMode || isApmTracingAgentlessMode
141
+
142
+ if (isCiVisibility && getTestOptimizationAgent === undefined) {
143
+ ({ getAgent: getTestOptimizationAgent } = require('../ci-visibility/exporters/agents'))
144
+ }
147
145
 
148
- if (isCiVisibilityAgentlessMode) {
146
+ if (isAgentlessMode) {
149
147
  try {
150
- url = testOptimization.DD_CIVISIBILITY_AGENTLESS_URL ?? new URL(getAgentlessTelemetryEndpoint(config.site))
148
+ url = isCiVisibilityAgentlessMode
149
+ ? testOptimization.DD_CIVISIBILITY_AGENTLESS_URL ?? getAgentlessTelemetryUrl(config.site)
150
+ : getAgentlessTelemetryUrl(config.site)
151
151
  } catch (err) {
152
152
  log.error('Telemetry endpoint url is invalid', err)
153
153
  // No point to do the request if the URL is invalid
@@ -160,9 +160,10 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
160
160
  hostname,
161
161
  port,
162
162
  method: 'POST',
163
- path: isCiVisibilityAgentlessMode ? '/api/v2/apmtelemetry' : '/telemetry/proxy/api/v2/apmtelemetry',
163
+ path: isAgentlessMode ? '/api/v2/apmtelemetry' : '/telemetry/proxy/api/v2/apmtelemetry',
164
164
  headers: getHeaders(config, application, reqType),
165
165
  }
166
+ if (isCiVisibility) options.agent = getTestOptimizationAgent(url)
166
167
 
167
168
  const data = JSON.stringify({
168
169
  api_version: 'v2',
@@ -177,7 +178,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
177
178
  })
178
179
 
179
180
  request(data, options, (error) => {
180
- if (error && config.DD_API_KEY && config.site) {
181
+ if (!isAgentlessMode && error && config.DD_API_KEY && config.site) {
181
182
  if (agentTelemetry) {
182
183
  log.warn('Agent telemetry failed, started agentless telemetry')
183
184
  agentTelemetry = false
@@ -185,7 +186,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
185
186
  // figure out which data center to send to
186
187
  let backendUrl
187
188
  try {
188
- backendUrl = new URL(getAgentlessTelemetryEndpoint(config.site))
189
+ backendUrl = getAgentlessTelemetryUrl(config.site)
189
190
  } catch {
190
191
  log.error('Invalid Telemetry URL')
191
192
  return
@@ -197,6 +198,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
197
198
  headers: backendHeader,
198
199
  path: '/api/v2/apmtelemetry',
199
200
  }
201
+ if (isCiVisibility) backendOptions.agent = getTestOptimizationAgent(backendUrl)
200
202
  request(data, backendOptions, (error) => {
201
203
  if (error) {
202
204
  log.error('Error sending telemetry data', error)
@@ -204,7 +206,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
204
206
  })
205
207
  }
206
208
 
207
- if (!error && !agentTelemetry) {
209
+ if (!isAgentlessMode && !error && !agentTelemetry) {
208
210
  agentTelemetry = true
209
211
  log.info('Started agent telemetry')
210
212
  }
@@ -4,13 +4,14 @@ const dc = /** @type {typeof import('diagnostics_channel')} */ (require('dc-poly
4
4
  const childProcessChannel = dc.tracingChannel('datadog:child_process:execution')
5
5
 
6
6
  let subscribed = false
7
- let runtimeId
7
+ let savedConfig
8
8
 
9
9
  function isOptionsObject (value) {
10
10
  return value != null && typeof value === 'object' && !Array.isArray(value) && value
11
11
  }
12
12
 
13
13
  function getEnvWithRuntimeId (env) {
14
+ const runtimeId = savedConfig.DD_ROOT_JS_SESSION_ID || savedConfig.tags['runtime-id']
14
15
  // eslint-disable-next-line eslint-rules/eslint-process-env
15
16
  return { ...(env ?? process.env), DD_ROOT_JS_SESSION_ID: runtimeId }
16
17
  }
@@ -43,7 +44,7 @@ function start (config) {
43
44
  if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED || subscribed) return
44
45
  subscribed = true
45
46
 
46
- runtimeId = config.DD_ROOT_JS_SESSION_ID || config.tags['runtime-id']
47
+ savedConfig = config
47
48
 
48
49
  childProcessChannel.subscribe(
49
50
  /** @type {import('diagnostics_channel').TracingChannelSubscribers<object>} */ ({ start: onChildProcessStart })
@@ -13,6 +13,7 @@ const { isError } = require('./util')
13
13
  const { setStartupLogConfig } = require('./startup-log')
14
14
  const { DataStreamsCheckpointer, DataStreamsManager, DataStreamsProcessor } = require('./datastreams')
15
15
  const { IS_SERVERLESS } = require('./serverless')
16
+ const { flushServerlessTelemetry } = require('./flush')
16
17
  const log = require('./log')
17
18
  // Always-on writer (console.warn), not the channel-gated `log`: these surface regardless of
18
19
  // DD_TRACE_DEBUG.
@@ -42,13 +43,10 @@ class DatadogTracer extends Tracer {
42
43
 
43
44
  if (!IS_SERVERLESS) {
44
45
  const storeConfig = require('./tracer_metadata')
45
- // Keep a reference to the handle, to keep the memfd alive in memory.
46
- // It is read by the service discovery feature.
47
46
  const metadata = storeConfig(config)
48
47
  if (metadata === undefined) {
49
48
  log.warn('Could not store tracer configuration for service discovery')
50
49
  }
51
- this._inmem_cfg = metadata
52
50
  }
53
51
  }
54
52
 
@@ -150,6 +148,27 @@ class DatadogTracer extends Tracer {
150
148
  this._dataStreamsProcessor.setUrl(url)
151
149
  }
152
150
 
151
+ /**
152
+ * Flushes every configured telemetry pipeline for a serverless lifecycle.
153
+ * @param {Function} [done] Called after every configured export completes
154
+ * @param {{ timeout?: number }} [options] Bounds this flush operation.
155
+ */
156
+ flushAll (done, options) {
157
+ const traceExporter = this._exporter
158
+ const spanStats = this._processor?._stats
159
+ const traceFlusher = typeof traceExporter?.flush === 'function'
160
+ ? callback => traceExporter.flush(callback)
161
+ : undefined
162
+ const spanStatsFlusher = typeof spanStats?.forceFlush === 'function'
163
+ ? callback => spanStats.forceFlush(callback)
164
+ : undefined
165
+
166
+ flushServerlessTelemetry(done, options, {
167
+ trace: traceFlusher,
168
+ spanStats: spanStatsFlusher,
169
+ })
170
+ }
171
+
153
172
  scope () {
154
173
  return this._scope
155
174
  }
@@ -158,8 +177,10 @@ class DatadogTracer extends Tracer {
158
177
  if (!this._enableGetRumData) {
159
178
  return ''
160
179
  }
161
- const span = this.scope().active().context()
162
- const traceId = span.toTraceId()
180
+ const span = this.scope().active()
181
+ if (!span) return ''
182
+
183
+ const traceId = span.context().toTraceId()
163
184
  const traceTime = Date.now()
164
185
  return `\
165
186
  <meta name="dd-trace-id" content="${traceId}" />\
@@ -2,11 +2,18 @@
2
2
 
3
3
  const tracerVersion = require('../../../version').VERSION
4
4
 
5
+ // Service discovery reads this metadata after tracer initialization; retaining the handle keeps
6
+ // the backing memfd open for the process lifetime.
7
+ let metadataHandle
8
+
9
+ /**
10
+ * @param {import('./config/config-base')} config
11
+ */
5
12
  function storeConfig (config) {
6
13
  try {
7
14
  // Load binding first to not import other modules if it throws
8
- const libdatadog = require('@datadog/libdatadog')
9
- const processDiscovery = libdatadog.maybeLoad('process-discovery')
15
+ const libdatadogExtras = require('@datadog/libdatadog-extras')
16
+ const processDiscovery = libdatadogExtras.maybeLoad('process-discovery')
10
17
  if (processDiscovery === undefined) {
11
18
  return
12
19
  }
@@ -42,7 +49,8 @@ function storeConfig (config) {
42
49
  threadlocalMetadata
43
50
  )
44
51
 
45
- return processDiscovery.storeMetadata(metadata)
52
+ metadataHandle = processDiscovery.storeMetadata(metadata)
53
+ return metadataHandle
46
54
  } catch {
47
55
  // Either libdatadog or process-discovery is unavailable.
48
56
  }
@@ -3,8 +3,9 @@
3
3
  // Per-span cache of "which tag bag from the started-spans chain identifies
4
4
  // this span (or its nearest web-server ancestor) as a web-server span?"
5
5
  // Populated lazily on first `getCachedWebTags(span)`, refreshed
6
- // automatically when a `dd-trace:span:tags:update` event promotes a
7
- // previously-empty answer for that span into a real value.
6
+ // automatically when a `dd-trace:span:tags:update` event turns a span into a
7
+ // web-server span after the fact — for that span and for every descendant
8
+ // whose answer the promotion changes.
8
9
  //
9
10
  // Used by the wall profiler (endpoint-collection label on samples) and by
10
11
  // the OTEP-4947 thread-context writer (endpoint attribute in the record);
@@ -13,11 +14,12 @@
13
14
  //
14
15
  // Consumers that want to react to late web-server-span discovery
15
16
  // subscribe to `resolvedCh` — a diagnostics channel we publish on once
16
- // per span at the moment its cached webTags transitions from undefined
17
- // to a real value. Doing it via a channel (rather than exposing a
18
- // stateful "did the transition happen?" query) means each consumer sees
19
- // every transition exactly once, regardless of subscription order or
20
- // how many other consumers are attached.
17
+ // per span at the moment its cached webTags changes, i.e. when a promotion
18
+ // gives it an answer it didn't have or replaces the one it had with a
19
+ // nearer one. Doing it via a channel (rather than exposing a stateful "did
20
+ // the transition happen?" query) means each consumer sees every transition
21
+ // exactly once, regardless of subscription order or how many other
22
+ // consumers are attached.
21
23
  //
22
24
  // `endpointResolvedCh` is the same idea one field over: published once per
23
25
  // web-server span at the moment its endpoint name settles (see finalEndpoint in
@@ -63,17 +65,21 @@ function getCache (span) {
63
65
  // Returns the web-server tag bag for this span or its nearest web-server
64
66
  // ancestor in the started-spans chain, or undefined if none is a
65
67
  // web-server span. Lazy: walks the parent chain on the first call, caches
66
- // the result on the span.
68
+ // the result on the span. Answers only ever change through onTagsUpdate, which
69
+ // rewrites the affected entries in place, so a resolved answer is never
70
+ // revisited here.
67
71
  function getCachedWebTags (span) {
68
72
  const cached = getCache(span)
69
73
  if (cached.resolved) return cached.webTags
70
74
  const spanContext = span.context()
71
75
  const tags = spanContext.getTags()
76
+ const parentId = spanContext._parentId
72
77
  let webTags
73
78
  if (isWebServerSpan(tags)) {
74
79
  webTags = tags
75
- } else {
76
- const parentId = spanContext._parentId
80
+ // A span with no parent has nothing to inherit from, and looking for one
81
+ // anyway means scanning the entire started-spans list to conclude that.
82
+ } else if (parentId != null) {
77
83
  const startedSpans = getStartedSpans(spanContext)
78
84
  for (let i = startedSpans.length; --i >= 0;) {
79
85
  const ispan = startedSpans[i]
@@ -97,11 +103,17 @@ function getCachedWebTags (span) {
97
103
  function onTagsUpdate (span) {
98
104
  const cached = span[CachedSym]
99
105
  if (cached === undefined || !cached.resolved) return
100
- const tags = span.context().getTags()
101
- if (cached.webTags === undefined) {
102
- if (!isWebServerSpan(tags)) return
106
+ const spanContext = span.context()
107
+ const tags = spanContext.getTags()
108
+ // Anything but this span's own bag is an answer that predates it being a
109
+ // web-server span: either empty, or an outer web-server ancestor's bag that
110
+ // this span now supersedes for itself and for its descendants. A span whose
111
+ // cached answer already is its own bag is the overwhelmingly common case here
112
+ // (every further tag update on a request span), and stops at one comparison.
113
+ if (cached.webTags !== tags && isWebServerSpan(tags)) {
103
114
  cached.webTags = tags
104
115
  resolvedCh.publish(span)
116
+ resolveDescendants(span, spanContext, tags)
105
117
  }
106
118
  // Endpoint finality is a property of the web-server span itself: for a
107
119
  // descendant, cached.webTags is an ancestor's bag rather than these tags, and
@@ -114,6 +126,48 @@ function onTagsUpdate (span) {
114
126
  endpointResolvedCh.publish(span)
115
127
  }
116
128
 
129
+ // A span has just become a web-server span; answers cached for its descendants
130
+ // before that moment found a farther ancestor or nothing at all, and are now
131
+ // wrong. Rewrite them here rather than letting each descendant discover it on
132
+ // its next lookup: a descendant that is already active keeps handing samplers
133
+ // its stale answer for as long as it runs without re-entering storage, which is
134
+ // precisely the uninterrupted synchronous stretch profiling cares about.
135
+ //
136
+ // One forward pass over the trace's started-spans list, which is in creation
137
+ // order, so a span's parent always precedes it: `answers` holds the new answer
138
+ // for each span in the promoted span's subtree, keyed by span id, and a span
139
+ // inherits its parent's unless it is a web-server span itself, in which case its
140
+ // own bag shadows the promotion for its own subtree. Spans outside the subtree
141
+ // are never in `answers`, so they cost one map lookup and nothing else.
142
+ //
143
+ // That same creation order means nothing before the promoted span can be a
144
+ // descendant of it, so the pass starts just past it. Locating it from the end
145
+ // costs one comparison in the common case, where a request span is promoted as
146
+ // it is created and is still the newest entry — leaving nothing to visit and
147
+ // nothing to allocate. A span the list no longer holds (finished, and dropped by
148
+ // a partial flush) ends that scan at -1, which visits the whole list.
149
+ function resolveDescendants (span, spanContext, webTags) {
150
+ const startedSpans = getStartedSpans(spanContext)
151
+ let index = startedSpans.length - 1
152
+ while (index >= 0 && startedSpans[index] !== span) index--
153
+ index++
154
+ if (index === startedSpans.length) return
155
+ const answers = new Map([[spanContext._spanId, webTags]])
156
+ for (; index < startedSpans.length; index++) {
157
+ const descendant = startedSpans[index]
158
+ const context = descendant.context()
159
+ const inherited = answers.get(context._parentId)
160
+ if (inherited === undefined) continue
161
+ const tags = context.getTags()
162
+ const answer = isWebServerSpan(tags) ? tags : inherited
163
+ answers.set(context._spanId, answer)
164
+ const cached = descendant[CachedSym]
165
+ if (cached === undefined || !cached.resolved || cached.webTags === answer) continue
166
+ cached.webTags = answer
167
+ resolvedCh.publish(descendant)
168
+ }
169
+ }
170
+
117
171
  let activeCount = 0
118
172
 
119
173
  function activate () {