dd-trace 6.16.0 → 6.17.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 (334) 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 +165 -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 +72 -12
  42. package/packages/datadog-instrumentations/src/cypress-config.js +0 -23
  43. package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
  44. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
  45. package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -0
  46. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +0 -3
  47. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
  48. package/packages/datadog-instrumentations/src/helpers/register.js +4 -0
  49. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +72 -24
  50. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +39 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -17
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +12 -0
  53. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
  54. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
  55. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +16 -0
  56. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
  57. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +0 -5
  58. package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
  59. package/packages/datadog-instrumentations/src/jest.js +141 -51
  60. package/packages/datadog-instrumentations/src/knex.js +0 -1
  61. package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
  62. package/packages/datadog-instrumentations/src/mariadb.js +0 -2
  63. package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
  64. package/packages/datadog-instrumentations/src/mocha/main.js +22 -25
  65. package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
  66. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
  67. package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
  68. package/packages/datadog-instrumentations/src/mysql2.js +0 -2
  69. package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
  70. package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
  71. package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
  72. package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
  73. package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
  74. package/packages/datadog-instrumentations/src/openai.js +51 -12
  75. package/packages/datadog-instrumentations/src/oracledb.js +9 -3
  76. package/packages/datadog-instrumentations/src/playwright-reporter.js +3 -16
  77. package/packages/datadog-instrumentations/src/playwright.js +238 -55
  78. package/packages/datadog-instrumentations/src/postgres.js +7 -0
  79. package/packages/datadog-instrumentations/src/router.js +0 -2
  80. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
  81. package/packages/datadog-instrumentations/src/sequelize.js +0 -1
  82. package/packages/datadog-instrumentations/src/supabase.js +15 -0
  83. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
  84. package/packages/datadog-instrumentations/src/vitest-main.js +37 -17
  85. package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
  86. package/packages/datadog-instrumentations/src/vitest-worker.js +49 -13
  87. package/packages/datadog-instrumentations/src/webdriverio.js +23 -42
  88. package/packages/datadog-instrumentations/src/ws.js +0 -1
  89. package/packages/datadog-plugin-ai/src/utils.js +0 -2
  90. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
  91. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
  92. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  93. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +0 -1
  94. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
  95. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
  96. package/packages/datadog-plugin-azure-durable-functions/src/index.js +0 -1
  97. package/packages/datadog-plugin-cucumber/src/index.js +10 -0
  98. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +171 -75
  99. package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
  100. package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
  101. package/packages/datadog-plugin-cypress/src/support.js +52 -7
  102. package/packages/datadog-plugin-graphql/src/resolve-error.js +1 -2
  103. package/packages/datadog-plugin-graphql/src/utils.js +0 -5
  104. package/packages/datadog-plugin-jest/src/index.js +13 -0
  105. package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
  106. package/packages/datadog-plugin-mocha/src/index.js +39 -11
  107. package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
  108. package/packages/datadog-plugin-openai/src/index.js +16 -1
  109. package/packages/datadog-plugin-openai/src/realtime.js +144 -0
  110. package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
  111. package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
  112. package/packages/datadog-plugin-playwright/src/index.js +76 -9
  113. package/packages/datadog-plugin-postgres/src/index.js +167 -0
  114. package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
  115. package/packages/datadog-plugin-redis/src/index.js +15 -1
  116. package/packages/datadog-plugin-supabase/src/error.js +18 -0
  117. package/packages/datadog-plugin-supabase/src/index.js +21 -0
  118. package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
  119. package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
  120. package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
  121. package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
  122. package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
  123. package/packages/datadog-plugin-supabase/src/url.js +17 -0
  124. package/packages/datadog-plugin-vitest/src/index.js +10 -0
  125. package/packages/datadog-plugin-ws/src/util.js +0 -4
  126. package/packages/datadog-turbopack/index.js +0 -6
  127. package/packages/datadog-turbopack/src/loader.js +0 -16
  128. package/packages/datadog-webpack/src/loader.js +0 -1
  129. package/packages/dd-trace/src/aiguard/client.js +2 -3
  130. package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
  131. package/packages/dd-trace/src/aiguard/index.js +2 -2
  132. package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
  133. package/packages/dd-trace/src/aiguard/integrations/openai.js +81 -7
  134. package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
  135. package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
  136. package/packages/dd-trace/src/aiguard/messages/utils.js +0 -1
  137. package/packages/dd-trace/src/aiguard/redaction.js +0 -1
  138. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  139. package/packages/dd-trace/src/appsec/activation.js +1 -1
  140. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
  141. package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
  142. package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
  143. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
  144. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  145. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
  146. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
  147. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  148. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
  149. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
  150. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
  151. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
  152. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
  153. package/packages/dd-trace/src/appsec/index.js +2 -2
  154. package/packages/dd-trace/src/appsec/lambda.js +71 -2
  155. package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
  156. package/packages/dd-trace/src/appsec/remote_config.js +8 -6
  157. package/packages/dd-trace/src/appsec/reporter.js +10 -6
  158. package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
  159. package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
  160. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  161. package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
  162. package/packages/dd-trace/src/carrier.js +0 -5
  163. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
  164. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
  165. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
  166. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
  167. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
  168. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
  169. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
  170. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
  171. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
  172. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
  173. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
  174. package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
  175. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
  176. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
  177. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
  178. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
  179. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
  180. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
  181. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
  182. package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
  183. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
  184. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
  185. package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
  186. package/packages/dd-trace/src/config/defaults.js +3 -2
  187. package/packages/dd-trace/src/config/generated-config-types.d.ts +73 -67
  188. package/packages/dd-trace/src/config/helper.js +0 -1
  189. package/packages/dd-trace/src/config/index.js +24 -24
  190. package/packages/dd-trace/src/config/parsers.js +34 -6
  191. package/packages/dd-trace/src/config/remote_config.js +25 -109
  192. package/packages/dd-trace/src/config/supported-configurations.json +139 -10
  193. package/packages/dd-trace/src/datastreams/encoding.js +0 -1
  194. package/packages/dd-trace/src/datastreams/pathway.js +0 -1
  195. package/packages/dd-trace/src/debugger/constants.js +10 -0
  196. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +62 -10
  197. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  198. package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
  199. package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
  200. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
  201. package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
  202. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +21 -8
  203. package/packages/dd-trace/src/debugger/devtools_client/send.js +40 -14
  204. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
  205. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
  206. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
  207. package/packages/dd-trace/src/debugger/devtools_client/status.js +20 -6
  208. package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
  209. package/packages/dd-trace/src/debugger/index.js +50 -2
  210. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
  211. package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
  212. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
  213. package/packages/dd-trace/src/dogstatsd.js +0 -23
  214. package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
  215. package/packages/dd-trace/src/encode/0.4.js +0 -7
  216. package/packages/dd-trace/src/encode/0.5.js +0 -2
  217. package/packages/dd-trace/src/encode/tags-processors.js +0 -1
  218. package/packages/dd-trace/src/evp_proxy/discovery.js +0 -1
  219. package/packages/dd-trace/src/evp_proxy/path.js +0 -2
  220. package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
  221. package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
  222. package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
  223. package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
  224. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
  225. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  226. package/packages/dd-trace/src/exporters/common/url.js +0 -2
  227. package/packages/dd-trace/src/exporters/common/writer.js +0 -2
  228. package/packages/dd-trace/src/id.js +0 -11
  229. package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
  230. package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
  231. package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
  232. package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
  233. package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
  234. package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
  235. package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
  236. package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
  237. package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
  238. package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
  239. package/packages/dd-trace/src/llmobs/index.js +1 -3
  240. package/packages/dd-trace/src/llmobs/noop.js +10 -0
  241. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
  242. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
  243. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
  244. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
  245. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
  246. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
  247. package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
  248. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
  249. package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
  250. package/packages/dd-trace/src/llmobs/prompts/manager.js +639 -0
  251. package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
  252. package/packages/dd-trace/src/llmobs/prompts/prompt.js +92 -0
  253. package/packages/dd-trace/src/llmobs/sdk.js +21 -6
  254. package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
  255. package/packages/dd-trace/src/llmobs/tagger.js +30 -7
  256. package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
  257. package/packages/dd-trace/src/llmobs/util.js +0 -34
  258. package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
  259. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  260. package/packages/dd-trace/src/log/channels.js +3 -0
  261. package/packages/dd-trace/src/log/index.js +23 -5
  262. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
  263. package/packages/dd-trace/src/msgpack/chunk.js +0 -1
  264. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
  265. package/packages/dd-trace/src/openfeature/encoding.js +0 -2
  266. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
  267. package/packages/dd-trace/src/openfeature/index.js +0 -4
  268. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
  269. package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
  270. package/packages/dd-trace/src/openfeature/writers/base.js +0 -6
  271. package/packages/dd-trace/src/openfeature/writers/exposures.js +0 -5
  272. package/packages/dd-trace/src/openfeature/writers/util.js +0 -4
  273. package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
  274. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
  275. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
  276. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
  277. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +0 -1
  278. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
  279. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
  280. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -14
  281. package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
  282. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +30 -13
  283. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
  284. package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
  285. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
  286. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
  287. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
  288. package/packages/dd-trace/src/opentracing/span_context.js +0 -1
  289. package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
  290. package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
  291. package/packages/dd-trace/src/plugins/ci_plugin.js +0 -1
  292. package/packages/dd-trace/src/plugins/database.js +2 -6
  293. package/packages/dd-trace/src/plugins/index.js +7 -0
  294. package/packages/dd-trace/src/plugins/plugin.js +0 -4
  295. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  296. package/packages/dd-trace/src/plugins/util/git.js +1 -3
  297. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
  298. package/packages/dd-trace/src/plugins/util/llm.js +0 -2
  299. package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
  300. package/packages/dd-trace/src/plugins/util/test.js +21 -20
  301. package/packages/dd-trace/src/plugins/util/url.js +0 -5
  302. package/packages/dd-trace/src/priority_sampler.js +0 -6
  303. package/packages/dd-trace/src/process-tags/index.js +0 -1
  304. package/packages/dd-trace/src/profiler.js +132 -35
  305. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
  306. package/packages/dd-trace/src/profiling/profiler.js +101 -41
  307. package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
  308. package/packages/dd-trace/src/propagation-hash/index.js +0 -1
  309. package/packages/dd-trace/src/proxy.js +21 -45
  310. package/packages/dd-trace/src/random_sampler.js +0 -4
  311. package/packages/dd-trace/src/rate_limiter.js +0 -4
  312. package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
  313. package/packages/dd-trace/src/remote_config/index.js +0 -1
  314. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
  315. package/packages/dd-trace/src/sampler.js +0 -4
  316. package/packages/dd-trace/src/sampling_rule.js +0 -7
  317. package/packages/dd-trace/src/serverless/vercel.js +0 -1
  318. package/packages/dd-trace/src/serverless.js +0 -1
  319. package/packages/dd-trace/src/service-naming/index.js +0 -1
  320. package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
  321. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
  322. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
  323. package/packages/dd-trace/src/span_processor.js +2 -2
  324. package/packages/dd-trace/src/span_sampler.js +0 -1
  325. package/packages/dd-trace/src/standalone/product.js +2 -2
  326. package/packages/dd-trace/src/standalone/tracesource.js +0 -1
  327. package/packages/dd-trace/src/startup-log.js +1 -1
  328. package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
  329. package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
  330. package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
  331. package/packages/dd-trace/src/telemetry/metrics.js +0 -2
  332. package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
  333. package/packages/dd-trace/src/util.js +13 -1
  334. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
