dd-trace 6.16.0 → 6.18.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 (356) hide show
  1. package/ci/diagnose.js +0 -23
  2. package/ci/test-optimization-validation/approval-artifacts.js +0 -3
  3. package/ci/test-optimization-validation/approval.js +0 -6
  4. package/ci/test-optimization-validation/artifact-id.js +0 -1
  5. package/ci/test-optimization-validation/bounded-json.js +0 -1
  6. package/ci/test-optimization-validation/cli.js +0 -12
  7. package/ci/test-optimization-validation/command-output-policy.js +0 -1
  8. package/ci/test-optimization-validation/command-runner.js +0 -3
  9. package/ci/test-optimization-validation/environment.js +0 -4
  10. package/ci/test-optimization-validation/executable-approval.js +0 -1
  11. package/ci/test-optimization-validation/executable.js +0 -5
  12. package/ci/test-optimization-validation/framework-adapters/cucumber.js +0 -6
  13. package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
  14. package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
  15. package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
  16. package/ci/test-optimization-validation/generated-test-contract.js +0 -5
  17. package/ci/test-optimization-validation/generated-verifier.js +0 -1
  18. package/ci/test-optimization-validation/literal-environment.js +0 -1
  19. package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
  20. package/ci/test-optimization-validation/manifest-schema.js +0 -21
  21. package/ci/test-optimization-validation/offline-fixtures.js +0 -4
  22. package/ci/test-optimization-validation/plan-writer.js +0 -11
  23. package/ci/test-optimization-validation/preflight-runner.js +0 -1
  24. package/ci/test-optimization-validation/redaction.js +0 -4
  25. package/ci/test-optimization-validation/report-writer.js +0 -16
  26. package/ci/test-optimization-validation/runner-command.js +0 -3
  27. package/ci/test-optimization-validation/runner-contract.js +0 -4
  28. package/ci/test-optimization-validation/safe-files.js +0 -1
  29. package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
  30. package/ci/test-optimization-validation/scenarios/ci-wiring.js +0 -1
  31. package/ci/test-optimization-validation/source-text.js +0 -3
  32. package/ci/vitest-no-worker-init-setup.mjs +41 -16
  33. package/index.d.ts +208 -0
  34. package/package.json +17 -16
  35. package/packages/datadog-esbuild/src/utils.js +0 -2
  36. package/packages/datadog-instrumentations/src/aerospike.js +28 -6
  37. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -2
  38. package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
  39. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +0 -2
  40. package/packages/datadog-instrumentations/src/console.js +97 -0
  41. package/packages/datadog-instrumentations/src/cucumber.js +77 -12
  42. package/packages/datadog-instrumentations/src/cypress-config.js +27 -38
  43. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +6 -0
  44. package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
  45. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -1
  46. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
  47. package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -6
  48. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +0 -3
  49. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
  50. package/packages/datadog-instrumentations/src/helpers/register.js +24 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +111 -26
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +41 -0
  53. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -17
  54. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +55 -0
  55. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
  56. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
  57. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +13 -0
  58. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +28 -3
  59. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +20 -0
  60. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
  61. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +0 -5
  62. package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
  63. package/packages/datadog-instrumentations/src/jest.js +165 -51
  64. package/packages/datadog-instrumentations/src/knex.js +0 -1
  65. package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
  66. package/packages/datadog-instrumentations/src/mariadb.js +0 -2
  67. package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
  68. package/packages/datadog-instrumentations/src/mocha/main.js +25 -26
  69. package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
  70. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
  71. package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
  72. package/packages/datadog-instrumentations/src/mysql2.js +0 -2
  73. package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
  74. package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
  75. package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
  76. package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
  77. package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
  78. package/packages/datadog-instrumentations/src/openai.js +51 -12
  79. package/packages/datadog-instrumentations/src/oracledb.js +9 -3
  80. package/packages/datadog-instrumentations/src/playwright-reporter.js +6 -20
  81. package/packages/datadog-instrumentations/src/playwright.js +280 -56
  82. package/packages/datadog-instrumentations/src/{azure-cosmos.js → postgres.js} +1 -1
  83. package/packages/datadog-instrumentations/src/router.js +0 -2
  84. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
  85. package/packages/datadog-instrumentations/src/sequelize.js +0 -1
  86. package/packages/datadog-instrumentations/src/supabase.js +15 -0
  87. package/packages/datadog-instrumentations/src/undici.js +7 -3
  88. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
  89. package/packages/datadog-instrumentations/src/vitest-main.js +121 -30
  90. package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
  91. package/packages/datadog-instrumentations/src/vitest-worker.js +49 -13
  92. package/packages/datadog-instrumentations/src/webdriverio.js +44 -47
  93. package/packages/datadog-instrumentations/src/ws.js +41 -2
  94. package/packages/datadog-plugin-ai/src/utils.js +0 -2
  95. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
  96. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
  97. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  98. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +0 -1
  99. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
  100. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
  101. package/packages/datadog-plugin-azure-durable-functions/src/index.js +0 -1
  102. package/packages/datadog-plugin-cucumber/src/index.js +9 -0
  103. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +211 -85
  104. package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
  105. package/packages/datadog-plugin-cypress/src/index.js +3 -1
  106. package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
  107. package/packages/datadog-plugin-cypress/src/support.js +52 -7
  108. package/packages/datadog-plugin-graphql/src/resolve-error.js +1 -2
  109. package/packages/datadog-plugin-graphql/src/utils.js +0 -5
  110. package/packages/datadog-plugin-jest/src/index.js +12 -0
  111. package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
  112. package/packages/datadog-plugin-mocha/src/index.js +38 -11
  113. package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
  114. package/packages/datadog-plugin-openai/src/index.js +16 -1
  115. package/packages/datadog-plugin-openai/src/realtime.js +144 -0
  116. package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
  117. package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
  118. package/packages/datadog-plugin-playwright/src/index.js +81 -9
  119. package/packages/datadog-plugin-postgres/src/index.js +167 -0
  120. package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
  121. package/packages/datadog-plugin-redis/src/index.js +15 -1
  122. package/packages/datadog-plugin-supabase/src/error.js +18 -0
  123. package/packages/datadog-plugin-supabase/src/index.js +21 -0
  124. package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
  125. package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
  126. package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
  127. package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
  128. package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
  129. package/packages/datadog-plugin-supabase/src/url.js +17 -0
  130. package/packages/datadog-plugin-vitest/src/index.js +9 -0
  131. package/packages/datadog-plugin-ws/src/producer.js +1 -1
  132. package/packages/datadog-plugin-ws/src/util.js +0 -4
  133. package/packages/datadog-shimmer/src/shimmer.js +14 -10
  134. package/packages/datadog-turbopack/index.js +4 -7
  135. package/packages/datadog-turbopack/src/loader.js +6 -18
  136. package/packages/datadog-webpack/src/loader.js +0 -1
  137. package/packages/dd-trace/src/agent/info.js +5 -4
  138. package/packages/dd-trace/src/aiguard/client.js +2 -3
  139. package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
  140. package/packages/dd-trace/src/aiguard/index.js +2 -2
  141. package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
  142. package/packages/dd-trace/src/aiguard/integrations/openai.js +81 -7
  143. package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
  144. package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
  145. package/packages/dd-trace/src/aiguard/messages/utils.js +0 -1
  146. package/packages/dd-trace/src/aiguard/redaction.js +0 -1
  147. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  148. package/packages/dd-trace/src/appsec/activation.js +1 -1
  149. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
  150. package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
  151. package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
  152. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
  153. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
  155. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  157. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
  158. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
  159. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
  160. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
  161. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
  162. package/packages/dd-trace/src/appsec/index.js +2 -2
  163. package/packages/dd-trace/src/appsec/lambda.js +71 -2
  164. package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
  165. package/packages/dd-trace/src/appsec/remote_config.js +8 -6
  166. package/packages/dd-trace/src/appsec/reporter.js +10 -6
  167. package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
  168. package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
  169. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  170. package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
  171. package/packages/dd-trace/src/carrier.js +0 -5
  172. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
  173. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
  174. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
  175. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
  176. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
  177. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
  178. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
  179. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
  180. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
  181. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
  182. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
  183. package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
  184. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
  185. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
  186. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
  187. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
  188. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
  189. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
  190. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
  191. package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
  192. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
  193. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
  194. package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
  195. package/packages/dd-trace/src/config/defaults.js +3 -2
  196. package/packages/dd-trace/src/config/generated-config-types.d.ts +88 -82
  197. package/packages/dd-trace/src/config/helper.js +0 -1
  198. package/packages/dd-trace/src/config/index.js +31 -30
  199. package/packages/dd-trace/src/config/parsers.js +34 -6
  200. package/packages/dd-trace/src/config/remote_config.js +25 -109
  201. package/packages/dd-trace/src/config/supported-configurations.json +150 -10
  202. package/packages/dd-trace/src/datastreams/encoding.js +0 -1
  203. package/packages/dd-trace/src/datastreams/pathway.js +0 -1
  204. package/packages/dd-trace/src/debugger/constants.js +10 -0
  205. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +62 -10
  206. package/packages/dd-trace/src/debugger/devtools_client/config.js +4 -1
  207. package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
  208. package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
  209. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
  210. package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
  211. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +21 -8
  212. package/packages/dd-trace/src/debugger/devtools_client/send.js +41 -15
  213. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
  214. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
  215. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +5 -3
  216. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
  217. package/packages/dd-trace/src/debugger/devtools_client/status.js +21 -7
  218. package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
  219. package/packages/dd-trace/src/debugger/index.js +51 -3
  220. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
  221. package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
  222. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
  223. package/packages/dd-trace/src/dogstatsd.js +0 -23
  224. package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
  225. package/packages/dd-trace/src/encode/0.4.js +0 -7
  226. package/packages/dd-trace/src/encode/0.5.js +0 -2
  227. package/packages/dd-trace/src/encode/tags-processors.js +0 -1
  228. package/packages/dd-trace/src/evp_proxy/constants.js +2 -0
  229. package/packages/dd-trace/src/evp_proxy/direct.js +17 -13
  230. package/packages/dd-trace/src/evp_proxy/discovery.js +14 -11
  231. package/packages/dd-trace/src/evp_proxy/path.js +21 -9
  232. package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
  233. package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
  234. package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
  235. package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
  236. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
  237. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  238. package/packages/dd-trace/src/exporters/common/url.js +33 -4
  239. package/packages/dd-trace/src/exporters/common/writer.js +0 -2
  240. package/packages/dd-trace/src/id.js +0 -11
  241. package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
  242. package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
  243. package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
  244. package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
  245. package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
  246. package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
  247. package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
  248. package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
  249. package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
  250. package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
  251. package/packages/dd-trace/src/llmobs/index.js +1 -3
  252. package/packages/dd-trace/src/llmobs/noop.js +10 -0
  253. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
  254. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
  255. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
  256. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
  257. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
  258. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
  259. package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
  260. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
  261. package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
  262. package/packages/dd-trace/src/llmobs/prompts/manager.js +647 -0
  263. package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
  264. package/packages/dd-trace/src/llmobs/prompts/prompt.js +158 -0
  265. package/packages/dd-trace/src/llmobs/sdk.js +21 -6
  266. package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
  267. package/packages/dd-trace/src/llmobs/tagger.js +39 -11
  268. package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
  269. package/packages/dd-trace/src/llmobs/util.js +0 -34
  270. package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
  271. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  272. package/packages/dd-trace/src/log/channels.js +3 -0
  273. package/packages/dd-trace/src/log/index.js +23 -5
  274. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
  275. package/packages/dd-trace/src/msgpack/chunk.js +0 -1
  276. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
  277. package/packages/dd-trace/src/openfeature/configuration_source.js +4 -1
  278. package/packages/dd-trace/src/openfeature/encoding.js +0 -2
  279. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
  280. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -3
  281. package/packages/dd-trace/src/openfeature/index.js +6 -6
  282. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
  283. package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
  284. package/packages/dd-trace/src/openfeature/writers/base.js +90 -23
  285. package/packages/dd-trace/src/openfeature/writers/exposures.js +4 -5
  286. package/packages/dd-trace/src/openfeature/writers/util.js +104 -37
  287. package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
  288. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
  289. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
  290. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
  291. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +0 -1
  292. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
  293. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
  294. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -14
  295. package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
  296. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +30 -13
  297. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
  298. package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
  299. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
  300. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
  301. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
  302. package/packages/dd-trace/src/opentracing/span_context.js +0 -1
  303. package/packages/dd-trace/src/opentracing/tracer.js +3 -3
  304. package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
  305. package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
  306. package/packages/dd-trace/src/plugin_manager.js +27 -8
  307. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -2
  308. package/packages/dd-trace/src/plugins/database.js +2 -6
  309. package/packages/dd-trace/src/plugins/index.js +7 -0
  310. package/packages/dd-trace/src/plugins/plugin.js +0 -4
  311. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  312. package/packages/dd-trace/src/plugins/util/git.js +1 -3
  313. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
  314. package/packages/dd-trace/src/plugins/util/llm.js +0 -2
  315. package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
  316. package/packages/dd-trace/src/plugins/util/test.js +26 -20
  317. package/packages/dd-trace/src/plugins/util/url.js +0 -5
  318. package/packages/dd-trace/src/priority_sampler.js +0 -6
  319. package/packages/dd-trace/src/process-tags/index.js +0 -1
  320. package/packages/dd-trace/src/profiler.js +132 -35
  321. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
  322. package/packages/dd-trace/src/profiling/profiler.js +101 -41
  323. package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
  324. package/packages/dd-trace/src/propagation-hash/index.js +0 -1
  325. package/packages/dd-trace/src/proxy.js +23 -47
  326. package/packages/dd-trace/src/random_sampler.js +0 -4
  327. package/packages/dd-trace/src/rate_limiter.js +0 -4
  328. package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
  329. package/packages/dd-trace/src/remote_config/index.js +0 -1
  330. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
  331. package/packages/dd-trace/src/sampler.js +0 -4
  332. package/packages/dd-trace/src/sampling_rule.js +0 -7
  333. package/packages/dd-trace/src/serverless/vercel.js +0 -1
  334. package/packages/dd-trace/src/serverless.js +0 -1
  335. package/packages/dd-trace/src/service-naming/index.js +0 -1
  336. package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
  337. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
  338. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
  339. package/packages/dd-trace/src/span_processor.js +2 -2
  340. package/packages/dd-trace/src/span_sampler.js +0 -1
  341. package/packages/dd-trace/src/standalone/product.js +2 -2
  342. package/packages/dd-trace/src/standalone/tracesource.js +0 -1
  343. package/packages/dd-trace/src/startup-log.js +1 -1
  344. package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
  345. package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
  346. package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
  347. package/packages/dd-trace/src/telemetry/metrics.js +0 -2
  348. package/packages/dd-trace/src/telemetry/send-data.js +2 -1
  349. package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
  350. package/packages/dd-trace/src/tracer.js +5 -4
  351. package/packages/dd-trace/src/util.js +13 -1
  352. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  353. package/packages/datadog-instrumentations/src/bullmq.js +0 -11
  354. package/packages/datadog-instrumentations/src/langchain.js +0 -7
  355. package/packages/datadog-instrumentations/src/langgraph.js +0 -7
  356. package/packages/datadog-instrumentations/src/mercurius.js +0 -11
