dd-trace 6.13.0 → 6.15.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/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/vitest.js +3 -3
  8. package/ci/test-optimization-validation/package-check.js +1 -1
  9. package/ci/test-optimization-validation/report-writer.js +7 -5
  10. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  11. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  12. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  13. package/index.d.ts +8 -0
  14. package/package.json +13 -10
  15. package/packages/datadog-esbuild/index.js +3 -1
  16. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  17. package/packages/datadog-instrumentations/src/fs.js +24 -20
  18. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  19. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  20. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  21. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  22. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +14 -5
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  34. package/packages/datadog-instrumentations/src/jest.js +166 -45
  35. package/packages/datadog-instrumentations/src/knex.js +76 -0
  36. package/packages/datadog-instrumentations/src/mariadb.js +6 -0
  37. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  38. package/packages/datadog-instrumentations/src/mocha/main.js +66 -20
  39. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  40. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  41. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  42. package/packages/datadog-instrumentations/src/net.js +14 -17
  43. package/packages/datadog-instrumentations/src/next.js +212 -62
  44. package/packages/datadog-instrumentations/src/openai.js +42 -40
  45. package/packages/datadog-instrumentations/src/pg.js +15 -0
  46. package/packages/datadog-instrumentations/src/playwright.js +113 -13
  47. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  48. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  49. package/packages/datadog-instrumentations/src/undici.js +53 -4
  50. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
  51. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  52. package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
  53. package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
  54. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  55. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  56. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  57. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  58. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  59. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  60. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  61. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  62. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  63. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  64. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  65. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  66. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  67. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  68. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  69. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  70. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  71. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  72. package/packages/datadog-plugin-jest/src/index.js +0 -6
  73. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  74. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  75. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  76. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  77. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  78. package/packages/datadog-plugin-next/src/index.js +63 -29
  79. package/packages/datadog-plugin-playwright/src/index.js +167 -27
  80. package/packages/datadog-plugin-undici/src/index.js +27 -41
  81. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  82. package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
  83. package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
  84. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  85. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  86. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  87. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  88. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  89. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  90. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  91. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  92. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
  93. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -95
  94. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  95. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  96. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  97. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  100. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
  101. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  102. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  103. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  104. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
  105. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  106. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  107. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  108. package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
  109. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  110. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  111. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
  112. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  113. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +180 -0
  114. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  115. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  116. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  117. package/packages/dd-trace/src/config/index.js +73 -13
  118. package/packages/dd-trace/src/config/parsers.js +11 -1
  119. package/packages/dd-trace/src/config/stable.js +3 -3
  120. package/packages/dd-trace/src/config/supported-configurations.json +22 -2
  121. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  122. package/packages/dd-trace/src/debugger/config.js +12 -1
  123. package/packages/dd-trace/src/debugger/constants.js +1 -0
  124. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  125. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +28 -0
  126. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
  127. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  128. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  129. package/packages/dd-trace/src/debugger/index.js +23 -3
  130. package/packages/dd-trace/src/dogstatsd.js +510 -76
  131. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  132. package/packages/dd-trace/src/encode/0.4.js +5 -5
  133. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  134. package/packages/dd-trace/src/evp_proxy/direct.js +3 -25
  135. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  136. package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
  137. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  138. package/packages/dd-trace/src/exporters/agentless/writer.js +103 -119
  139. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  140. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  141. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  142. package/packages/dd-trace/src/exporters/common/proxy.js +52 -0
  143. package/packages/dd-trace/src/exporters/common/request.js +42 -14
  144. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  145. package/packages/dd-trace/src/exporters/common/writer.js +9 -7
  146. package/packages/dd-trace/src/flush.js +6 -3
  147. package/packages/dd-trace/src/id.js +14 -0
  148. package/packages/dd-trace/src/index.js +5 -2
  149. package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
  150. package/packages/dd-trace/src/llmobs/index.js +32 -32
  151. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  152. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  153. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  154. package/packages/dd-trace/src/llmobs/util.js +0 -33
  155. package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
  156. package/packages/dd-trace/src/log/index.js +7 -1
  157. package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
  158. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  159. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  160. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
  161. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  162. package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
  163. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
  164. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
  165. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +5 -0
  166. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  167. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  168. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  169. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  170. package/packages/dd-trace/src/opentracing/propagation/text_map.js +101 -20
  171. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  172. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  173. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  174. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  175. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  176. package/packages/dd-trace/src/plugins/util/test.js +83 -20
  177. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  178. package/packages/dd-trace/src/plugins/util/web.js +4 -9
  179. package/packages/dd-trace/src/priority_sampler.js +4 -0
  180. package/packages/dd-trace/src/profiling/config.js +1 -0
  181. package/packages/dd-trace/src/profiling/oom.js +7 -2
  182. package/packages/dd-trace/src/profiling/profiler.js +23 -13
  183. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  184. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  185. package/packages/dd-trace/src/proxy.js +75 -5
  186. package/packages/dd-trace/src/remote_config/index.js +319 -101
  187. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  188. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  189. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
  190. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
  191. package/packages/dd-trace/src/sampling_rule.js +39 -10
  192. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
  193. package/packages/dd-trace/src/serverless/vercel.js +53 -20
  194. package/packages/dd-trace/src/serverless.js +10 -3
  195. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  196. package/packages/dd-trace/src/span_processor.js +18 -3
  197. package/packages/dd-trace/src/span_stats.js +24 -28
  198. package/packages/dd-trace/src/startup-log.js +3 -2
  199. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  200. package/packages/dd-trace/src/telemetry/send-data.js +23 -20
  201. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  202. package/packages/dd-trace/src/tracer.js +4 -5
  203. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  204. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  205. package/vendor/dist/protobufjs/index.js +1 -1
  206. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  207. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  208. package/packages/dd-trace/src/encode/agentless-json.js +0 -210