@@ -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
  }
@@ -5,17 +5,18 @@ const { stringify } = require('querystring')
5
5
 
6
6
  const { version } = require('../../../../../package.json')
7
7
  const request = require('../../exporters/common/request')
8
- const { DEBUGGER_DIAGNOSTICS_V1, DEBUGGER_INPUT_V2 } = require('../constants')
8
+ const { DEBUGGER_DIAGNOSTICS_V1, DEBUGGER_INPUT_V2, MAX_MESSAGE_LENGTH } = require('../constants')
9
+ const { DROPPED_REASON, INCOMPLETE_REASON } = require('../guardrail-metrics')
9
10
  const log = require('./log')
10
11
  const JSONBuffer = require('./json-buffer')
11
12
  const config = require('./config')
13
+ const guardrailMetrics = require('./guardrail-metrics')
12
14
  const getRequestOptions = require('./request-options')
13
15
  const { pruneSnapshot } = require('./snapshot-pruner')
14
16
  const buildTags = require('./tags')
15
17
 
16
18
  module.exports = send
17
19
 
18
- const MAX_MESSAGE_LENGTH = 8 * 1024 // 8KB
19
20
  const MAX_LOG_PAYLOAD_SIZE_MB = 1
20
21
  const MAX_LOG_PAYLOAD_SIZE_BYTES = MAX_LOG_PAYLOAD_SIZE_MB * 1024 * 1024