@@ -57,6 +57,7 @@ module.exports = {
57
57
  addBreakpoint: lock(addBreakpoint),
58
58
  removeBreakpoint: lock(removeBreakpoint),
59
59
  modifyBreakpoint: lock(modifyBreakpoint),
60
+ refreshBreakpoints: lock(refreshBreakpoints),
60
61
  }
61
62
 
62
63
  async function addBreakpoint (probe) {
@@ -241,23 +242,77 @@ async function modifyBreakpoint (probe) {
241
242
  await addBreakpoint(probe)
242
243
  }
243
244
 
245
+ /**
246
+ * Rebuild the breakpoint conditions at the locations of the given probes from the current state of the probes
247
+ * attached to them.
248
+ *
249
+ * A breakpoint condition bakes in whether each probe produces snapshots, which decides if a hit counts against the
250
+ * global snapshot rate limit and how a skipped hit is classified. That changes when the pause handler permanently
251
+ * disables capture for a probe after a fatal capture error, so the conditions have to be recompiled.
252
+ *
253
+ * Probes sharing a location share a breakpoint, so each location is only refreshed once. A probe that has been removed
254
+ * in the meantime is ignored: its location no longer needs the update.
255
+ *
256
+ * The locations are independent, so one that fails does not stop the others from being refreshed. Any failure is
257
+ * thrown once every location has been attempted.
258
+ *
259
+ * @param {{ id: string }[]} probes - Probes attached to the breakpoints to refresh.
260
+ * @returns {Promise<void>}
261
+ */
262
+ async function refreshBreakpoints (probes) {
263
+ if (!sessionStarted) return
264
+
265
+ // Breakpoints set next to each other can snap to the same logical location and be hit at the same time, so the
266
+ // probes can be spread over more than one breakpoint.
267
+ const locationKeys = new Set()
268
+ for (const { id } of probes) {
269
+ const locationKey = probeToLocation.get(id)
270
+ if (locationKey !== undefined) locationKeys.add(locationKey)
271
+ }
272
+
273
+ /** @type {Error[] | undefined} */
274
+ let errors
275
+ for (const locationKey of locationKeys) {
276
+ try {
277
+ // eslint-disable-next-line no-await-in-loop
278
+ await updateBreakpointInternal(locationToBreakpoint.get(locationKey))
279
+ } catch (err) {
280
+ // Keep going: the remaining locations would otherwise hold on to the condition this refresh exists to replace
281
+ errors ??= []
282
+ errors.push(err)
283
+ }
284
+ }
285
+
286
+ if (errors !== undefined) {
287
+ throw errors.length === 1 ? errors[0] : new AggregateError(errors, 'Error refreshing breakpoints')
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Replace the breakpoint at a location with one whose condition matches the probes currently attached to it.
293
+ *
294
+ * @param {{ id: string, location: object, locationKey: string }} breakpoint - The breakpoint to replace.
295
+ * @param {object} [probe] - A probe to attach to the breakpoint first, when one is being added.
296
+ * @returns {Promise<void>}
297
+ */
244
298
  async function updateBreakpointInternal (breakpoint, probe) {
245
299
  const probesAtLocation = breakpointToProbes.get(breakpoint.id)
246
300
 
247
- // If a probe is provided, add it to the breakpoint. If not, it's because we're removing a probe. In both cases the
248
- // breakpoint condition must be rebuilt to match the remaining probes at the location.
301
+ // If a probe is provided, add it to the breakpoint. If not, it's because we're removing a probe or the probes at the
302
+ // location changed. In all cases the breakpoint condition must be rebuilt to match the probes at the location.
303
+ let context // identifies the update in the error messages below
249
304
  if (probe) {
250
305
  probesAtLocation.set(probe.id, probe)
251
306
  probeToLocation.set(probe.id, breakpoint.locationKey)
307
+ context = `while adding probe ${probe.id} (version: ${probe.version})`
308
+ } else {
309
+ context = `at ${breakpoint.locationKey}`
252
310
  }
253
311
 
254
312
  try {
255
313
  await session.post('Debugger.removeBreakpoint', { breakpointId: breakpoint.id })
256
314
  } catch (err) {
257
- const message = probe
258
- ? `Error replacing breakpoint while adding probe ${probe.id} (version: ${probe.version})`
259
- : `Error replacing breakpoint after removing probe from ${breakpoint.locationKey}`
260
- throw new Error(message, { cause: err })
315
+ throw new Error(`Error replacing breakpoint ${context}`, { cause: err })
261
316
  }
262
317
  breakpointToProbes.delete(breakpoint.id)
263
318
  let result
@@ -267,10 +322,7 @@ async function updateBreakpointInternal (breakpoint, probe) {
267
322
  condition: compileBreakpointCondition([...probesAtLocation.values()]),
268
323
  }))
269
324
  } catch (err) {
270
- const message = probe
271
- ? `Error setting breakpoint while adding probe ${probe.id} (version: ${probe.version})`
272
- : `Error setting breakpoint after removing probe from ${breakpoint.locationKey}`
273
- throw new Error(message, { cause: err })
325
+ throw new Error(`Error setting breakpoint ${context}`, { cause: err })
274
326
  }
275
327
  breakpoint.id = result.breakpointId
276
328
  breakpointToProbes.set(result.breakpointId, probesAtLocation)
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { workerData: { config: parentConfig, parentThreadId, configPort } } = require('node:worker_threads')
4
4
  const processTags = require('../../process-tags')
5
+ const { DEFAULT_QUEUE_MAX_BYTES } = require('../constants')
5
6
  const log = require('./log')
6
7
 
7
8
  processTags.initialize()
@@ -10,6 +11,7 @@ const config = module.exports = {
10
11
  ...parentConfig,
11
12
  parentThreadId,
12
13
  maxTotalPayloadSize: 5 * 1024 * 1024, // 5MB
14
+ queueMaxBytes: DEFAULT_QUEUE_MAX_BYTES,
13
15
  }
14
16
 
15
17
  updateConfig(parentConfig)
@@ -23,5 +25,6 @@ function updateConfig (updates) {
23
25
  // The worker receives a serialized config (see ../config.js) where `url` is a string, so it is
24
26
  // reconstructed into a URL here rather than read directly off a Config instance.
25
27
  config.url = new URL(updates.url)
26
- config.dynamicInstrumentation.captureTimeoutNs = BigInt(updates.dynamicInstrumentation.captureTimeoutMs) * 1_000_000n
28
+ config.dynamicInstrumentation.captureTimeoutNs =
29
+ BigInt(updates.dynamicInstrumentation.DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS) * 1_000_000n
27
30
  }
@@ -0,0 +1,15 @@
1
+ 'use strict'
2
+
3
+ const { workerData } = require('node:worker_threads')
4
+
5
+ const { GuardrailMetrics } = require('../guardrail-metrics')
6
+
7
+ // For testing purposes, we allow `workerData` to be undefined and fallback to counters that are never drained
8
+ const buffer = workerData?.guardrailMetricsBuffer ?? GuardrailMetrics.createBuffer()
9
+
10
+ /**
11
+ * The worker's view of the guardrail counters shared with the main thread, which drains them into telemetry.
12
+ *
13
+ * @type {GuardrailMetrics}
14
+ */
15
+ module.exports = new GuardrailMetrics(buffer)
@@ -1,17 +1,21 @@
1
1
  'use strict'
2
2
 
3
3
  const { randomUUID } = require('crypto')
4
- const { workerData: { probeSamplerBuffer } } = require('worker_threads')
4
+ const { parentPort, workerData: { probeSamplerBuffer } } = require('worker_threads')
5
5
  const { version } = require('../../../../../package.json')
6
6
  const processTags = require('../../process-tags')
7
7
  const { INSPECT_SEGMENT_GLOBAL_PROPERTY } = require('../constants')
8
+ const { EVENT_TYPE, INCOMPLETE_REASON } = require('../guardrail-metrics')
8
9
  const {
10
+ CONDITION_ERROR_FLAG,
9
11
  MAX_SAMPLED_PROBES_PER_PAUSE,
10
12
  SAMPLED_PROBE_COUNT_INDEX,
11
13
  SAMPLED_PROBE_INDEXES_START,
12
14
  SAMPLED_PROBE_OVERFLOW_INDEX,
13
15
  } = require('../probe_sampler_constants')
16
+ const { getTakeConditionErrorExpression } = require('./probe_sampler')
14
17
  const { breakpointToProbes, samplingIndexToProbe } = require('./state')
18
+ const { refreshBreakpoints } = require('./breakpoints')
15
19
  const session = require('./session')
16
20
  const { getLocalStateForCallFrame, evaluateCaptureExpressions } = require('./snapshot')
17
21
  const send = require('./send')
@@ -55,7 +59,11 @@ session.on('Debugger.paused', async ({ params }) => {
55
59
  let numberOfProbesWithSnapshots = 0
56
60
  let probesWithCaptureExpressions = false
57
61
  const probes = []
58
- let templateExpressions = ''
62
+ // Expressions evaluated on the paused frame in one round trip, in the order of `probes`: the evaluated template for
63
+ // probes whose template requires evaluation, and the recorded error for probes paused to report a condition error
64
+ let frameExpressions = ''
65
+ /** @type {Set<object> | undefined} */
66
+ let conditionErrorProbes
59
67
 
60
68
  // V8 doesn't allow setting more than one breakpoint at a specific location, however, it's possible to set two
61
69
  // breakpoints just next to each other that will "snap" to the same logical location, which in turn will be hit at the
@@ -81,7 +89,8 @@ session.on('Debugger.paused', async ({ params }) => {
81
89
  }
82
90
 
83
91
  for (let j = 0; j < numberOfSampledProbeIndexes; j++) {
84
- const samplingIndex = Atomics.load(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + j)
92
+ const sampledValue = Atomics.load(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + j)
93
+ const samplingIndex = sampledValue & ~CONDITION_ERROR_FLAG
85
94
  const probe = samplingIndexToProbe.get(samplingIndex)
86
95
 
87
96
  if (probe === undefined) {
@@ -94,6 +103,15 @@ session.on('Debugger.paused', async ({ params }) => {
94
103
  continue
95
104
  }
96
105
 
106
+ if ((sampledValue & CONDITION_ERROR_FLAG) !== 0) {
107
+ // The condition threw, so there's nothing to capture. Only the recorded error is needed from the paused thread.
108
+ conditionErrorProbes ??= new Set()
109
+ conditionErrorProbes.add(probe)
110
+ frameExpressions += `,${getTakeConditionErrorExpression(probe.id)}`
111
+ probes.push(probe)
112
+ continue
113
+ }
114
+
97
115
  if (probe.captureSnapshot === true || probe.compiledCaptureExpressions !== undefined) {
98
116
  if (probe.captureSnapshot === true) {
99
117
  numberOfProbesWithSnapshots++
@@ -107,7 +125,7 @@ session.on('Debugger.paused', async ({ params }) => {
107
125
  }
108
126
 
109
127
  if (probe.templateRequiresEvaluation) {
110
- templateExpressions += `,${probe.template}`
128
+ frameExpressions += `,${probe.template}`
111
129
  }
112
130
 
113
131
  probes.push(probe)
@@ -116,7 +134,9 @@ session.on('Debugger.paused', async ({ params }) => {
116
134
 
117
135
  // This can happen if sampled probe indexes are inconsistent with the worker state. Those cases are logged above.
118
136
  if (probes.length === 0) {
119
- return session.post('Debugger.resume')
137
+ await session.post('Debugger.resume')
138
+ reportPauseDuration(start)
139
+ return
120
140
  }
121
141
 
122
142
  const timestamp = Date.now()
@@ -125,9 +145,9 @@ session.on('Debugger.paused', async ({ params }) => {
125
145
  const { result } = /** @type {EvaluateOnCallFrameResult} */ (
126
146
  await session.post('Debugger.evaluateOnCallFrame', {
127
147
  callFrameId: params.callFrames[0].callFrameId,
128
- expression: templateExpressions.length === 0
148
+ expression: frameExpressions.length === 0
129
149
  ? `[${getDDTagsExpression}]`
130
- : `${templateExpressionSetupCode}[${getDDTagsExpression}${templateExpressions}]`,
150
+ : `${templateExpressionSetupCode}[${getDDTagsExpression}${frameExpressions}]`,
131
151
  returnByValue: true,
132
152
  includeCommandLineAPI: true,
133
153
  })
@@ -140,17 +160,14 @@ session.on('Debugger.paused', async ({ params }) => {
140
160
  }
141
161
 
142
162
  // TODO: Create unique states for each affected probe based on that probes unique `capture` settings (DEBUG-2863)
143
- let processLocalState
144
- /** @type {Error[] | undefined} */
145
- let fatalSnapshotErrors
163
+ /** @type {Awaited<ReturnType<typeof getLocalStateForCallFrame>> | undefined} */
164
+ let localState
146
165
  if (numberOfProbesWithSnapshots !== 0) {
147
- const result = await getLocalStateForCallFrame(
166
+ localState = await getLocalStateForCallFrame(
148
167
  params.callFrames[0],
149
168
  { maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength },
150
169
  start + config.dynamicInstrumentation.captureTimeoutNs
151
170
  )
152
- processLocalState = result.processLocalState
153
- fatalSnapshotErrors = result.fatalErrors
154
171
  }
155
172
 
156
173
  // Evaluate capture expressions for probes that have them
@@ -158,7 +175,7 @@ session.on('Debugger.paused', async ({ params }) => {
158
175
  if (probesWithCaptureExpressions === true) {
159
176
  captureExpressionResults = new Map()
160
177
  for (const probe of probes) {
161
- if (probe.compiledCaptureExpressions === undefined) continue
178
+ if (conditionErrorProbes?.has(probe) || probe.compiledCaptureExpressions === undefined) continue
162
179
  // eslint-disable-next-line no-await-in-loop
163
180
  captureExpressionResults.set(probe.id, await evaluateCaptureExpressions(
164
181
  params.callFrames[0],
@@ -169,14 +186,7 @@ session.on('Debugger.paused', async ({ params }) => {
169
186
  }
170
187
 
171
188
  await session.post('Debugger.resume')
172
- const diff = process.hrtime.bigint() - start // TODO: Recorded as telemetry (DEBUG-2858)
173
-
174
- // This doesn't measure the overhead of the CDP protocol. The actual pause time is slightly larger.
175
- // On my machine I'm seeing around 1.7ms of overhead.
176
- // eslint-disable-next-line eslint-rules/eslint-log-printf-style
177
- log.debug(() => `[debugger:devtools_client] Finished processing breakpoints - main thread paused for: ~${
178
- Number(diff) / 1_000_000
179
- } ms`)
189
+ reportPauseDuration(start)
180
190
 
181
191
  const logger = {
182
192
  // We can safely use `location.file` from the first probe in the array, since all probes hit by `hitBreakpoints`
@@ -189,9 +199,13 @@ session.on('Debugger.paused', async ({ params }) => {
189
199
  }
190
200
 
191
201
  const stack = await getStackFromCallFrames(params.callFrames)
192
- const dd = processDD(evalResults[0]) // the first result is the dd tags, the rest are the probe template results
202
+ const dd = processDD(evalResults[0]) // the first result is the dd tags, the rest are the frame expression results
193
203
  let messageIndex = 1
194
204
 
205
+ // The probes whose capture got permanently disabled during this pause, if any
206
+ /** @type {object[] | undefined} */
207
+ let captureDisabledProbes
208
+
195
209
  // TODO: Send multiple probes in one HTTP request as an array (DEBUG-2848)
196
210
  for (const probe of probes) {
197
211
  const snapshot = {
@@ -206,19 +220,48 @@ session.on('Debugger.paused', async ({ params }) => {
206
220
  language: 'javascript',
207
221
  }
208
222
 
223
+ // Which guardrail bucket the event belongs to, and which capture limits were enforced while producing it. The
224
+ // snapshot module records the reasons, including runtime errors: a fatal error does not necessarily mean one, as
225
+ // the collector also raises a fatal error to disable capture when it hits its large object safety threshold.
226
+ /** @type {number} */
227
+ let eventType = EVENT_TYPE.LOG
228
+ let incompleteReasons = 0
229
+
230
+ if (conditionErrorProbes?.has(probe)) {
231
+ // Report the failing condition instead of a probe result, so the user can see why the probe doesn't fire
232
+ const error = evalResults[messageIndex++]
233
+ const message = typeof error === 'string' ? error : 'Unknown evaluation error'
234
+ log.debug('[debugger:devtools_client] Condition of probe %s failed to evaluate: %s', probe.id, message)
235
+ snapshot.evaluationErrors = [{ expr: probe.when.dsl, message }]
236
+ ackEmitting(probe)
237
+ send(message, logger, dd, snapshot,
238
+ config.propagateProcessTags.enabled ? processTags.serialized : undefined,
239
+ probe.captureSnapshot === true || probe.compiledCaptureExpressions !== undefined
240
+ ? EVENT_TYPE.SNAPSHOT
241
+ : EVENT_TYPE.LOG,
242
+ 0)
243
+ continue
244
+ }
245
+
209
246
  if (probe.captureSnapshot) {
210
- if (fatalSnapshotErrors && fatalSnapshotErrors.length > 0) {
247
+ eventType = EVENT_TYPE.SNAPSHOT
248
+ const { processLocalState, fatalErrors, incomplete } = /** @type {NonNullable<typeof localState>} */ (localState)
249
+ if (fatalErrors.length > 0) {
211
250
  // There was an error collecting the snapshot for this probe, let's not try again
212
251
  probe.captureSnapshot = false
213
- probe.permanentEvaluationErrors = fatalSnapshotErrors.map(error => ({
252
+ probe.permanentEvaluationErrors = fatalErrors.map(error => ({
214
253
  expr: '',
215
254
  message: error.message,
216
255
  }))
256
+ captureDisabledProbes ??= []
257
+ captureDisabledProbes.push(probe)
217
258
  }
218
259
  snapshot.captures = {
219
- lines: { [probe.location.lines[0]]: { locals: /** @type {Function} */ (processLocalState)() } },
260
+ lines: { [probe.location.lines[0]]: { locals: processLocalState() } },
220
261
  }
262
+ incompleteReasons |= incomplete.reasons
221
263
  } else if (probe.compiledCaptureExpressions !== undefined) {
264
+ eventType = EVENT_TYPE.SNAPSHOT
222
265
  const expressionResult = /** @type {Map} */ (captureExpressionResults).get(probe.id)
223
266
  if (expressionResult) {
224
267
  // Handle fatal capture errors - disable capture expressions for this probe permanently
@@ -228,11 +271,14 @@ session.on('Debugger.paused', async ({ params }) => {
228
271
  expr: '',
229
272
  message: error.message,
230
273
  }))
274
+ captureDisabledProbes ??= []
275
+ captureDisabledProbes.push(probe)
231
276
  }
232
277
 
233
278
  snapshot.captures = {
234
279
  lines: { [probe.location.lines[0]]: { captureExpressions: expressionResult.processCaptureExpressions() } },
235
280
  }
281
+ incompleteReasons |= expressionResult.incomplete.reasons
236
282
 
237
283
  // Handle transient evaluation errors - include in snapshot for this capture
238
284
  if (expressionResult.evaluationErrors?.length > 0) {
@@ -249,6 +295,7 @@ session.on('Debugger.paused', async ({ params }) => {
249
295
  expr: '',
250
296
  message: 'Internal error: capture expression results not found',
251
297
  }]
298
+ incompleteReasons |= INCOMPLETE_REASON.RUNTIME_ERROR
252
299
  }
253
300
  }
254
301
 
@@ -283,10 +330,39 @@ session.on('Debugger.paused', async ({ params }) => {
283
330
  ackEmitting(probe)
284
331
 
285
332
  send(message, logger, dd, snapshot,
286
- config.propagateProcessTags.enabled ? processTags.serialized : undefined)
333
+ config.propagateProcessTags.enabled ? processTags.serialized : undefined,
334
+ eventType, incompleteReasons)
335
+ }
336
+
337
+ if (captureDisabledProbes !== undefined) {
338
+ // The breakpoint condition bakes in whether each probe produces snapshots, which decides if a hit counts against
339
+ // the global snapshot rate limit and how a skipped hit is classified. Rebuild the conditions now that this
340
+ // changed. The disabled probes can be spread over more than one breakpoint, but each affected location is only
341
+ // refreshed once.
342
+ refreshBreakpoints(captureDisabledProbes).catch((err) => {
343
+ // eslint-disable-next-line eslint-rules/eslint-log-printf-style
344
+ log.error(() => {
345
+ let ids = captureDisabledProbes[0].id
346
+ for (let i = 1; i < captureDisabledProbes.length; i++) ids += `, ${captureDisabledProbes[i].id}`
347
+ return `[debugger:devtools_client] Error refreshing breakpoints after disabling capture for probes: ${ids}`
348
+ }, err)
349
+ })
287
350
  }
288
351
  })
289
352
 
353
+ /**
354
+ * Called after resuming so reporting the elapsed time doesn't extend the pause. This measures from receipt of the
355
+ * pause notification through the resume response, not the full time V8 suspends the instrumented thread.
356
+ *
357
+ * @param {bigint} start - Monotonic time when the pause notification was received, in nanoseconds.
358
+ */
359
+ function reportPauseDuration (start) {
360
+ const durationMs = Number(process.hrtime.bigint() - start) / 1_000_000
361
+ parentPort.postMessage({ type: 'thread-paused', durationMs })
362
+ log.debug('[debugger:devtools_client] Finished processing breakpoints - instrumented thread paused for: ~%d ms',
363
+ durationMs)
364
+ }
365
+
290
366
  function processDD (result) {
291
367
  return result?.trace_id === undefined ? undefined : result
292
368
  }
@@ -1,40 +1,83 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * Batches JSON documents into a JSON array payload, flushed when the batch reaches its size limit or after a timeout.
5
+ *
6
+ * The buffer is bounded in bytes: the batch being assembled plus all flushed payloads that are still in flight (handed
7
+ * to `onFlush` but not yet released through its `done` callback) can never exceed `maxQueueBytes`. Documents that would
8
+ * exceed the bound are rejected instead of queued, so a stalled or slow upload target cannot make the buffer grow
9
+ * without bound.
10
+ */
3
11
  class JSONBuffer {
4
12
  #maxSize
13
+ #maxQueueBytes
5
14
  #timeout
6
15
  #onFlush
7
16
  #timer
8
17
  #partialJson
18
+ #partialBytes = 0
19
+ #inFlightBytes = 0
9
20
 
10
- constructor ({ size, timeout, onFlush }) {
21
+ /**
22
+ * @param {object} options
23
+ * @param {number} options.size - Maximum size in bytes of a single flushed payload
24
+ * @param {number} options.maxQueueBytes - Maximum bytes queued, including payloads still in flight
25
+ * @param {number} options.timeout - Milliseconds to wait for more documents before flushing a partial payload
26
+ * @param {(json: string, done: () => void) => void} options.onFlush - Receives each payload. Must call `done` once
27
+ * the payload is no longer held in memory, so its bytes are released from the queue bound.
28
+ */
29
+ constructor ({ size, maxQueueBytes, timeout, onFlush }) {
11
30
  this.#maxSize = size
31
+ this.#maxQueueBytes = maxQueueBytes
12
32
  this.#timeout = timeout
13
33
  this.#onFlush = onFlush
14
34
  }
15
35
 
16
36
  #flush () {
17
37
  const json = `${this.#partialJson}]`
38
+ const bytes = this.#partialBytes + 1
18
39
  this.#partialJson = undefined
19
- this.#onFlush(json)
40
+ this.#partialBytes = 0
41
+ this.#inFlightBytes += bytes
42
+
43
+ let released = false
44
+ this.#onFlush(json, () => {
45
+ if (released) return
46
+ released = true
47
+ this.#inFlightBytes -= bytes
48
+ })
20
49
  }
21
50
 
51
+ /**
52
+ * Add a JSON document to the batch.
53
+ *
54
+ * @param {string} str - The JSON document
55
+ * @param {number} [size] - The size of the document in bytes. Calculated if not provided.
56
+ */
22
57
  write (str, size = Buffer.byteLength(str)) {
58
+ // The document is prefixed by `[` or `,` and the payload is terminated by `]`, hence the extra 2 bytes
59
+ if (this.#partialJson !== undefined && this.#partialBytes + size + 2 > this.#maxSize) {
60
+ clearTimeout(this.#timer)
61
+ this.#timer = undefined
62
+ this.#flush()
63
+ }
64
+
65
+ if (this.#partialBytes + this.#inFlightBytes + size + 2 > this.#maxQueueBytes) return false
66
+
23
67
  if (this.#partialJson === undefined) {
24
68
  this.#partialJson = `[${str}`
69
+ this.#partialBytes = size + 1
25
70
  if (this.#timer === undefined) {
26
71
  this.#timer = setTimeout(() => this.#flush(), this.#timeout)
27
72
  } else {
28
73
  this.#timer.refresh()
29
74
  }
30
- } else if (Buffer.byteLength(this.#partialJson) + size + 2 > this.#maxSize) {
31
- clearTimeout(this.#timer)
32
- this.#timer = undefined
33
- this.#flush()
34
- this.write(str, size)
35
75
  } else {
36
76
  this.#partialJson += `,${str}`
77
+ this.#partialBytes += size + 1
37
78
  }
79
+
80
+ return true
38
81
  }
39
82
  }
40
83
 
@@ -13,7 +13,9 @@ const { config: { debug = false, logLevel } = {}, logPort } = workerData ?? {}
13
13
  const LEVELS = ['error', 'warn', 'info', 'debug']
14
14
  const on = (level, ...args) => {
15
15
  if (typeof args[0] === 'function') {
16
- args = [args[0]()]
16
+ // Resolve the message only now that we know it will be logged. Keep the remaining arguments: the main thread
17
+ // logger reads a trailing error as the cause of the log record.
18
+ args[0] = args[0]()
17
19
  }
18
20
  logPort.postMessage({ level, args })
19
21
  }
@@ -8,6 +8,7 @@ const SAMPLER_EXPRESSION = `globalThis[Symbol.for(${JSON.stringify(DD_TRACE_SYMB
8
8
  module.exports = {
9
9
  compileBreakpointCondition,
10
10
  getRemoveProbeExpression,
11
+ getTakeConditionErrorExpression,
11
12
  isSnapshotProducingProbe,
12
13
  }
13
14
 
@@ -17,7 +18,6 @@ module.exports = {
17
18
  * global snapshot rate limit.
18
19
  *
19
20
  * @param {{ captureSnapshot?: boolean, compiledCaptureExpressions?: object[] }} probe - The probe to inspect.
20
- * @returns {boolean}
21
21
  */
22
22
  function isSnapshotProducingProbe (probe) {
23
23
  return probe.captureSnapshot === true || probe.compiledCaptureExpressions !== undefined
@@ -28,12 +28,21 @@ function isSnapshotProducingProbe (probe) {
28
28
  * the debuggee.
29
29
  *
30
30
  * @param {string} id - The probe id.
31
- * @returns {string}
32
31
  */
33
32
  function getRemoveProbeExpression (id) {
34
33
  return `${SAMPLER_EXPRESSION}?.remove(${JSON.stringify(id)})`
35
34
  }
36
35
 
36
+ /**
37
+ * Build the expression that hands over the condition error recorded for a probe. Called by the devtools worker and
38
+ * evaluated on the paused frame of the debuggee.
39
+ *
40
+ * @param {string} id - The probe id.
41
+ */
42
+ function getTakeConditionErrorExpression (id) {
43
+ return `${SAMPLER_EXPRESSION}?.takeConditionError(${JSON.stringify(id)})`
44
+ }
45
+
37
46
  /**
38
47
  * Build a Chrome DevTools breakpoint condition that samples all matching probes at a location. Called by the devtools
39
48
  * worker.
@@ -46,7 +55,6 @@ function getRemoveProbeExpression (id) {
46
55
  * captureSnapshot?: boolean,
47
56
  * compiledCaptureExpressions?: object[]
48
57
  * }[]} probes - The probes at the breakpoint location.
49
- * @returns {string}
50
58
  */
51
59
  function compileBreakpointCondition (probes) {
52
60
  const probeConditions = probes.map(compileProbeCondition)
@@ -75,7 +83,6 @@ function compileBreakpointCondition (probes) {
75
83
  * captureSnapshot?: boolean,
76
84
  * compiledCaptureExpressions?: object[]
77
85
  * }} probe - The probe to sample.
78
- * @returns {string}
79
86
  */
80
87
  function compileProbeCondition (probe) {
81
88
  const sample = `$dd_sampler.makeSampleDecision(${probe.samplingIndex}, ${JSON.stringify(probe.id)}, ` +
@@ -85,9 +92,15 @@ function compileProbeCondition (probe) {
85
92
  return `$dd_sampled = ${sample} || $dd_sampled`
86
93
  }
87
94
 
88
- return `try {
89
- if ((${probe.condition}) === true) {
90
- $dd_sampled = ${sample} || $dd_sampled
95
+ // A condition that throws is reported once per throttle window and skipped at probe entry in between
96
+ return `if ($dd_sampler.shouldEvaluateCondition(${JSON.stringify(probe.id)})) {
97
+ try {
98
+ if ((${probe.condition}) === true) {
99
+ $dd_sampled = ${sample} || $dd_sampled
100
+ }
101
+ } catch ($dd_error) {
102
+ $dd_sampled = $dd_sampler.conditionError(${probe.samplingIndex}, ${JSON.stringify(probe.id)}, $dd_error) ||
103
+ $dd_sampled
91
104
  }
92
- } catch {}`
105
+ }`
93
106
  }