@@ -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
@@ -0,0 +1,52 @@
1
+ 'use strict'
2
+
3
+ const { isIPv6 } = require('node:net')
4
+
5
+ let defaultAgentKey
6
+ let getProxyForUrl
7
+ let proxyAgents
8
+
9
+ /**
10
+ * Selects a proxy agent for an HTTPS target while preserving the direct agent's pool boundaries.
11
+ *
12
+ * @param {string|URL|object} url
13
+ * @param {import('node:http').Agent|false} [directAgent]
14
+ * @returns {import('node:http').Agent|false|undefined}
15
+ */
16
+ function getHttpsProxyAgent (url, directAgent) {
17
+ getProxyForUrl ??= require('../../../../../vendor/dist/proxy-from-env').getProxyForUrl
18
+
19
+ const host = typeof url === 'string' ? undefined : url.host ?? url.hostname
20
+ const isUnbracketedIPv6 = typeof host === 'string' && host.includes(':') && isIPv6(host)
21
+ const target = typeof url === 'string'
22
+ ? url
23
+ : { protocol: url.protocol, host: isUnbracketedIPv6 ? `[${host}]` : host, port: url.port }
24
+ const proxyUrl = getProxyForUrl(target)
25
+ if (!proxyUrl) return directAgent
26
+
27
+ directAgent ??= require('node:https').globalAgent
28
+
29
+ if (proxyAgents === undefined) {
30
+ defaultAgentKey = {}
31
+ proxyAgents = new WeakMap()
32
+ }
33
+ const cacheKey = directAgent || defaultAgentKey
34
+ let agents = proxyAgents.get(cacheKey)
35
+ if (agents === undefined) {
36
+ agents = new Map()
37
+ proxyAgents.set(cacheKey, agents)
38
+ }
39
+
40
+ let agent = agents.get(proxyUrl)
41
+ if (agent === undefined) {
42
+ const { HttpsProxyAgent } = require('../../../../../vendor/dist/https-proxy-agent')
43
+ const options = directAgent
44
+ ? { keepAlive: directAgent.keepAlive, maxSockets: directAgent.maxSockets }
45
+ : undefined
46
+ agent = new HttpsProxyAgent(proxyUrl, options)
47
+ agents.set(proxyUrl, agent)
48
+ }
49
+ return agent
50
+ }
51
+
52
+ module.exports = { getHttpsProxyAgent }
@@ -10,9 +10,10 @@ 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
+ const { getHttpsProxyAgent } = require('./proxy')
16
17
  const {
17
18
  getMaxAttempts,
18
19
  getRetryDelay,
@@ -30,7 +31,7 @@ let activeBufferSize = 0
30
31
  * @param {Buffer|string|Readable|Array<Buffer|string>} data
31
32
  * @param {object} options
32
33
  * @param {(error: Error|null, result?: string|null, statusCode?: number,
33
- * headers?: import('node:http').IncomingHttpHeaders) => void} callback
34
+ * headers?: import('node:http').IncomingHttpHeaders, dropped?: boolean) => void} callback
34
35
  */
