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
@@ -1,42 +1,55 @@
1
1
  'use strict'
2
2
 
3
+ const { createAgentlessExporter } = require('@datadog/libdatadog')
4
+
5
+ const { storage } = require('../../../../datadog-core')
3
6
  const getConfig = require('../../config')
4
7
  const log = require('../../log')
5
- const request = require('../common/request')
6
8
  const tracerVersion = require('../../../../../package.json').version
7
9
 
10
+ const { canSendApiKey } = require('../common/url')
8
11
  const BaseWriter = require('../common/writer')
9
- const { AgentlessJSONEncoder } = require('../../encode/agentless-json')
12
+ const { AgentEncoder } = require('../../encode/0.4')
10
13
  const { computeIntakeUrl, INTAKE_PATH } = require('./intake')
11
14
 
15
+ const legacyStorage = storage('legacy')
16
+
12
17
  /**
13
18
  * Writer for agentless APM trace intake.
14
- * Sends traces directly to the Datadog intake endpoint without an agent.
19
+ * Encodes traces as v0.4 MessagePack and delegates transformation and delivery
20
+ * to the APM data pipeline.
15
21
  */
16
22
  class AgentlessWriter extends BaseWriter {
17
23
  #apiKeyMissing = false
24
+ #apiKeyUnsafeReceiver = false
25
+ #exporter
26
+ #exporterApiKey
27
+ #exporterEnv
28
+ #exporterRuntimeId
29
+ #metadata
18
30
  #urlMissing = false
19
31
 
20
32
  /**
21
33
  * @param {object} options - Writer options
22
34
  * @param {URL} [options.url] - The intake URL. If not provided, constructed from site.
23
35
  * @param {string} [options.site] - The Datadog site
24
- * @param {object} [options.metadata] - Metadata to pass to the encoder (hostname, env, etc.)
36
+ * @param {object} [options.metadata] - Metadata to pass to the data pipeline
25
37
  */
26
38
  constructor ({ url, site = 'datadoghq.com', metadata = {} }) {
27
39
  super({ url })
28
- this._encoder = new AgentlessJSONEncoder(this, metadata)
40
+ this.#metadata = metadata
41
+ this._encoder = new AgentEncoder(this)
29
42
 
30
43
  if (!url) {
31
44
  try {
32
45
  this._url = new URL(computeIntakeUrl(site))
33
- } catch (err) {
46
+ } catch (error) {
34
47
  log.error(
35
48
  'Invalid site value for agentless intake: %s. Cannot construct URL. Error: %s',
36
49
  site,
37
- err.message
50
+ error.message
38
51
  )
39
- this._url = null
52
+ this._url = undefined
40
53
  }
41
54
  }
42
55
 
@@ -46,59 +59,23 @@ class AgentlessWriter extends BaseWriter {
46
59
  }
47
60
  }
48
61
 
62
+ /**
63
+ * @param {URL} url - The new intake URL.
64
+ */
49
65
  setUrl (url) {
50
66
  super.setUrl(url)
67
+ this.#closeExporter()
51
68
  if (url) {
52
69
  this.#urlMissing = false
53
70
  }
54
71
  }
55
72
 
56
73
  /**
57
- * Flushes accumulated traces to the intake as a single request.
58
- * @param {Function} [done] - Callback when send completes
59
- */
60
- flush (done = () => {}) {
61
- if (!request.writable) {
62
- const count = this._encoder.count()
63
- if (count > 0) {
64
- log.error('Maximum number of active requests reached. Dropping %d trace(s).', count)
65
- }
66
- this._encoder.reset()
67
- done()
68
- return
69
- }
70
-
71
- const count = this._encoder.count()
72
-
73
- if (count === 0) {
74
- done()
75
- return
76
- }
77
-
78
- const payload = this._encoder.makePayload()
79
-
80
- if (payload.length === 0) {
81
- log.debug('Skipping send of empty payload')
82
- done()
83
- return
84
- }
85
-
86
- this._sendPayload(payload, count, done)
87
- }
88
-
89
- /**
90
- * Sends the encoded payload to the intake endpoint.
91
- * @param {Buffer} data - The encoded JSON payload
92
- * @param {number} count - Number of traces in the payload
93
- * @param {Function} done - Callback when complete
74
+ * @param {Buffer} data - v0.4 MessagePack payload.
75
+ * @param {number} count - Number of traces in the payload.
76
+ * @param {() => void} done - Callback invoked after delivery completes or fails.
94
77
  */
95
78
  _sendPayload (data, count, done) {
96
- if (!data || data.length === 0) {
97
- log.debug('Skipping send of empty payload')
98
- done()
99
- return
100
- }
101
-
102
79
  if (!this._url) {
103
80
  if (!this.#urlMissing) {
104
81
  this.#urlMissing = true
@@ -121,81 +98,86 @@ class AgentlessWriter extends BaseWriter {
121
98
  }
122
99
  this.#apiKeyMissing = false
123
100
 
124
- const options = {
125
- path: INTAKE_PATH,
126
- method: 'POST',
127
- headers: {
128
- 'Content-Type': 'application/json',
129
- 'dd-api-key': DD_API_KEY,
130
- 'X-Datadog-Trace-Count': String(count),
131
- 'Datadog-Meta-Lang': 'nodejs',
132
- 'Datadog-Meta-Lang-Version': process.version,
133
- 'Datadog-Meta-Lang-Interpreter': process.versions.bun ? 'JavaScriptCore' : 'v8',
134
- 'Datadog-Meta-Tracer-Version': tracerVersion,
135
- },
136
- timeout: 15_000,
137
- url: this._url,
101
+ // The WASM transport performs its HTTP request in JavaScript. Keep that
102
+ // internal request out of the instrumented application's traces.
103
+ try {
104
+ legacyStorage.run({ noop: true }, () => {
105
+ const exporter = this.#applyConfiguration(DD_API_KEY)
106
+ if (exporter) {
107
+ exporter.sendV04(data, done, log)
108
+ } else {
109
+ done()
110
+ }
111
+ })
112
+ } catch (error) {
113
+ const message = error instanceof Error ? error.message : String(error)
114
+ log.error('Failed to send %d trace(s) to the agentless intake: %s', count, message)
115
+ done()
138
116
  }
117
+ }
139
118
 
140
- log.debug('Request to the agentless intake: %j', options)
119
+ /**
120
+ * @returns {string} The full agentless intake endpoint.
121
+ */
122
+ #endpoint () {
123
+ const endpoint = new URL(this._url)
124
+ endpoint.pathname = INTAKE_PATH
125
+ endpoint.search = ''
126
+ endpoint.hash = ''
127
+ return endpoint.href
128
+ }
141
129
 
142
- request(data, options, (err, res, statusCode) => {
143
- if (err) {
144
- this.#logRequestError(err, statusCode, count)
145
- done()
146
- return
130
+ /**
131
+ * @param {string} apiKey - Datadog API key.
132
+ * @returns {import('@datadog/libdatadog').AgentlessExporter|undefined}
133
+ */
134
+ #applyConfiguration (apiKey) {
135
+ if (!canSendApiKey(this._url.protocol, this._url.hostname)) {
136
+ if (!this.#apiKeyUnsafeReceiver) {
137
+ this.#apiKeyUnsafeReceiver = true
138
+ log.warn('DD_API_KEY will not be sent because the configured receiver is neither HTTPS nor loopback.')
147
139
  }
140
+ return
141
+ }
142
+ this.#apiKeyUnsafeReceiver = false
143
+
144
+ const { env, runtimeID } = this.#metadata
145
+ if (
146
+ this.#exporter &&
147
+ this.#exporterApiKey === apiKey &&
148
+ this.#exporterEnv === env &&
149
+ this.#exporterRuntimeId === runtimeID
150
+ ) {
151
+ return this.#exporter
152
+ }
148
153
 
149
- log.debug('Response from the agentless intake: %s', res)
150
- done()
154
+ this.#closeExporter()
155
+ const config = getConfig()
156
+ this.#exporter = createAgentlessExporter({
157
+ endpoint: this.#endpoint(),
158
+ apiKey,
159
+ hostname: this.#metadata.hostname,
160
+ env,
161
+ service: config.service,
162
+ version: config.version,
163
+ runtimeId: runtimeID,
164
+ containerId: this.#metadata.containerId,
165
+ tracerVersion,
166
+ languageVersion: process.version,
167
+ languageInterpreter: process.versions.bun ? 'JavaScriptCore' : 'v8',
151
168
  })
169
+ this.#exporterApiKey = apiKey
170
+ this.#exporterEnv = env
171
+ this.#exporterRuntimeId = runtimeID
172
+ return this.#exporter
152
173
  }
153
174
 
154
- /**
155
- * Logs request errors with status-specific guidance.
156
- * @param {Error} err - The error object
157
- * @param {number} statusCode - HTTP status code (if available)
158
- * @param {number} count - Number of traces that were being sent
159
- */
160
- #logRequestError (err, statusCode, count) {
161
- if (statusCode === 401 || statusCode === 403) {
162
- log.error(
163
- 'Authentication failed sending %d trace(s) (status %s). Verify DD_API_KEY is valid.',
164
- count,
165
- statusCode
166
- )
167
- } else if (statusCode === 404) {
168
- log.error(
169
- 'Trace intake endpoint not found (status %s). Verify DD_SITE is correctly configured. %d trace(s) dropped.',
170
- statusCode,
171
- count
172
- )
173
- } else if (statusCode === 429) {
174
- log.error(
175
- 'Rate limited by trace intake (status 429). %d trace(s) dropped.',
176
- count
177
- )
178
- } else if (statusCode >= 500) {
179
- log.error(
180
- 'Trace intake server error (status %s). %d trace(s) dropped. This may be transient.',
181
- statusCode,
182
- count
183
- )
184
- } else if (statusCode) {
185
- log.error(
186
- 'Error sending agentless payload (status %s): %s. %d trace(s) dropped.',
187
- statusCode,
188
- err.message,
189
- count
190
- )
191
- } else {
192
- log.error(
193
- 'Network error sending %d trace(s) to %s: %s',
194
- count,
195
- this._url?.hostname || 'unknown',
196
- err.message
197
- )
198
- }
175
+ #closeExporter () {
176
+ this.#exporter?.close()
177
+ this.#exporter = undefined
178
+ this.#exporterApiKey = undefined
179
+ this.#exporterEnv = undefined
180
+ this.#exporterRuntimeId = undefined
199
181
  }
200
182
  }
201
183
 
@@ -7,9 +7,15 @@ const { storage } = require('../../../../datadog-core')
7
7
  const legacyStorage = storage('legacy')
8
8
 
9
9
  const keepAlive = true
10
- const maxSockets = 1
11
10
 
12
- function createAgentClass (BaseAgent) {
11
+ /**
12
+ * Creates an agent class that suppresses tracing around socket lifecycle operations.
13
+ *
14
+ * @param {typeof http.Agent|typeof https.Agent} BaseAgent
15
+ * @param {number} maxSockets
16
+ * @returns {typeof http.Agent|typeof https.Agent}
17
+ */
18
+ function createAgentClass (BaseAgent, maxSockets) {
13
19
  class CustomAgent extends BaseAgent {
14
20
  constructor () {
15
21
  super({ keepAlive, maxSockets })
@@ -35,10 +41,26 @@ function createAgentClass (BaseAgent) {
35
41
  return CustomAgent
36
42
  }
37
43
 
38
- const HttpAgent = createAgentClass(http.Agent)
39
- const HttpsAgent = createAgentClass(https.Agent)
44
+ /**
45
+ * Creates isolated HTTP and HTTPS agents with a bounded connection pool.
46
+ *
47
+ * @param {number} maxSockets
48
+ * @returns {{ httpAgent: http.Agent, httpsAgent: https.Agent }}
49
+ */
50
+ function createAgents (maxSockets) {
51
+ const HttpAgent = createAgentClass(http.Agent, maxSockets)
52
+ const HttpsAgent = createAgentClass(https.Agent, maxSockets)
53
+
54
+ return {
55
+ httpAgent: new HttpAgent(),
56
+ httpsAgent: new HttpsAgent(),
57
+ }
58
+ }
59
+
60
+ const { httpAgent, httpsAgent } = createAgents(1)
40
61
 
41
62
  module.exports = {
42
- httpAgent: new HttpAgent(),
43
- httpsAgent: new HttpsAgent(),
63
+ createAgents,
64
+ httpAgent,
65
+ httpsAgent,
44
66
  }
@@ -25,10 +25,10 @@ class BufferingExporter {
25
25
  }
26
26
 
27
27
  _export (payload, writer = this._writer, timerKey = '_timer', deferImmediateFlush = false) {
28
- if (this._config.isCiVisibility) {
28
+ const appended = writer.append(payload)
29
+ if (this._config.isCiVisibility && appended !== false) {
29
30
  incrementCountMetric(TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION, {}, payload.length)
30
31
  }
31
- const appended = writer.append(payload)
32
32
 
33
33
  const { flushInterval } = this._config
34
34
 
@@ -0,0 +1,191 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @typedef {Record<string, unknown> & { deadline?: number, signal?: AbortSignal }} RequestOptions
5
+ */
6
+
7
+ /**
8
+ * @typedef {object} FinalFlush
9
+ * @property {number} deadline
10
+ * @property {(error?: Error) => void} done
11
+ * @property {Error | undefined} error
12
+ * @property {Set<PendingRequest>} requests
13
+ * @property {ReturnType<typeof setTimeout> | undefined} timeoutId
14
+ * @property {boolean} writerDone
15
+ */
16
+
17
+ /**
18
+ * @typedef {object} PendingRequest
19
+ * @property {AbortController} controller
20
+ * @property {Set<FinalFlush>} finalFlushes
21
+ * @property {RequestOptions} options
22
+ */
23
+
24
+ class FinalFlushRequestTracker {
25
+ #flush
26
+ #createTimeoutError
27
+ /** @type {Set<PendingRequest>} */
28
+ #pendingRequests = new Set()
29
+ /** @type {Set<FinalFlush>} */
30
+ #finalFlushes = new Set()
31
+ /** @type {FinalFlush | undefined} */
32
+ #activeFinalFlush
33
+
34
+ /**
35
+ * @param {(done?: (error?: Error) => void, options?: { deadline?: number }) => void} flush
36
+ * @param {() => Error} createTimeoutError
37
+ */
38
+ constructor (flush, createTimeoutError) {
39
+ this.#flush = flush
40
+ this.#createTimeoutError = createTimeoutError
41
+ }
42
+
43
+ /**
44
+ * Flushes queued payloads and waits for requests that were already in flight.
45
+ *
46
+ * @param {(error?: Error) => void} [done]
47
+ * @param {{ deadline?: number }} [options]
48
+ * @returns {void}
49
+ */
50
+ flush (done, options) {
51
+ if (options?.deadline === undefined) {
52
+ this.#flush(done, options)
53
+ return
54
+ }
55
+
56
+ /** @type {FinalFlush} */
57
+ const finalFlush = {
58
+ deadline: options.deadline,
59
+ done: done || (() => {}),
60
+ error: undefined,
61
+ requests: new Set(),
62
+ timeoutId: undefined,
63
+ writerDone: false,
64
+ }
65
+ this.#finalFlushes.add(finalFlush)
66
+
67
+ for (const pendingRequest of this.#pendingRequests) {
68
+ this.#attachRequest(finalFlush, pendingRequest)
69
+ }
70
+
71
+ const remaining = Math.max(0, options.deadline - Date.now())
72
+ finalFlush.timeoutId = setTimeout(() => {
73
+ const error = this.#createTimeoutError()
74
+
75
+ finalFlush.error ||= error
76
+ finalFlush.writerDone = true
77
+
78
+ for (const pendingRequest of finalFlush.requests) {
79
+ pendingRequest.finalFlushes.delete(finalFlush)
80
+ if (pendingRequest.finalFlushes.size === 0) {
81
+ pendingRequest.controller.abort(error)
82
+ this.#pendingRequests.delete(pendingRequest)
83
+ } else {
84
+ this.#updateRequestDeadline(pendingRequest)
85
+ }
86
+ }
87
+ finalFlush.requests.clear()
88
+ this.#finishFinalFlush(finalFlush)
89
+ }, remaining)
90
+
91
+ const previousFinalFlush = this.#activeFinalFlush
92
+ this.#activeFinalFlush = finalFlush
93
+ try {
94
+ this.#flush((error) => {
95
+ finalFlush.error ||= error
96
+ finalFlush.writerDone = true
97
+ this.#finishFinalFlush(finalFlush)
98
+ }, options)
99
+ } finally {
100
+ this.#activeFinalFlush = previousFinalFlush
101
+ }
102
+ this.#finishFinalFlush(finalFlush)
103
+ }
104
+
105
+ /**
106
+ * Sends and tracks a request so a later final flush can wait for or abort it.
107
+ *
108
+ * @param {(data: Buffer|string|object, options: RequestOptions,
109
+ * callback: (error: Error|null, result?: string|null, statusCode?: number,
110
+ * headers?: import('node:http').IncomingHttpHeaders) => void) => void} request
111
+ * @param {Buffer|string|object} data
112
+ * @param {RequestOptions} options
113
+ * @param {(error: Error|null, result?: string|null, statusCode?: number,
114
+ * headers?: import('node:http').IncomingHttpHeaders) => void} callback
115
+ * @returns {void}
116
+ */
117
+ send (request, data, options, callback) {
118
+ const controller = new AbortController()
119
+ const requestOptions = { ...options, signal: controller.signal }
120
+ const pendingRequest = { controller, finalFlushes: new Set(), options: requestOptions }
121
+ this.#pendingRequests.add(pendingRequest)
122
+ if (this.#activeFinalFlush) this.#attachRequest(this.#activeFinalFlush, pendingRequest)
123
+
124
+ try {
125
+ request(data, requestOptions, (error, result, statusCode, headers) => {
126
+ if (error) {
127
+ for (const finalFlush of pendingRequest.finalFlushes) finalFlush.error ||= error
128
+ }
129
+
130
+ try {
131
+ callback(error, result, statusCode, headers)
132
+ } finally {
133
+ this.#dropRequest(pendingRequest)
134
+ }
135
+ })
136
+ } catch (error) {
137
+ this.#dropRequest(pendingRequest)
138
+ throw error
139
+ }
140
+ }
141
+
142
+ /**
143
+ * @param {PendingRequest} pendingRequest
144
+ * @returns {void}
145
+ */
146
+ #dropRequest (pendingRequest) {
147
+ this.#pendingRequests.delete(pendingRequest)
148
+ for (const finalFlush of pendingRequest.finalFlushes) {
149
+ finalFlush.requests.delete(pendingRequest)
150
+ this.#finishFinalFlush(finalFlush)
151
+ }
152
+ pendingRequest.finalFlushes.clear()
153
+ }
154
+
155
+ /**
156
+ * @param {FinalFlush} finalFlush
157
+ * @param {PendingRequest} pendingRequest
158
+ * @returns {void}
159
+ */
160
+ #attachRequest (finalFlush, pendingRequest) {
161
+ finalFlush.requests.add(pendingRequest)
162
+ pendingRequest.finalFlushes.add(finalFlush)
163
+ this.#updateRequestDeadline(pendingRequest)
164
+ }
165
+
166
+ /**
167
+ * @param {PendingRequest} pendingRequest
168
+ * @returns {void}
169
+ */
170
+ #updateRequestDeadline (pendingRequest) {
171
+ let deadline = 0
172
+ for (const finalFlush of pendingRequest.finalFlushes) {
173
+ deadline = Math.max(deadline, finalFlush.deadline)
174
+ }
175
+ pendingRequest.options.deadline = deadline
176
+ }
177
+
178
+ /**
179
+ * @param {FinalFlush} finalFlush
180
+ * @returns {void}
181
+ */
182
+ #finishFinalFlush (finalFlush) {
183
+ if (!this.#finalFlushes.has(finalFlush) || !finalFlush.writerDone || finalFlush.requests.size !== 0) return
184
+
185
+ this.#finalFlushes.delete(finalFlush)
186
+ clearTimeout(finalFlush.timeoutId)
187
+ finalFlush.done(finalFlush.error)
188
+ }
189
+ }
190
+
191
+ module.exports = FinalFlushRequestTracker
@@ -10,7 +10,7 @@ const zlib = require('zlib')
10
10
 
11
11
  const { storage } = require('../../../../datadog-core')
12
12
  const log = require('../../log')
13
- const { isLoopbackHost, parseUrl } = require('./url')
13
+ const { canSendApiKey, parseUrl } = require('./url')
14
14
  const docker = require('./docker')
15
15
  const { httpAgent, httpsAgent } = require('./agents')
16
16
  const {
@@ -30,7 +30,7 @@ let activeBufferSize = 0
30
30
  * @param {Buffer|string|Readable|Array<Buffer|string>} data
31
31
  * @param {object} options
32
32
  * @param {(error: Error|null, result?: string|null, statusCode?: number,
33
- * headers?: import('node:http').IncomingHttpHeaders) => void} callback
33
+ * headers?: import('node:http').IncomingHttpHeaders, dropped?: boolean) => void} callback
34
34
  */
35
35
  function request (data, options, callback) {
36
36
  if (!options.headers) {
@@ -54,7 +54,7 @@ function request (data, options, callback) {
54
54
  // rather than drop the request: the agent proxies telemetry with its own key, while an https
55
55
  // intake URL is required to authenticate agentless traffic.
56
56
  const hasApiKey = options.headers['dd-api-key'] !== undefined || options.headers['DD-API-KEY'] !== undefined
57
- if (hasApiKey && options.protocol === 'http:' && !isLoopbackHost(options.hostname)) {
57
+ if (hasApiKey && !canSendApiKey(options.protocol, options.hostname)) {
58
58
  log.error(
59
59
  'Not sending the Datadog API key over a non-TLS connection to %s. Configure an https intake URL.',
60
60
  options.hostname
@@ -89,7 +89,8 @@ function request (data, options, callback) {
89
89
  if (!Array.isArray(data)) {
90
90
  dataArray = [data]
91
91
  }
92
- options.headers['Content-Length'] = byteLength(dataArray)
92
+ const contentLength = byteLength(dataArray)
93
+ options.headers['Content-Length'] = contentLength
93
94
 
94
95
  docker.inject(options.headers)
95
96
 
@@ -172,20 +173,23 @@ function request (data, options, callback) {
172
173
  // outside AsyncContextFrame, so a synchronous re-entry would lose the store.
173
174
  /** @param {number} attemptIndex */
174
175
  const attempt = attemptIndex => {
175
- if (!request.writable) {
176
- log.debug('Maximum number of active requests reached: payload is discarded.')
177
- return callback(null)
176
+ if (activeBufferSize + contentLength > maxActiveBufferSize) {
177
+ const error = new log.NoTransmitError('Maximum active request buffer size reached: payload is discarded.')
178
+ error.code = 'ERR_DD_REQUEST_BUFFER_FULL'
179
+ log.debug(error.message)
180
+ return callback(error, undefined, undefined, undefined, true)
178
181
  }
179
182
 
180
- activeBufferSize += options.headers['Content-Length'] ?? 0
183
+ activeBufferSize += contentLength
181
184
 
182
185
  legacyStorage.run({ noop: true }, () => {
183
186
  let finished = false
184
187
  let settled = false
188
+ let timeoutImmediate
185
189
  const finalize = () => {
186
190
  if (finished) return
187
191
  finished = true
188
- activeBufferSize -= options.headers['Content-Length'] ?? 0
192
+ activeBufferSize -= contentLength
189
193
  }
190
194
 
191
195
  /**
@@ -197,6 +201,7 @@ function request (data, options, callback) {
197
201
  const complete = (error, result, statusCode, headers) => {
198
202
  if (settled) return
199
203
  settled = true
204
+ clearImmediate(timeoutImmediate)
200
205
  finalize()
201
206
  callback(error, result, statusCode, headers)
202
207
  }
@@ -206,6 +211,7 @@ function request (data, options, callback) {
206
211
  */
207
212
  const handleError = (error) => {
208
213
  if (settled) return
214
+ clearImmediate(timeoutImmediate)
209
215
 
210
216
  if (options.retry !== false &&
211
217
  attemptIndex < getMaxAttempts(options) &&
@@ -224,10 +230,11 @@ function request (data, options, callback) {
224
230
  const req = client.request(connectionOptions, (res) => onResponse(res, complete, handleError))
225
231
 
226
232
  req.once('close', finalize)
227
- req.once('timeout', finalize)
233
+ if (!options.deferTimeoutAbort) req.once('timeout', finalize)
228
234
  req.once('error', handleError)
229
235
 
230
- req.setTimeout(timeout, () => {
236
+ const abortRequest = () => {
237
+ if (settled) return
231
238
  try {
232
239
  if (typeof req.abort === 'function') {
233
240
  req.abort()
@@ -237,6 +244,19 @@ function request (data, options, callback) {
237
244
  } catch {
238
245
  // ignore
239
246
  }
247
+ }
248
+
249
+ req.setTimeout(timeout, () => {
250
+ if (!options.deferTimeoutAbort) {
251
+ abortRequest()
252
+ return
253
+ }
254
+
255
+ timeoutImmediate = setImmediate(() => {
256
+ abortRequest()
257
+ finalize()
258
+ })
259
+ if (!options.keepProcessAlive) timeoutImmediate.unref?.()
240
260
  })
241
261
 
242
262
  for (const buffer of dataArray) req.write(buffer)