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
@@ -0,0 +1,167 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Guardrail telemetry counters shared between the main thread and the debugger worker thread.
5
+ *
6
+ * Guardrail decisions are made in two places: the probe sampler that runs inside the V8 breakpoint condition on the
7
+ * main thread (rate limits) and the debugger worker (capture limits, queue overflow). Neither should pay for a message
8
+ * or a telemetry API call per skipped, dropped or incomplete event, so both increment counters in a shared buffer using
9
+ * atomics, and the main thread periodically drains the counters into instrumentation telemetry metrics.
10
+ *
11
+ * The metric names, tags and reason codes are defined by the "Debugger Observability for GA" specification and are
12
+ * shared with the other tracers. All metrics are counts in the `live_debugger` telemetry namespace.
13
+ */
14
+
15
+ const TELEMETRY_NAMESPACE = 'live_debugger'
16
+
17
+ const EVENT_TYPE = Object.freeze({
18
+ SNAPSHOT: 0,
19
+ LOG: 1,
20
+ DIAGNOSTIC: 2,
21
+ })
22
+ const EVENT_TYPE_NAMES = ['snapshot', 'log', 'diagnostic']
23
+
24
+ /** Attempts stopped before event creation (`events.skipped`) */
25
+ const SKIPPED_REASON = Object.freeze({
26
+ RATE_LIMIT_GLOBAL: 0,
27
+ RATE_LIMIT_PROBE: 1,
28
+ EVALUATION_TIMEOUT: 2,
29
+ })
30
+ const SKIPPED_REASON_NAMES = ['rateLimitGlobal', 'rateLimitProbe', 'evaluationTimeout']
31
+
32
+ /** Complete events dropped before transport (`events.dropped`) */
33
+ const DROPPED_REASON = Object.freeze({
34
+ QUEUE_FULL: 0,
35
+ // The spec allows either trimming oversized payloads or dropping them whole. This tracer trims, and reports
36
+ // `capture.incomplete{payloadTooLarge}` instead, so this reason exists for spec parity but is never emitted.
37
+ PAYLOAD_TOO_LARGE: 1,
38
+ })
39
+ const DROPPED_REASON_NAMES = ['queueFull', 'payloadTooLarge']
40
+
41
+ /**
42
+ * Capture enforced limits (`capture.incomplete`). These are bit flags so that the reasons hit while producing a single
43
+ * event can be accumulated in a bitmask and reported once per event per reason.
44
+ */
45
+ const INCOMPLETE_REASON = Object.freeze({
46
+ RUNTIME_ERROR: 1 << 0,
47
+ TIMEOUT: 1 << 1,
48
+ DEPTH: 1 << 2,
49
+ FIELD_COUNT: 1 << 3,
50
+ COLLECTION_SIZE: 1 << 4,
51
+ STRING_LENGTH: 1 << 5,
52
+ PAYLOAD_TOO_LARGE: 1 << 6,
53
+ OTHER: 1 << 7,
54
+ })
55
+ const INCOMPLETE_REASON_NAMES = [
56
+ 'runtimeError', 'timeout', 'depth', 'fieldCount', 'collectionSize', 'stringLength', 'payloadTooLarge', 'other',
57
+ ]
58
+
59
+ const EVENT_TYPE_COUNT = EVENT_TYPE_NAMES.length
60
+ const SKIPPED_OFFSET = 0
61
+ const DROPPED_OFFSET = SKIPPED_OFFSET + SKIPPED_REASON_NAMES.length * EVENT_TYPE_COUNT
62
+ const INCOMPLETE_OFFSET = DROPPED_OFFSET + DROPPED_REASON_NAMES.length * EVENT_TYPE_COUNT
63
+ const SLOT_COUNT = INCOMPLETE_OFFSET + INCOMPLETE_REASON_NAMES.length * EVENT_TYPE_COUNT
64
+
65
+ /**
66
+ * @typedef {object} MetricSlot
67
+ * @property {string} metric - The telemetry metric name
68
+ * @property {string[]} tags - The telemetry tags, sorted
69
+ */
70
+
71
+ /** @type {MetricSlot[]} */
72
+ const SLOTS = []
73
+ addSlots('events.skipped', SKIPPED_REASON_NAMES)
74
+ addSlots('events.dropped', DROPPED_REASON_NAMES)
75
+ addSlots('capture.incomplete', INCOMPLETE_REASON_NAMES)
76
+
77
+ class GuardrailMetrics {
78
+ /** @type {Int32Array} */
79
+ #counters
80
+
81
+ /**
82
+ * @param {SharedArrayBuffer} buffer - A buffer created with {@link GuardrailMetrics.createBuffer}
83
+ */
84
+ constructor (buffer) {
85
+ this.#counters = new Int32Array(buffer)
86
+ }
87
+
88
+ /**
89
+ * Create the shared buffer backing the counters.
90
+ *
91
+ * @returns {SharedArrayBuffer}
92
+ */
93
+ static createBuffer () {
94
+ return new SharedArrayBuffer(SLOT_COUNT * Int32Array.BYTES_PER_ELEMENT)
95
+ }
96
+
97
+ /**
98
+ * Record an attempt that was stopped before an event was created.
99
+ *
100
+ * @param {number} reason - One of {@link SKIPPED_REASON}
101
+ * @param {number} eventType - One of {@link EVENT_TYPE}
102
+ */
103
+ eventSkipped (reason, eventType) {
104
+ Atomics.add(this.#counters, SKIPPED_OFFSET + reason * EVENT_TYPE_COUNT + eventType, 1)
105
+ }
106
+
107
+ /**
108
+ * Record a complete event that was dropped before transport.
109
+ *
110
+ * @param {number} reason - One of {@link DROPPED_REASON}
111
+ * @param {number} eventType - One of {@link EVENT_TYPE}
112
+ * @param {number} [count] - The number of dropped events
113
+ */
114
+ eventDropped (reason, eventType, count = 1) {
115
+ Atomics.add(this.#counters, DROPPED_OFFSET + reason * EVENT_TYPE_COUNT + eventType, count)
116
+ }
117
+
118
+ /**
119
+ * Record the capture limits enforced while producing a single event, once per reason.
120
+ *
121
+ * @param {number} reasons - A bitmask of {@link INCOMPLETE_REASON} flags
122
+ * @param {number} eventType - One of {@link EVENT_TYPE}
123
+ */
124
+ captureIncomplete (reasons, eventType) {
125
+ for (let bit = 0; reasons !== 0; bit++, reasons >>>= 1) {
126
+ if ((reasons & 1) === 1) {
127
+ Atomics.add(this.#counters, INCOMPLETE_OFFSET + bit * EVENT_TYPE_COUNT + eventType, 1)
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Reset all counters and report the ones that were non-zero.
134
+ *
135
+ * @param {(metric: string, tags: string[], count: number) => void} report - Called once per non-zero counter
136
+ */
137
+ drain (report) {
138
+ for (let i = 0; i < SLOT_COUNT; i++) {
139
+ const count = Atomics.exchange(this.#counters, i, 0)
140
+ if (count !== 0) {
141
+ const { metric, tags } = SLOTS[i]
142
+ report(metric, tags, count)
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * @param {string} metric
150
+ * @param {string[]} reasonNames
151
+ */
152
+ function addSlots (metric, reasonNames) {
153
+ for (const reason of reasonNames) {
154
+ for (const eventType of EVENT_TYPE_NAMES) {
155
+ SLOTS.push({ metric, tags: [`event_type:${eventType}`, `reason:${reason}`] })
156
+ }
157
+ }
158
+ }
159
+
160
+ module.exports = {
161
+ DROPPED_REASON,
162
+ EVENT_TYPE,
163
+ GuardrailMetrics,
164
+ INCOMPLETE_REASON,
165
+ SKIPPED_REASON,
166
+ TELEMETRY_NAMESPACE,
167
+ }
@@ -4,15 +4,19 @@ const { readFile } = require('fs')
4
4
  const { types } = require('util')
5
5
  const { join } = require('path')
6
6
  const { Worker, MessageChannel, threadId: parentThreadId } = require('worker_threads')
7
+ const dc = require('dc-polyfill')
7
8
  const log = require('../log')
8
9
  const { fetchAgentInfo } = require('../agent/info')
10
+ const telemetryMetrics = require('../telemetry/metrics')
9
11
  const getDebuggerConfig = require('./config')
10
12
  const {
11
13
  DEBUGGER_DIAGNOSTICS_V1,
12
14
  DEBUGGER_INPUT_DIRECT,
13
15
  DEBUGGER_INPUT_V2,
16
+ GUARDRAIL_METRICS_FLUSH_INTERVAL_MS,
14
17
  INSPECT_SEGMENT_GLOBAL_PROPERTY,
15
18
  } = require('./constants')
19
+ const { GuardrailMetrics, TELEMETRY_NAMESPACE } = require('./guardrail-metrics')
16
20
  const { installProbeSampler, uninstallProbeSampler } = require('./probe_sampler')
17
21
 
18
22
  /**
@@ -23,12 +27,20 @@ const { installProbeSampler, uninstallProbeSampler } = require('./probe_sampler'
23
27
  * @typedef {import('../remote_config')} RemoteConfig
24
28
  */
25
29
 
30
+ // Published by telemetry right before it sends its final metrics on process exit. The flush interval timer is unref'ed
31
+ // and the worker does not keep the process alive, so without this hook everything counted since the last tick would
32
+ // be lost when the application exits on its own.
33
+ const TELEMETRY_APP_CLOSING_CHANNEL = 'datadog:telemetry:app-closing'
34
+
26
35
  let worker = null
27
36
  let configChannel = null
28
37
  let ackId = 0
29
38
  let rcAckCallbacks = null
30
39
  let rc = null
31
40
  let inputPath = null
41
+ /** @type {GuardrailMetrics | null} */
42
+ let guardrailMetrics = null
43
+ let guardrailMetricsTimer = null
32
44
 
33
45
  // eslint-disable-next-line eslint-rules/eslint-process-env
34
46
  const { NODE_OPTIONS, ...env } = process.env
@@ -43,7 +55,6 @@ module.exports = {
43
55
  /**
44
56
  * Check if the Debugger worker is currently running
45
57
  *
46
- * @returns {boolean} True if the worker is started, false otherwise
47
58
  */
48
59
  function isStarted () {
49
60
  return worker !== null
@@ -77,9 +88,15 @@ function start (config, rcInstance) {
77
88
  debuggerGlobals.utilTypes = types
78
89
  debuggerGlobals[INSPECT_SEGMENT_GLOBAL_PROPERTY] = require('./inspect-segment')
79
90
 
80
- const probeSamplerBuffer = installProbeSampler()
91
+ const guardrailMetricsBuffer = GuardrailMetrics.createBuffer()
92
+ guardrailMetrics = new GuardrailMetrics(guardrailMetricsBuffer)
93
+ guardrailMetricsTimer = setInterval(flushGuardrailMetrics, GUARDRAIL_METRICS_FLUSH_INTERVAL_MS)
94
+ guardrailMetricsTimer.unref?.()
95
+ dc.subscribe(TELEMETRY_APP_CLOSING_CHANNEL, flushGuardrailMetrics)
96
+
97
+ const probeSamplerBuffer = installProbeSampler(guardrailMetrics)
81
98
 
82
- readProbeFile(config.dynamicInstrumentation.probeFile, (probes) => {
99
+ readProbeFile(config.dynamicInstrumentation.DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE, (probes) => {
83
100
  const action = 'apply'
84
101
  for (const probe of probes) {
85
102
  probeChannel.port2.postMessage({ action, probe })
@@ -125,6 +142,7 @@ function start (config, rcInstance) {
125
142
  logPort: logChannel.port1,
126
143
  configPort: configChannel.port1,
127
144
  probeSamplerBuffer,
145
+ guardrailMetricsBuffer,
128
146
  },
129
147
  transferList: [probeChannel.port1, logChannel.port1, configChannel.port1],
130
148
  }
@@ -137,6 +155,11 @@ function start (config, rcInstance) {
137
155
  )
138
156
  })
139
157
 
158
+ const threadPausedMetric = telemetryMetrics.manager.namespace(TELEMETRY_NAMESPACE)
159
+ .distribution('execution.pause.duration')
160
+ worker.on('message', (/** @type {{ type: string, durationMs: number }} */ { type, durationMs }) => {
161
+ if (type === 'thread-paused') threadPausedMetric.track(durationMs)
162
+ })
140
163
  worker.on('error', (err) => log.error('[debugger] worker thread error', err))
141
164
  worker.on('messageerror', (err) => log.error('[debugger] received "messageerror" from worker', err))
142
165
 
@@ -211,6 +234,20 @@ function cleanup (error) {
211
234
  configChannel = null
212
235
  inputPath = null
213
236
 
237
+ if (guardrailMetricsTimer !== null) {
238
+ clearInterval(guardrailMetricsTimer)
239
+ guardrailMetricsTimer = null
240
+ }
241
+ if (guardrailMetrics !== null) {
242
+ dc.unsubscribe(TELEMETRY_APP_CLOSING_CHANNEL, flushGuardrailMetrics)
243
+ // Report what the worker counted up until it was stopped. Known limitation: `Worker#terminate()` interrupts the
244
+ // worker asynchronously, so anything it counts between this drain and its actual termination is lost. That only
245
+ // concerns events still sitting in the worker's upload buffer, which die with the worker anyway, so it isn't worth
246
+ // deferring the drain until the worker has exited.
247
+ flushGuardrailMetrics()
248
+ guardrailMetrics = null
249
+ }
250
+
214
251
  // Call any pending ack callbacks
215
252
  // Pass error for unexpected exits, or undefined for graceful shutdown
216
253
  if (rcAckCallbacks) {
@@ -223,6 +260,17 @@ function cleanup (error) {
223
260
  }
224
261
  }
225
262
 
263
+ /**
264
+ * Convert the guardrail counters accumulated by the probe sampler and the worker into telemetry metrics.
265
+ */
266
+ function flushGuardrailMetrics () {
267
+ if (guardrailMetrics === null) return
268
+ const namespace = telemetryMetrics.manager.namespace(TELEMETRY_NAMESPACE)
269
+ guardrailMetrics.drain((metric, tags, count) => {
270
+ namespace.count(metric, tags).inc(count)
271
+ })
272
+ }
273
+
226
274
  /**
227
275
  * Detect which debugger endpoint is available on the agent
228
276
  *
@@ -34,7 +34,6 @@ module.exports = inspectSegment
34
34
  * truncates objects before inspecting them.
35
35
  *
36
36
  * @param {unknown} value
37
- * @returns {string}
38
37
  */
39
38
  function inspectSegment (value) {
40
39
  if (value === null || (typeof value !== 'object' && typeof value !== 'function')) {
@@ -99,7 +98,6 @@ function inspectSegment (value) {
99
98
  *
100
99
  * @param {Collection} value
101
100
  * @param {boolean} isMap
102
- * @returns {string}
103
101
  */
104
102
  function inspectCollection (value, isMap) {
105
103
  if (NODE_MAJOR !== 18) return inspect(value, segmentInspectOptions)
@@ -137,7 +135,6 @@ function inspectCollection (value, isMap) {
137
135
  * Determine whether inspecting a value could invoke a proxy trap or toStringTag getter.
138
136
  *
139
137
  * @param {unknown} value
140
- * @returns {boolean}
141
138
  */
142
139
  function inspectionCanRunUserCode (value) {
143
140
  const type = typeof value
@@ -1,7 +1,13 @@
1
1
  'use strict'
2
2
 
3
+ const { types } = require('node:util')
4
+
3
5
  const { MAX_SNAPSHOTS_PER_SECOND_GLOBALLY } = require('./devtools_client/defaults')
6
+ const { MAX_MESSAGE_LENGTH } = require('./constants')
7
+ const { EVENT_TYPE, SKIPPED_REASON } = require('./guardrail-metrics')
4
8
  const {
9
+ CONDITION_ERROR_FLAG,
10
+ CONDITION_ERROR_THROTTLE_NS,
5
11
  DD_TRACE_SYMBOL,
6
12
  MAX_SAMPLED_PROBES_PER_PAUSE,
7
13
  PROBE_SAMPLER_SYMBOL,
@@ -22,12 +28,20 @@ module.exports = {
22
28
  /**
23
29
  * Install the runtime sampler in the debuggee context.
24
30
  *
31
+ * @param {import('./guardrail-metrics').GuardrailMetrics} guardrailMetrics - Counters for skipped probe hits.
25
32
  * @returns {SharedArrayBuffer} The shared sampler buffer to pass to the debugger worker.
26
33
  */
27
- function installProbeSampler () {
34
+ function installProbeSampler (guardrailMetrics) {
28
35
  const buffer = createProbeSamplerBuffer()
29
36
 
30
37
  const lastCaptureNsByProbeId = new Map()
38
+ /**
39
+ * Probes whose condition recently failed to evaluate, keyed by probe id. The error is kept until the worker picks it
40
+ * up for the error result.
41
+ *
42
+ * @type {Map<string, { throttledUntilNs: bigint, error: string | undefined }>}
43
+ */
44
+ const conditionErrorByProbeId = new Map()
31
45
  const sampledProbeIndexes = new Int32Array(buffer)
32
46
  Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 0)
33
47
  Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 0)
@@ -44,27 +58,29 @@ function installProbeSampler () {
44
58
  * @param {string} probeId - The probe id.
45
59
  * @param {bigint} nsBetweenSampling - Minimum nanoseconds between samples for this probe.
46
60
  * @param {boolean} isSnapshotProducingProbe - Whether this probe counts toward the global snapshot sample limit.
47
- * @returns {boolean} Whether this probe should make the breakpoint condition pause.
48
61
  */
49
62
  makeSampleDecision (probeIndex, probeId, nsBetweenSampling, isSnapshotProducingProbe) {
50
63
  const now = process.hrtime.bigint()
51
64
  const lastCaptureNs = lastCaptureNsByProbeId.get(probeId)
52
- if (lastCaptureNs !== undefined && now - lastCaptureNs < nsBetweenSampling) return false
65
+ if (lastCaptureNs !== undefined && now - lastCaptureNs < nsBetweenSampling) {
66
+ guardrailMetrics.eventSkipped(
67
+ SKIPPED_REASON.RATE_LIMIT_PROBE,
68
+ isSnapshotProducingProbe === true ? EVENT_TYPE.SNAPSHOT : EVENT_TYPE.LOG
69
+ )
70
+ return false
71
+ }
53
72
 
54
73
  let shouldResetGlobalSnapshotRateWindow = false
55
74
  if (isSnapshotProducingProbe === true) {
56
75
  if (now - globalSnapshotSamplingRateWindowStart > oneSecondNs) {
57
76
  shouldResetGlobalSnapshotRateWindow = true
58
77
  } else if (snapshotsSampledWithinTheLastSecond >= MAX_SNAPSHOTS_PER_SECOND_GLOBALLY) {
78
+ guardrailMetrics.eventSkipped(SKIPPED_REASON.RATE_LIMIT_GLOBAL, EVENT_TYPE.SNAPSHOT)
59
79
  return false
60
80
  }
61
81
  }
62
82
 
63
- const sampledProbeCount = Atomics.add(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 1)
64
- if (sampledProbeCount >= MAX_SAMPLED_PROBES_PER_PAUSE) {
65
- Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 1)
66
- return false
67
- }
83
+ if (!storeSampledProbeIndex(probeIndex)) return false
68
84
 
69
85
  if (isSnapshotProducingProbe === true) {
70
86
  if (shouldResetGlobalSnapshotRateWindow === true) {
@@ -76,10 +92,56 @@ function installProbeSampler () {
76
92
  }
77
93
 
78
94
  lastCaptureNsByProbeId.set(probeId, now)
79
- Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + sampledProbeCount, probeIndex)
80
95
  return true
81
96
  },
82
97
 
98
+ /**
99
+ * Decide if a probe's condition should be evaluated, or skipped because a recent evaluation error throttled it.
100
+ *
101
+ * @param {string} probeId - The probe id.
102
+ */
103
+ shouldEvaluateCondition (probeId) {
104
+ const state = conditionErrorByProbeId.get(probeId)
105
+ if (state === undefined) return true
106
+ if (process.hrtime.bigint() < state.throttledUntilNs) return false
107
+ conditionErrorByProbeId.delete(probeId)
108
+ return true
109
+ },
110
+
111
+ /**
112
+ * Record that a probe's condition threw, throttle the probe, and request a pause if the error can be reported.
113
+ *
114
+ * Error results bypass the per-probe and global rate limits: they are rate limited by the throttle instead, which
115
+ * allows one error result per probe per window. If the shared buffer is full, the error is dropped but the throttle
116
+ * is retained.
117
+ *
118
+ * @param {number} probeIndex - The worker-side probe sampling index.
119
+ * @param {string} probeId - The probe id.
120
+ * @param {unknown} error - The value thrown by the condition.
121
+ */
122
+ conditionError (probeIndex, probeId, error) {
123
+ const stored = storeSampledProbeIndex(probeIndex | CONDITION_ERROR_FLAG)
124
+ conditionErrorByProbeId.set(probeId, {
125
+ throttledUntilNs: process.hrtime.bigint() + CONDITION_ERROR_THROTTLE_NS,
126
+ error: stored ? describeError(error) : undefined,
127
+ })
128
+ return stored
129
+ },
130
+
131
+ /**
132
+ * Hand over the recorded condition error for a probe to the worker. Called by the worker on the paused thread.
133
+ *
134
+ * @param {string} probeId - The probe id.
135
+ * @returns {string | undefined} The error description, if any.
136
+ */
137
+ takeConditionError (probeId) {
138
+ const state = conditionErrorByProbeId.get(probeId)
139
+ if (state === undefined) return
140
+ const { error } = state
141
+ state.error = undefined
142
+ return error
143
+ },
144
+
83
145
  /**
84
146
  * Remove cached sampling state for a probe.
85
147
  *
@@ -87,9 +149,25 @@ function installProbeSampler () {
87
149
  */
88
150
  remove (probeId) {
89
151
  lastCaptureNsByProbeId.delete(probeId)
152
+ conditionErrorByProbeId.delete(probeId)
90
153
  },
91
154
  }
92
155
 
156
+ /**
157
+ * Hand a sampled probe index over to the worker for the upcoming pause.
158
+ *
159
+ * @param {number} value - The probe sampling index, possibly with flags set.
160
+ */
161
+ function storeSampledProbeIndex (value) {
162
+ const sampledProbeCount = Atomics.add(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 1)
163
+ if (sampledProbeCount >= MAX_SAMPLED_PROBES_PER_PAUSE) {
164
+ Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 1)
165
+ return false
166
+ }
167
+ Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + sampledProbeCount, value)
168
+ return true
169
+ }
170
+
93
171
  return buffer
94
172
  }
95
173
 
@@ -100,6 +178,52 @@ function uninstallProbeSampler () {
100
178
  delete ddTraceGlobal[Symbol.for(PROBE_SAMPLER_SYMBOL)]
101
179
  }
102
180
 
181
+ /**
182
+ * Describe a value thrown by a probe condition without invoking user code. Conditions can throw anything, including
183
+ * errors with accessors and proxies whose traps have side effects.
184
+ *
185
+ * @param {unknown} error - The thrown value.
186
+ */
187
+ function describeError (error) {
188
+ if (typeof error === 'string') return truncateErrorDescription(error)
189
+ if (error === null || (typeof error !== 'object' && typeof error !== 'function')) {
190
+ return 'Unknown evaluation error'
191
+ }
192
+
193
+ const name = getErrorStringProperty(error, 'name')
194
+ const message = getErrorStringProperty(error, 'message')
195
+ if (name === undefined) return truncateErrorDescription(message ?? 'Unknown evaluation error')
196
+ if (message === undefined) return truncateErrorDescription(name)
197
+
198
+ return truncateErrorDescription(`${name}: ${message}`)
199
+ }
200
+
201
+ /**
202
+ * Bound an error description before retaining or transferring it.
203
+ *
204
+ * @param {string} description - The description to bound.
205
+ */
206
+ function truncateErrorDescription (description) {
207
+ return description.length > MAX_MESSAGE_LENGTH
208
+ ? `${description.slice(0, MAX_MESSAGE_LENGTH)}…`
209
+ : description
210
+ }
211
+
212
+ /**
213
+ * Read a string-valued error property without invoking accessors or proxy traps.
214
+ *
215
+ * @param {object} error - The thrown object to inspect.
216
+ * @param {'name' | 'message'} property - The property to find.
217
+ */
218
+ function getErrorStringProperty (error, property) {
219
+ while (error !== null) {
220
+ if (types.isProxy(error)) return
221
+ const descriptor = Object.getOwnPropertyDescriptor(error, property)
222
+ if (descriptor !== undefined) return typeof descriptor.value === 'string' ? descriptor.value : undefined
223
+ error = Object.getPrototypeOf(error)
224
+ }
225
+ }
226
+
103
227
  /**
104
228
  * Create the shared buffer used to hand sampled probe indexes from breakpoint conditions to the debugger worker.
105
229
  *
@@ -3,13 +3,43 @@
3
3
  const DD_TRACE_SYMBOL = 'dd-trace'
4
4
  const PROBE_SAMPLER_SYMBOL = 'dd-trace.debugger.probeSampler'
5
5
 
6
- // Shared sampler contract used by the main debugger bootstrap and the devtools worker.
6
+ // Shared sampler contract used by the main debugger bootstrap and the devtools worker. The debuggee hands sampled
7
+ // probes to the worker through a shared Int32Array, drained by the worker on every pause:
8
+ //
9
+ // +----------+----------+----------+----------+- - - -+------------+
10
+ // slot | 0 | 1 | 2 | 3 | | 2+MAX-1 |
11
+ // holds | count | overflow | value | value | ... | value |
12
+ // +----------+----------+----------+----------+- - - -+------------+
13
+ // ^COUNT ^OVERFLOW ^INDEXES_START
7
14
  const MAX_SAMPLED_PROBES_PER_PAUSE = 256
8
15
  const SAMPLED_PROBE_COUNT_INDEX = 0
9
16
  const SAMPLED_PROBE_OVERFLOW_INDEX = 1
10
17
  const SAMPLED_PROBE_INDEXES_START = 2
11
18
 
19
+ // Each value above is a probe sampling index, with this flag set in bit 30 when the pause is for reporting a
20
+ // condition evaluation error instead of a probe result. Bit 31 stays unused because the array is signed:
21
+ //
22
+ // +--------+------------+--------------------------------------------+
23
+ // bit | 31 | 30 | 29..0 |
24
+ // means | unused | CONDITION_ | samplingIndex |
25
+ // | (sign) | ERROR_FLAG | |
26
+ // +--------+------------+--------------------------------------------+
27
+ //
28
+ // 0x0000_0007 probe #7 matched -> worker captures as usual
29
+ // 0x4000_0007 probe #7's condition threw -> worker takes the recorded error, captures nothing
30
+ //
31
+ // write: probeIndex | CONDITION_ERROR_FLAG
32
+ // read: value & ~CONDITION_ERROR_FLAG -> the sampling index
33
+ // value & CONDITION_ERROR_FLAG -> non-zero when the condition threw
34
+ const CONDITION_ERROR_FLAG = 1 << 30
35
+
36
+ // A probe whose condition failed to evaluate is not evaluated again for this long. One error result is reported per
37
+ // window, so a probe with a broken condition stays visible without repeatedly paying for the failing evaluation.
38
+ const CONDITION_ERROR_THROTTLE_NS = 5n * 60n * 1_000_000_000n // 5 minutes
39
+
12
40
  module.exports = {
41
+ CONDITION_ERROR_FLAG,
42
+ CONDITION_ERROR_THROTTLE_NS,
13
43
  DD_TRACE_SYMBOL,
14
44
  MAX_SAMPLED_PROBES_PER_PAUSE,
15
45
  PROBE_SAMPLER_SYMBOL,