21
22
 
@@ -30,18 +31,33 @@ setInputPath(config.inputPath)
30
31
 
31
32
  const jsonBuffer = new JSONBuffer({
32
33
  size: config.maxTotalPayloadSize,
34
+ maxQueueBytes: config.queueMaxBytes,
33
35
  timeout: config.dynamicInstrumentation.uploadIntervalSeconds * 1000,
34
36
  onFlush,
35
37
  })
36
38
 
37
- function send (message, logger, dd, snapshot, processTags) {
39
+ /**
40
+ * Queue a probe result for upload.
41
+ *
42
+ * @param {string} message - The evaluated log message
43
+ * @param {object} logger - The logger metadata
44
+ * @param {object | undefined} dd - The trace and span ids of the active trace, if any
45
+ * @param {object} snapshot - The snapshot payload
46
+ * @param {string | undefined} processTags - The serialized process tags, if enabled
47
+ * @param {number} eventType - The guardrail event type, one of `EVENT_TYPE`
48
+ * @param {number} incompleteReasons - Bitmask of `INCOMPLETE_REASON` flags enforced while capturing the snapshot
49
+ */
50
+ function send (message, logger, dd, snapshot, processTags, eventType, incompleteReasons) {
51
+ if (message?.length > MAX_MESSAGE_LENGTH) {
52
+ message = message.slice(0, MAX_MESSAGE_LENGTH) + '…'
53
+ incompleteReasons |= INCOMPLETE_REASON.STRING_LENGTH
54
+ }
55
+
38
56
  const payload = {
39
57
  ddsource,
40
58
  hostname,
41
59
  service,
42
- message: message?.length > MAX_MESSAGE_LENGTH
43
- ? message.slice(0, MAX_MESSAGE_LENGTH) + '…'
44
- : message,
60
+ message,
45
61
  logger,
46
62
  dd,
47
63
  process_tags: processTags,
@@ -52,6 +68,7 @@ function send (message, logger, dd, snapshot, processTags) {
52
68
  let size = Buffer.byteLength(json)
53
69
 
54
70
  if (size > MAX_LOG_PAYLOAD_SIZE_BYTES) {
71
+ incompleteReasons |= INCOMPLETE_REASON.PAYLOAD_TOO_LARGE
55
72
  let pruned
56
73
  try {
57
74
  pruned = pruneSnapshot(json, size, MAX_LOG_PAYLOAD_SIZE_BYTES)
@@ -61,7 +78,7 @@ function send (message, logger, dd, snapshot, processTags) {
61
78
 
62
79
  if (pruned) {
63
80
  json = pruned
64
- } else {
81
+ } else if (snapshot.captures !== undefined) {
65
82
  // Fallback if pruning fails
66
83
  const line = Object.keys(snapshot.captures.lines)[0]
67
84
  snapshot.captures.lines[line] = { pruned: true }
@@ -70,28 +87,36 @@ function send (message, logger, dd, snapshot, processTags) {
70
87
  size = Buffer.byteLength(json)
71
88
  }
72
89
 
73
- jsonBuffer.write(json, size)
90
+ if (jsonBuffer.write(json, size)) {
91
+ if (incompleteReasons !== 0) guardrailMetrics.captureIncomplete(incompleteReasons, eventType)
92
+ } else {
93
+ // The upload queue is full, typically because the intake is slow or unreachable. Dropping is the only option that
94
+ // keeps memory bounded without blocking.
95
+ log.debug('[debugger:devtools_client] Dropping probe result for probe %s: upload queue is full', snapshot.probe.id)
96
+ guardrailMetrics.eventDropped(DROPPED_REASON.QUEUE_FULL, eventType)
97
+ }
74
98
  }
75
99
 
76
100
  /**
77
101
  * @param {string} payload - The payload to send
102
+ * @param {() => void} done - Releases the payload from the upload queue
78
103
  */
79
- function onFlush (payload) {
104
+ function onFlush (payload, done) {
80
105
  log.debug('[debugger:devtools_client] Flushing probe payload buffer')
81
106
 
82
107
  request(payload, buildRequestOptions(), (err, res, statusCode) => {
83
- if (!handleV2FallbackIfNeeded(statusCode, payload) && err) {
84
- log.error('[debugger:devtools_client] Error sending probe payload', err)
85
- }
108
+ if (handleV2FallbackIfNeeded(statusCode, payload, done)) return
109
+ done()
110
+ if (err) log.error('[debugger:devtools_client] Error sending probe payload', err)
86
111
  })
87
112
  }
88
113
 
89
114
  /**
90
115
  * @param {number} statusCode - The status code of the response
91
116
  * @param {string} payload - The payload to send
92
- * @returns {boolean} True if the fallback was needed, false otherwise
117
+ * @param {() => void} done - Releases the payload from the upload queue
93
118
  */
94
- function handleV2FallbackIfNeeded (statusCode, payload) {
119
+ function handleV2FallbackIfNeeded (statusCode, payload, done) {
95
120
  if (statusCode !== 404 || config.inputPath !== DEBUGGER_INPUT_V2) {
96
121
  return false
97
122
  }
@@ -103,6 +128,7 @@ function handleV2FallbackIfNeeded (statusCode, payload) {
103
128
  setInputPath(DEBUGGER_DIAGNOSTICS_V1)
104
129
 
105
130
  request(payload, buildRequestOptions(), (err) => {
131
+ done()
106
132
  if (err) {
107
133
  log.error('[debugger:devtools_client] Error sending probe payload after fallback to %s',
108
134
  DEBUGGER_DIAGNOSTICS_V1,