35
36
  function request (data, options, callback) {
36
37
  if (!options.headers) {
@@ -54,7 +55,7 @@ function request (data, options, callback) {
54
55
  // rather than drop the request: the agent proxies telemetry with its own key, while an https
55
56
  // intake URL is required to authenticate agentless traffic.
56
57
  const hasApiKey = options.headers['dd-api-key'] !== undefined || options.headers['DD-API-KEY'] !== undefined
57
- if (hasApiKey && options.protocol === 'http:' && !isLoopbackHost(options.hostname)) {
58
+ if (hasApiKey && !canSendApiKey(options.protocol, options.hostname)) {
58
59
  log.error(
59
60
  'Not sending the Datadog API key over a non-TLS connection to %s. Configure an https intake URL.',
60
61
  options.hostname
@@ -89,15 +90,23 @@ function request (data, options, callback) {
89
90
  if (!Array.isArray(data)) {
90
91
  dataArray = [data]
91
92
  }
92
- options.headers['Content-Length'] = byteLength(dataArray)
93
+ const contentLength = byteLength(dataArray)
94
+ options.headers['Content-Length'] = contentLength
93
95
 
94
96
  docker.inject(options.headers)
95
97
 
96
- const connectionOptions = {
97
- ...options,
98
- agent: options.agent ?? (isSecure ? httpsAgent : httpAgent),
98
+ let agent = options.agent ?? (isSecure ? httpsAgent : httpAgent)
99
+ if (hasApiKey && isSecure) {
100
+ try {
101
+ agent = getHttpsProxyAgent(options, agent)
102
+ } catch (error) {
103
+ callback(error)
104
+ return
105
+ }
99
106
  }
100
107
 
108
+ const connectionOptions = { ...options, agent }
109
+
101
110
  /**
102
111
  * @param {import('node:http').IncomingMessage} res
103
112
  * @param {(error: Error|null, result?: string|null, statusCode?: number,
@@ -172,20 +181,23 @@ function request (data, options, callback) {
172
181
  // outside AsyncContextFrame, so a synchronous re-entry would lose the store.
173
182
  /** @param {number} attemptIndex */
174
183
  const attempt = attemptIndex => {
175
- if (!request.writable) {
176
- log.debug('Maximum number of active requests reached: payload is discarded.')
177
- return callback(null)
184
+ if (activeBufferSize + contentLength > maxActiveBufferSize) {
185
+ const error = new log.NoTransmitError('Maximum active request buffer size reached: payload is discarded.')
186
+ error.code = 'ERR_DD_REQUEST_BUFFER_FULL'
187
+ log.debug(error.message)
188
+ return callback(error, undefined, undefined, undefined, true)
178
189
  }
179
190
 
180
- activeBufferSize += options.headers['Content-Length'] ?? 0
191
+ activeBufferSize += contentLength
181
192
 
182
193
  legacyStorage.run({ noop: true }, () => {
183
194
  let finished = false
184
195
  let settled = false
196
+ let timeoutImmediate
185
197
  const finalize = () => {
186
198
  if (finished) return
187
199
  finished = true
188
- activeBufferSize -= options.headers['Content-Length'] ?? 0
200
+ activeBufferSize -= contentLength
189
201
  }
190
202
 
191
203
  /**
@@ -197,6 +209,7 @@ function request (data, options, callback) {
197
209
  const complete = (error, result, statusCode, headers) => {
198
210
  if (settled) return
199
211
  settled = true
212
+ clearImmediate(timeoutImmediate)
200
213
  finalize()
201
214
  callback(error, result, statusCode, headers)
202
215
  }
@@ -206,6 +219,7 @@ function request (data, options, callback) {
206
219
  */
207
220
  const handleError = (error) => {
208
221
  if (settled) return
222
+ clearImmediate(timeoutImmediate)
209
223
 
210
224
  if (options.retry !== false &&
211
225
  attemptIndex < getMaxAttempts(options) &&
@@ -224,10 +238,11 @@ function request (data, options, callback) {
224
238
  const req = client.request(connectionOptions, (res) => onResponse(res, complete, handleError))
225
239
 
226
240
  req.once('close', finalize)
227
- req.once('timeout', finalize)
241
+ if (!options.deferTimeoutAbort) req.once('timeout', finalize)
228
242
  req.once('error', handleError)
229
243
 
230
- req.setTimeout(timeout, () => {
244
+ const abortRequest = () => {
245
+ if (settled) return
231
246
  try {
232
247
  if (typeof req.abort === 'function') {
233
248
  req.abort()
@@ -237,6 +252,19 @@ function request (data, options, callback) {
237
252
  } catch {
238
253
  // ignore
239
254
  }
255
+ }
256
+
257
+ req.setTimeout(timeout, () => {
258
+ if (!options.deferTimeoutAbort) {
259
+ abortRequest()
260
+ return
261
+ }
262
+
263
+ timeoutImmediate = setImmediate(() => {
264
+ abortRequest()
265
+ finalize()
266
+ })
267
+ if (!options.keepProcessAlive) timeoutImmediate.unref?.()
240
268
  })
241
269
 
242
270
  for (const buffer of dataArray) req.write(buffer)
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const net = require('node:net')
4
+ const { format } = require('node:url')
4
5
 
5
6
  const { urlToHttpOptions } = require('./url-to-http-options-polyfill')
6
7
 
@@ -16,6 +17,44 @@ function isLoopbackHost (hostname) {
16
17
  (hostname.startsWith('127.') && net.isIPv4(hostname))
17
18
  }
18
19
 
20
+ /**
21
+ * @param {string|undefined} protocol
22
+ * @param {string|undefined} hostname
23
+ * @returns {boolean}
24
+ */
25
+ function canSendApiKey (protocol, hostname) {
26
+ return protocol === 'https:' || protocol === 'unix:' ||
27
+ typeof hostname === 'string' && isLoopbackHost(hostname)
28
+ }
29
+
30
+ /**
31
+ * @param {string} site
32
+ * @param {string} [intake]
33
+ * @returns {URL | undefined}
34
+ */
35
+ function createSiteUrl (site, intake) {
36
+ const hostname = `${intake === undefined ? '' : `${intake}.`}${site}`.toLowerCase()
37
+
38
+ try {
39
+ const url = new URL(format({
40
+ protocol: 'https:',
41
+ hostname,
42
+ }))
43
+ if (
44
+ url.hostname !== hostname ||
45
+ url.username ||
46
+ url.password ||
47
+ url.port ||
48
+ url.pathname !== '/' ||
49
+ url.search ||
50
+ url.hash
51
+ ) {
52
+ return
53
+ }
54
+ return url
55
+ } catch {}
56
+ }
57
+
19
58
  /**
20
59
  * Convert an agent/intake URL into Node http(s) request options.
21
60
  *
@@ -43,4 +82,4 @@ function parseUrl (urlObjOrString) {
43
82
  return url
44
83
  }
45
84
 
46
- module.exports = { isLoopbackHost, parseUrl }
85
+ module.exports = { canSendApiKey, createSiteUrl, isLoopbackHost, parseUrl }
@@ -8,13 +8,16 @@ const request = require('./request')
8
8
  const { safeJSONStringify } = require('./util')
9
9
 
10
10
  const firstFlushChannel = channel('dd-trace:exporter:first-flush')
11
+ const noop = () => {}
11
12
 
12
13
  class Writer {
13
14
  #deliveryTracker
15
+ #retainOnBackpressure
14
16
 
15
- constructor ({ url, beforeFirstFlush, deliveryTracker }) {
17
+ constructor ({ url, beforeFirstFlush, deliveryTracker, retainOnBackpressure = false }) {
16
18
  this._url = url
17
19
  this._beforeFirstFlush = beforeFirstFlush
20
+ this.#retainOnBackpressure = retainOnBackpressure
18
21
  this.#deliveryTracker = deliveryTracker
19
22
  }
20
23
 
@@ -39,10 +42,10 @@ class Writer {
39
42
  * @param {{ deadline?: number }} [options]
40
43
  * @returns {void}
41
44
  */
42
- flushDirect (done = () => {}, options) {
45
+ flushDirect (done = noop, options) {
43
46
  const count = this._encoder.count()
44
47
 
45
- if (!request.writable && options?.deadline === undefined) {
48
+ if (!request.writable && options?.deadline === undefined && !this.#retainOnBackpressure) {
46
49
  this._encoder.reset()
47
50
  done()
48
51
  } else if (count > 0) {
@@ -77,7 +80,7 @@ class Writer {
77
80
  }
78
81
 
79
82
  append (payload, options) {
80
- if (!request.writable && options?.deadline === undefined) {
83
+ if (!request.writable && options?.deadline === undefined && !this.#retainOnBackpressure) {
81
84
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
82
85
  log.debug(() => `Maximum number of active requests reached. Payload discarded: ${safeJSONStringify(payload)}`)
83
86
  return false
@@ -86,12 +89,11 @@ class Writer {
86
89
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
87
90
  log.debug(() => `Encoding payload: ${safeJSONStringify(payload)}`)
88
91
 
89
- this._encode(payload)
90
- return true
92
+ return this._encode(payload) !== false
91
93
  }
92
94
 
93
95
  _encode (payload) {
94
- this._encoder.encode(payload)
96
+ return this._encoder.encode(payload)
95
97
  }
96
98
 
97
99
  setUrl (url) {
@@ -43,12 +43,16 @@ function registerTelemetryFlusher (flusher, options) {
43
43
  * @param {{ timeout?: number }} [options]
44
44
  * @param {TraceFlushers} [traceFlushers]
45
45
  */
46
- function flushServerlessTelemetry (done, options, traceFlushers = {}) {
47
- const { trace: traceFlusher, spanStats: spanStatsFlusher } = traceFlushers
46
+ function flushServerlessTelemetry (done, options, traceFlushers) {
47
+ const traceFlusher = traceFlushers?.trace
48
+ const spanStatsFlusher = traceFlushers?.spanStats
48
49
  // TODO: Include DSM after DataStreamsProcessor exposes a completion-aware flush API.
49
50
  let pending = telemetryFlushers.size + postTraceTelemetryFlushers.size +
50
51
  (typeof traceFlusher === 'function' ? 1 : 0) +
51
52
  (typeof spanStatsFlusher === 'function' ? 1 : 0)
53
+
54
+ if (pending === 0) return done?.()
55
+
52
56
  let completed = false
53
57
  let timeout
54
58
 
@@ -62,7 +66,6 @@ function flushServerlessTelemetry (done, options, traceFlushers = {}) {
62
66
  if (--pending === 0) finish()
63
67
  }
64
68
 
65
- if (pending === 0) return finish()
66
69
  if (options?.timeout) {
67
70
  timeout = setTimeout(() => {
68
71
  log.warn('Timed out waiting for telemetry flush after %dms', options.timeout)
@@ -2,6 +2,8 @@
2
2
 
3
3
  const { randomFillSync } = require('crypto')
4
4
 
5
+ const { channel } = require('dc-polyfill')
6
+
5
7
  const UINT_MAX = 4_294_967_296
6
8
 
7
9
  const data = new Uint8Array(8 * 8192)
@@ -9,6 +11,8 @@ const zeroId = new Uint8Array(8)
9
11
 
10
12
  let batch = 0
11
13
 
14
+ channel('datadog:identity:update').subscribe(reseed)
15
+
12
16
  // Internal representation of a trace or span ID.
13
17
  class Identifier {
14
18
  /** @type {number[] | Uint8Array} */
@@ -254,6 +258,16 @@ function writeUInt32BE (buffer, value, offset) {
254
258
  buffer[0 + offset] = value & 255
255
259
  }
256
260
 
261
+ /**
262
+ * Resets the batch cursor, forcing the next ID batch to draw a fresh
263
+ * randomFillSync() call on MicroVM clone resume. Node's crypto RNG is
264
+ * re-seeded from the kernel CSPRNG on snapshot resume, so re-invoking it
265
+ * is sufficient — no need to read /dev/urandom directly.
266
+ */
267
+ function reseed () {
268
+ batch = 0
269
+ }
270
+
257
271
  /**
258
272
  * @param {string} [value]
259
273
  * @param {number} [radix]
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('./config/helper')
3
+ const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
4
+ const { isTrue } = require('./util')
4
5
 
5
6
  // Global `jest` is only present in Jest workers.
6
7
  const inJestWorker = typeof jest !== 'undefined'
@@ -8,8 +9,10 @@ const inJestWorker = typeof jest !== 'undefined'
8
9
  // skipDefault: distinguish an unset DD_TRACE_ENABLED (fall back to the OTel signal) from an
9
10
  // explicit value; the registered default would otherwise mask the OTEL_TRACES_EXPORTER check.
10
11
  const ddTraceEnabled = getValueFromEnvSources('DD_TRACE_ENABLED', true)
12
+ const agentlessTracingEnabled = getValueFromEnvSources('DD_AGENTLESS_ENABLED') ||
13
+ isTrue(getEnvironmentVariable('_DD_APM_TRACING_AGENTLESS_ENABLED'))
11
14
  const ddTraceDisabled = ddTraceEnabled === undefined
12
- ? getValueFromEnvSources('OTEL_TRACES_EXPORTER') === 'none'
15
+ ? !agentlessTracingEnabled && getValueFromEnvSources('OTEL_TRACES_EXPORTER') === 'none'
13
16
  : ddTraceEnabled === false
14
17
 
15
18
  module.exports = ddTraceDisabled || inJestWorker
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { randomUUID } = require('node:crypto')
4
- const snapshotPayload = require('../../../../../vendor/dist/rfdc')({ proto: false, circles: false })
4
+ const createRfdc = require('../../../../../vendor/dist/rfdc')
5
+ const snapshotPayload = createRfdc({ proto: false, circles: false })
5
6
 
6
7
  /** @typedef {{add?: string[], remove?: string[], replace?: string[]}} TagOperations */
7
8
  /**