dd-trace 6.15.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 (378) hide show
  1. package/README.md +15 -0
  2. package/ci/diagnose.js +0 -23
  3. package/ci/test-optimization-validation/approval-artifacts.js +0 -3
  4. package/ci/test-optimization-validation/approval.js +0 -6
  5. package/ci/test-optimization-validation/artifact-id.js +0 -1
  6. package/ci/test-optimization-validation/bounded-json.js +0 -1
  7. package/ci/test-optimization-validation/cli.js +0 -12
  8. package/ci/test-optimization-validation/command-output-policy.js +0 -1
  9. package/ci/test-optimization-validation/command-runner.js +0 -3
  10. package/ci/test-optimization-validation/environment.js +0 -4
  11. package/ci/test-optimization-validation/executable-approval.js +0 -1
  12. package/ci/test-optimization-validation/executable.js +0 -5
  13. package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -8
  14. package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
  15. package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
  16. package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
  17. package/ci/test-optimization-validation/generated-test-contract.js +0 -5
  18. package/ci/test-optimization-validation/generated-verifier.js +0 -1
  19. package/ci/test-optimization-validation/literal-environment.js +0 -1
  20. package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
  21. package/ci/test-optimization-validation/manifest-schema.js +0 -21
  22. package/ci/test-optimization-validation/offline-fixtures.js +0 -4
  23. package/ci/test-optimization-validation/package-check.js +4 -1
  24. package/ci/test-optimization-validation/plan-writer.js +0 -11
  25. package/ci/test-optimization-validation/preflight-runner.js +0 -1
  26. package/ci/test-optimization-validation/redaction.js +0 -4
  27. package/ci/test-optimization-validation/report-writer.js +0 -16
  28. package/ci/test-optimization-validation/runner-command.js +0 -3
  29. package/ci/test-optimization-validation/runner-contract.js +0 -4
  30. package/ci/test-optimization-validation/safe-files.js +0 -1
  31. package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
  32. package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -2
  33. package/ci/test-optimization-validation/source-text.js +0 -3
  34. package/ci/vitest-no-worker-init-setup.mjs +41 -16
  35. package/index.d.ts +169 -0
  36. package/next.d.ts +27 -0
  37. package/next.js +3 -0
  38. package/package.json +28 -23
  39. package/packages/datadog-esbuild/index.js +14 -1
  40. package/packages/datadog-esbuild/src/resolver.js +284 -0
  41. package/packages/datadog-esbuild/src/utils.js +162 -120
  42. package/packages/datadog-instrumentations/src/aerospike.js +28 -6
  43. package/packages/datadog-instrumentations/src/ai.js +39 -97
  44. package/packages/datadog-instrumentations/src/anthropic.js +76 -134
  45. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -3
  46. package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
  47. package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
  48. package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
  49. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  50. package/packages/datadog-instrumentations/src/bullmq.js +1 -1
  51. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -3
  52. package/packages/datadog-instrumentations/src/console.js +97 -0
  53. package/packages/datadog-instrumentations/src/cucumber.js +72 -12
  54. package/packages/datadog-instrumentations/src/cypress-config.js +0 -23
  55. package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
  56. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  57. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
  58. package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
  59. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
  60. package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -0
  61. package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
  62. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +69 -0
  63. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
  64. package/packages/datadog-instrumentations/src/helpers/register.js +13 -64
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +109 -29
  66. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +39 -0
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
  68. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -16
  69. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +12 -0
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +17 -0
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -75
  76. package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
  77. package/packages/datadog-instrumentations/src/jest.js +141 -51
  78. package/packages/datadog-instrumentations/src/knex.js +0 -1
  79. package/packages/datadog-instrumentations/src/langchain.js +1 -1
  80. package/packages/datadog-instrumentations/src/langgraph.js +1 -1
  81. package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
  82. package/packages/datadog-instrumentations/src/mariadb.js +0 -2
  83. package/packages/datadog-instrumentations/src/mercurius.js +1 -1
  84. package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
  85. package/packages/datadog-instrumentations/src/mocha/main.js +22 -25
  86. package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
  87. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
  88. package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
  89. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
  90. package/packages/datadog-instrumentations/src/mysql2.js +0 -2
  91. package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
  92. package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
  93. package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
  94. package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
  95. package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
  96. package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
  97. package/packages/datadog-instrumentations/src/openai.js +97 -80
  98. package/packages/datadog-instrumentations/src/oracledb.js +9 -3
  99. package/packages/datadog-instrumentations/src/playwright-reporter.js +3 -16
  100. package/packages/datadog-instrumentations/src/playwright.js +238 -55
  101. package/packages/datadog-instrumentations/src/postgres.js +7 -0
  102. package/packages/datadog-instrumentations/src/router.js +0 -2
  103. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
  104. package/packages/datadog-instrumentations/src/sequelize.js +0 -1
  105. package/packages/datadog-instrumentations/src/supabase.js +15 -0
  106. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
  107. package/packages/datadog-instrumentations/src/vitest-main.js +37 -17
  108. package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
  109. package/packages/datadog-instrumentations/src/vitest-worker.js +56 -23
  110. package/packages/datadog-instrumentations/src/webdriverio.js +23 -42
  111. package/packages/datadog-instrumentations/src/ws.js +0 -1
  112. package/packages/datadog-plugin-ai/src/utils.js +0 -2
  113. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
  114. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
  115. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  116. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -2
  117. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
  118. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
  119. package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
  120. package/packages/datadog-plugin-azure-durable-functions/src/index.js +95 -1
  121. package/packages/datadog-plugin-cucumber/src/index.js +10 -0
  122. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +171 -75
  123. package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
  124. package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
  125. package/packages/datadog-plugin-cypress/src/support.js +52 -7
  126. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
  127. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
  128. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
  129. package/packages/datadog-plugin-graphql/src/resolve-error.js +32 -11
  130. package/packages/datadog-plugin-graphql/src/utils.js +0 -5
  131. package/packages/datadog-plugin-jest/src/index.js +13 -0
  132. package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
  133. package/packages/datadog-plugin-mocha/src/index.js +39 -11
  134. package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
  135. package/packages/datadog-plugin-openai/src/index.js +16 -1
  136. package/packages/datadog-plugin-openai/src/realtime.js +144 -0
  137. package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
  138. package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
  139. package/packages/datadog-plugin-playwright/src/index.js +76 -9
  140. package/packages/datadog-plugin-postgres/src/index.js +167 -0
  141. package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
  142. package/packages/datadog-plugin-redis/src/index.js +15 -1
  143. package/packages/datadog-plugin-supabase/src/error.js +18 -0
  144. package/packages/datadog-plugin-supabase/src/index.js +21 -0
  145. package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
  146. package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
  147. package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
  148. package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
  149. package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
  150. package/packages/datadog-plugin-supabase/src/url.js +17 -0
  151. package/packages/datadog-plugin-vitest/src/index.js +10 -0
  152. package/packages/datadog-plugin-ws/src/util.js +0 -4
  153. package/packages/datadog-turbopack/index.js +206 -0
  154. package/packages/datadog-turbopack/src/constants.js +5 -0
  155. package/packages/datadog-turbopack/src/loader.js +484 -0
  156. package/packages/datadog-webpack/src/loader.js +0 -1
  157. package/packages/dd-trace/src/aiguard/client.js +2 -3
  158. package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
  159. package/packages/dd-trace/src/aiguard/index.js +3 -2
  160. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
  161. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
  162. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -3
  163. package/packages/dd-trace/src/aiguard/integrations/openai.js +132 -29
  164. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
  165. package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
  166. package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
  167. package/packages/dd-trace/src/aiguard/messages/utils.js +29 -2
  168. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
  169. package/packages/dd-trace/src/aiguard/redaction.js +0 -1
  170. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  171. package/packages/dd-trace/src/appsec/activation.js +1 -1
  172. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
  173. package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
  174. package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
  175. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
  176. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  177. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
  178. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
  179. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  180. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
  181. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
  182. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
  183. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
  184. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
  185. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
  186. package/packages/dd-trace/src/appsec/index.js +2 -2
  187. package/packages/dd-trace/src/appsec/lambda.js +71 -2
  188. package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
  189. package/packages/dd-trace/src/appsec/remote_config.js +8 -6
  190. package/packages/dd-trace/src/appsec/reporter.js +10 -6
  191. package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
  192. package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
  193. package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
  194. package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
  195. package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
  196. package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
  197. package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
  198. package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
  199. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  200. package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
  201. package/packages/dd-trace/src/carrier.js +0 -5
  202. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
  203. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
  204. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
  205. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
  206. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
  207. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
  208. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
  209. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
  210. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
  211. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
  212. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
  213. package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
  214. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
  215. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
  216. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
  217. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
  218. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
  219. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
  220. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
  221. package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
  222. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
  223. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
  224. package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
  225. package/packages/dd-trace/src/config/defaults.js +3 -2
  226. package/packages/dd-trace/src/config/generated-config-types.d.ts +74 -66
  227. package/packages/dd-trace/src/config/helper.js +9 -2
  228. package/packages/dd-trace/src/config/index.js +44 -29
  229. package/packages/dd-trace/src/config/parsers.js +34 -6
  230. package/packages/dd-trace/src/config/remote_config.js +25 -109
  231. package/packages/dd-trace/src/config/supported-configurations.json +146 -9
  232. package/packages/dd-trace/src/datastreams/encoding.js +0 -1
  233. package/packages/dd-trace/src/datastreams/pathway.js +0 -1
  234. package/packages/dd-trace/src/debugger/constants.js +10 -0
  235. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +76 -17
  236. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  237. package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
  238. package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
  239. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
  240. package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
  241. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +34 -8
  242. package/packages/dd-trace/src/debugger/devtools_client/send.js +40 -14
  243. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
  244. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
  245. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
  246. package/packages/dd-trace/src/debugger/devtools_client/status.js +20 -6
  247. package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
  248. package/packages/dd-trace/src/debugger/index.js +50 -2
  249. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
  250. package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
  251. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
  252. package/packages/dd-trace/src/dogstatsd.js +0 -23
  253. package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
  254. package/packages/dd-trace/src/encode/0.4.js +0 -7
  255. package/packages/dd-trace/src/encode/0.5.js +0 -2
  256. package/packages/dd-trace/src/encode/tags-processors.js +0 -1
  257. package/packages/dd-trace/src/evp_proxy/discovery.js +3 -5
  258. package/packages/dd-trace/src/evp_proxy/path.js +11 -4
  259. package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
  260. package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
  261. package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
  262. package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
  263. package/packages/dd-trace/src/exporters/common/docker.js +4 -1
  264. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
  265. package/packages/dd-trace/src/exporters/common/proxy.js +3 -0
  266. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  267. package/packages/dd-trace/src/exporters/common/url.js +0 -2
  268. package/packages/dd-trace/src/exporters/common/writer.js +0 -2
  269. package/packages/dd-trace/src/id.js +0 -11
  270. package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
  271. package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
  272. package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
  273. package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
  274. package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
  275. package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
  276. package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
  277. package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
  278. package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
  279. package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
  280. package/packages/dd-trace/src/llmobs/index.js +1 -3
  281. package/packages/dd-trace/src/llmobs/noop.js +10 -0
  282. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
  283. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
  284. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
  285. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
  286. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
  287. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
  288. package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
  289. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
  290. package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
  291. package/packages/dd-trace/src/llmobs/prompts/manager.js +639 -0
  292. package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
  293. package/packages/dd-trace/src/llmobs/prompts/prompt.js +92 -0
  294. package/packages/dd-trace/src/llmobs/sdk.js +21 -6
  295. package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
  296. package/packages/dd-trace/src/llmobs/tagger.js +30 -7
  297. package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
  298. package/packages/dd-trace/src/llmobs/util.js +0 -34
  299. package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
  300. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  301. package/packages/dd-trace/src/log/channels.js +3 -0
  302. package/packages/dd-trace/src/log/index.js +23 -5
  303. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
  304. package/packages/dd-trace/src/msgpack/chunk.js +0 -1
  305. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
  306. package/packages/dd-trace/src/openfeature/encoding.js +0 -2
  307. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
  308. package/packages/dd-trace/src/openfeature/index.js +0 -4
  309. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
  310. package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
  311. package/packages/dd-trace/src/openfeature/writers/base.js +0 -6
  312. package/packages/dd-trace/src/openfeature/writers/exposures.js +0 -5
  313. package/packages/dd-trace/src/openfeature/writers/util.js +0 -4
  314. package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
  315. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
  316. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
  317. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
  318. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  319. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +4 -4
  320. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
  321. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
  322. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +103 -35
  323. package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
  324. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +36 -13
  325. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
  326. package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
  327. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
  328. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
  329. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
  330. package/packages/dd-trace/src/opentracing/span_context.js +0 -1
  331. package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
  332. package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
  333. package/packages/dd-trace/src/plugin_manager.js +9 -3
  334. package/packages/dd-trace/src/plugins/ci_plugin.js +0 -1
  335. package/packages/dd-trace/src/plugins/database.js +2 -6
  336. package/packages/dd-trace/src/plugins/index.js +8 -0
  337. package/packages/dd-trace/src/plugins/plugin.js +0 -4
  338. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  339. package/packages/dd-trace/src/plugins/util/git.js +1 -3
  340. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
  341. package/packages/dd-trace/src/plugins/util/llm.js +0 -2
  342. package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
  343. package/packages/dd-trace/src/plugins/util/test.js +25 -21
  344. package/packages/dd-trace/src/plugins/util/url.js +0 -5
  345. package/packages/dd-trace/src/priority_sampler.js +0 -6
  346. package/packages/dd-trace/src/process-tags/index.js +0 -1
  347. package/packages/dd-trace/src/profiler.js +132 -35
  348. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
  349. package/packages/dd-trace/src/profiling/profiler.js +101 -41
  350. package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
  351. package/packages/dd-trace/src/propagation-hash/index.js +0 -1
  352. package/packages/dd-trace/src/proxy.js +21 -45
  353. package/packages/dd-trace/src/random_sampler.js +0 -4
  354. package/packages/dd-trace/src/rate_limiter.js +0 -4
  355. package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
  356. package/packages/dd-trace/src/remote_config/index.js +0 -1
  357. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
  358. package/packages/dd-trace/src/sampler.js +0 -4
  359. package/packages/dd-trace/src/sampling_rule.js +0 -7
  360. package/packages/dd-trace/src/serverless/vercel.js +0 -1
  361. package/packages/dd-trace/src/serverless.js +0 -1
  362. package/packages/dd-trace/src/service-naming/index.js +0 -1
  363. package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
  364. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
  365. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
  366. package/packages/dd-trace/src/span_processor.js +2 -2
  367. package/packages/dd-trace/src/span_sampler.js +0 -1
  368. package/packages/dd-trace/src/standalone/product.js +2 -2
  369. package/packages/dd-trace/src/standalone/tracesource.js +0 -1
  370. package/packages/dd-trace/src/startup-log.js +1 -1
  371. package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
  372. package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
  373. package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
  374. package/packages/dd-trace/src/telemetry/metrics.js +0 -2
  375. package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
  376. package/packages/dd-trace/src/util.js +13 -1
  377. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  378. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { INCOMPLETE_REASON } = require('../../guardrail-metrics')
3
4
  const { LARGE_OBJECT_SKIP_THRESHOLD } = require('./constants')
4
5
  const { collectionSizeSym, largeCollectionSkipThresholdSym, fieldCountSym, timeBudgetSym } = require('./symbols')
5
6
  const { normalizeName, REDACTED_IDENTIFIERS } = require('./redaction')
@@ -15,15 +16,24 @@ module.exports = {
15
16
  * @typedef {import('inspector').Runtime.RemoteObject & { properties?: object[] }} RemoteObjectWithProperties
16
17
  */
17
18
 
19
+ /**
20
+ * Accumulates which capture limits were enforced while processing a single event, so they can be reported once per
21
+ * event instead of once per affected node.
22
+ *
23
+ * @typedef {object} IncompleteCapture
24
+ * @property {number} reasons - Bitmask of {@link INCOMPLETE_REASON} flags
25
+ */
26
+
18
27
  /**
19
28
  * Process a RemoteObject into the snapshot format.
20
29
  *
21
30
  * @param {RemoteObjectWithProperties} remoteObject
22
31
  * @param {number} maxLength - Maximum string length
32
+ * @param {IncompleteCapture} incomplete - Receives the capture limits enforced on the object
23
33
  * @returns {object} The processed value in snapshot format
24
34
  */
25
- function processRemoteObject (remoteObject, maxLength) {
26
- return getPropertyValueRaw({ value: remoteObject }, maxLength)
35
+ function processRemoteObject (remoteObject, maxLength, incomplete) {
36
+ return getPropertyValueRaw({ value: remoteObject }, maxLength, incomplete)
27
37
  }
28
38
 
29
39
  // Matches classes in source code, no matter how it's written:
@@ -33,7 +43,15 @@ function processRemoteObject (remoteObject, maxLength) {
33
43
  // - Anonymous, with odd whitespace: class\n{}
34
44
  const CLASS_REGEX = /^class\s([^{]*)/
35
45
 
36
- function processProperties (props, maxLength) {
46
+ /**
47
+ * Process the collected properties of an object into the snapshot format.
48
+ *
49
+ * @param {object[]} props - The collected property descriptors
50
+ * @param {number} maxLength - Maximum string length
51
+ * @param {IncompleteCapture} incomplete - Receives the capture limits enforced on the properties
52
+ * @returns {Record<string, object>} The processed properties in snapshot format
53
+ */
54
+ function processProperties (props, maxLength, incomplete) {
37
55
  const result = {}
38
56
 
39
57
  for (const prop of props) {
@@ -42,20 +60,26 @@ function processProperties (props, maxLength) {
42
60
  if (name.includes('.')) {
43
61
  name = name.replaceAll('.', '_')
44
62
  }
45
- result[name] = getPropertyValue(prop, maxLength)
63
+ result[name] = getPropertyValue(prop, maxLength, incomplete)
46
64
  }
47
65
 
48
66
  return result
49
67
  }
50
68
 
69
+ // Capture limits enforced while processing a value that ends up redacted never make it into the snapshot, so they are
70
+ // not recorded as incomplete capture reasons either
71
+ const discardedIncomplete = { reasons: 0 }
72
+
51
73
  // TODO: Improve performance of redaction algorithm.
52
74
  // This algorithm is probably slower than if we embedded the redaction logic inside the functions below.
53
75
  // That way we didn't have to traverse objects that will just be redacted anyway.
54
- function getPropertyValue (prop, maxLength) {
55
- return redact(prop, getPropertyValueRaw(prop, maxLength))
76
+ function getPropertyValue (prop, maxLength, incomplete) {
77
+ return shouldRedactProperty(prop)
78
+ ? notCapturedRedacted(getPropertyValueRaw(prop, maxLength, discardedIncomplete).type)
79
+ : getPropertyValueRaw(prop, maxLength, incomplete)
56
80
  }
57
81
 
58
- function getPropertyValueRaw (prop, maxLength) {
82
+ function getPropertyValueRaw (prop, maxLength, incomplete) {
59
83
  // Special case for getters and setters which does not have a value property
60
84
  if (prop.get) {
61
85
  const hasGet = prop.get.type !== 'undefined'
@@ -69,15 +93,15 @@ function getPropertyValueRaw (prop, maxLength) {
69
93
 
70
94
  switch (prop.value?.type) {
71
95
  case 'object':
72
- return getObjectValue(prop.value, maxLength)
96
+ return getObjectValue(prop.value, maxLength, incomplete)
73
97
  case 'string':
74
- return toString(prop.value.value, maxLength)
98
+ return toString(prop.value.value, maxLength, incomplete)
75
99
  case 'number':
76
100
  return { type: 'number', value: prop.value.description } // use `description` to get it as string
77
101
  case 'boolean':
78
102
  return { type: 'boolean', value: prop.value.value === true ? 'true' : 'false' }
79
103
  case 'function':
80
- return toFunctionOrClass(prop.value, maxLength)
104
+ return toFunctionOrClass(prop.value, maxLength, incomplete)
81
105
  case undefined: // TODO: Add test for when a prop has no value. I think it's if it's defined after the breakpoint?
82
106
  case 'undefined':
83
107
  return { type: 'undefined' }
@@ -88,18 +112,19 @@ function getPropertyValueRaw (prop, maxLength) {
88
112
  default:
89
113
  // As of this writing, the Chrome DevTools Protocol doesn't allow any other types than the ones listed above, but
90
114
  // in the future new ones might be added.
115
+ incomplete.reasons |= INCOMPLETE_REASON.OTHER
91
116
  return { type: prop.value.type, notCapturedReason: 'Unsupported property type' }
92
117
  }
93
118
  }
94
119
 
95
- function getObjectValue (obj, maxLength) {
120
+ function getObjectValue (obj, maxLength, incomplete) {
96
121
  const timeBudgetReached = obj[timeBudgetSym] === true
97
122
 
98
123
  switch (obj.subtype) {
99
124
  case undefined:
100
- return toObject(obj.className, obj.properties, maxLength, timeBudgetReached)
125
+ return toObject(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
101
126
  case 'array':
102
- return toArray(obj.className, obj.properties, maxLength, timeBudgetReached)
127
+ return toArray(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
103
128
  case 'null':
104
129
  return { type: 'null', isNull: true }
105
130
  // case 'node': // TODO: What does this subtype represent?
@@ -113,28 +138,28 @@ function getObjectValue (obj, maxLength) {
113
138
  return { type: obj.className, value }
114
139
  }
115
140
  case 'map':
116
- return toMap(obj.className, obj.properties, maxLength, timeBudgetReached)
141
+ return toMap(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
117
142
  case 'set':
118
- return toSet(obj.className, obj.properties, maxLength, timeBudgetReached)
143
+ return toSet(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
119
144
  case 'error':
120
145
  // TODO: Convert stack trace to array to avoid string truncation or disable truncation in this case?
121
- return toObject(obj.className, obj.properties, maxLength, timeBudgetReached)
146
+ return toObject(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
122
147
  case 'proxy':
123
148
  // Use `description` instead of `className` as the `type` to get type of target object (`Proxy(Error)` vs `proxy`)
124
- return toObject(obj.description, obj.properties, maxLength, timeBudgetReached)
149
+ return toObject(obj.description, obj.properties, maxLength, timeBudgetReached, incomplete)
125
150
  case 'promise':
126
- return toObject(obj.className, obj.properties, maxLength, timeBudgetReached)
151
+ return toObject(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
127
152
  case 'typedarray':
128
- return toArray(obj.className, obj.properties, maxLength, timeBudgetReached)
153
+ return toArray(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
129
154
  case 'generator':
130
155
  // Use `subtype` instead of `className` to make it obvious it's a generator
131
- return toObject(obj.subtype, obj.properties, maxLength, timeBudgetReached)
156
+ return toObject(obj.subtype, obj.properties, maxLength, timeBudgetReached, incomplete)
132
157
  case 'arraybuffer':
133
- return toArrayBuffer(obj.className, obj.properties, maxLength, timeBudgetReached)
158
+ return toArrayBuffer(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
134
159
  case 'weakmap':
135
- return toMap(obj.className, obj.properties, maxLength, timeBudgetReached)
160
+ return toMap(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
136
161
  case 'weakset':
137
- return toSet(obj.className, obj.properties, maxLength, timeBudgetReached)
162
+ return toSet(obj.className, obj.properties, maxLength, timeBudgetReached, incomplete)
138
163
  // case 'iterator': // TODO: I've not been able to trigger this subtype
139
164
  // case 'dataview': // TODO: Looks like the internal ArrayBuffer is only accessible via the `buffer` getter
140
165
  // case 'webassemblymemory': // TODO: Looks like the internal ArrayBuffer is only accessible via the `buffer` getter
@@ -142,31 +167,34 @@ function getObjectValue (obj, maxLength) {
142
167
  default:
143
168
  // As of this writing, the Chrome DevTools Protocol doesn't allow any other subtypes than the ones listed above,
144
169
  // but in the future new ones might be added.
170
+ incomplete.reasons |= INCOMPLETE_REASON.OTHER
145
171
  return { type: obj.subtype, notCapturedReason: 'Unsupported object type' }
146
172
  }
147
173
  }
148
174
 
149
- function toFunctionOrClass (value, maxLength) {
175
+ function toFunctionOrClass (value, maxLength, incomplete) {
150
176
  const classMatch = value.description.match(CLASS_REGEX)
151
177
 
152
178
  if (classMatch === null) {
153
179
  // This is a function
154
180
  const timeBudgetReached = value[timeBudgetSym] === true
155
181
  // TODO: Would it make sense to detect if it's an arrow function or not?
156
- return toObject(value.className, value.properties, maxLength, timeBudgetReached)
182
+ return toObject(value.className, value.properties, maxLength, timeBudgetReached, incomplete)
157
183
  }
158
184
  // This is a class
159
185
  const className = classMatch[1].trim()
160
186
  return { type: className ? `class ${className}` : 'class' }
161
187
  }
162
188
 
163
- function toString (str, maxLength) {
189
+ function toString (str, maxLength, incomplete) {
164
190
  const size = str.length
165
191
 
166
192
  if (size <= maxLength) {
167
193
  return { type: 'string', value: str }
168
194
  }
169
195
 
196
+ incomplete.reasons |= INCOMPLETE_REASON.STRING_LENGTH
197
+
170
198
  return {
171
199
  type: 'string',
172
200
  value: str.slice(0, maxLength),
@@ -175,16 +203,17 @@ function toString (str, maxLength) {
175
203
  }
176
204
  }
177
205
 
178
- function toObject (type, props, maxLength, timeBudgetReached) {
179
- if (timeBudgetReached === true) return notCapturedTimeBudget(type)
180
- if (props === undefined) return notCapturedDepth(type)
206
+ function toObject (type, props, maxLength, timeBudgetReached, incomplete) {
207
+ if (timeBudgetReached === true) return notCapturedTimeBudget(type, incomplete)
208
+ if (props === undefined) return notCapturedDepth(type, incomplete)
181
209
 
182
210
  const result = {
183
211
  type,
184
- fields: processProperties(props, maxLength),
212
+ fields: processProperties(props, maxLength, incomplete),
185
213
  }
186
214
 
187
215
  if (props[fieldCountSym] !== undefined) {
216
+ incomplete.reasons |= INCOMPLETE_REASON.FIELD_COUNT
188
217
  result.notCapturedReason = 'fieldCount'
189
218
  result.size = props[fieldCountSym]
190
219
  }
@@ -192,27 +221,29 @@ function toObject (type, props, maxLength, timeBudgetReached) {
192
221
  return result
193
222
  }
194
223
 
195
- function toArray (type, elements, maxLength, timeBudgetReached) {
196
- if (timeBudgetReached === true) return notCapturedTimeBudget(type)
197
- if (elements === undefined) return notCapturedDepth(type)
224
+ function toArray (type, elements, maxLength, timeBudgetReached, incomplete) {
225
+ if (timeBudgetReached === true) return notCapturedTimeBudget(type, incomplete)
226
+ if (elements === undefined) return notCapturedDepth(type, incomplete)
198
227
 
199
228
  const result = {
200
229
  type,
201
230
  elements: elements.map((element) => {
202
- return getPropertyValue(element, maxLength)
231
+ return getPropertyValue(element, maxLength, incomplete)
203
232
  }),
204
233
  }
205
234
 
206
- setNotCaptureReasonOnCollection(result, elements)
207
- setNotCaptureReasonOnTooLargeCollection(result, elements)
235
+ setNotCaptureReasonOnCollection(result, elements, incomplete)
236
+ setNotCaptureReasonOnTooLargeCollection(result, elements, incomplete)
208
237
 
209
238
  return result
210
239
  }
211
240
 
212
- function toMap (type, pairs, maxLength, timeBudgetReached) {
213
- if (timeBudgetReached === true) return notCapturedTimeBudget(type)
214
- if (pairs === undefined) return notCapturedDepth(type)
215
- if (pairs.length > 0 && pairs.every(({ value }) => value[timeBudgetSym] === true)) return notCapturedTimeBudget(type)
241
+ function toMap (type, pairs, maxLength, timeBudgetReached, incomplete) {
242
+ if (timeBudgetReached === true) return notCapturedTimeBudget(type, incomplete)
243
+ if (pairs === undefined) return notCapturedDepth(type, incomplete)
244
+ if (pairs.length > 0 && pairs.every(({ value }) => value[timeBudgetSym] === true)) {
245
+ return notCapturedTimeBudget(type, incomplete)
246
+ }
216
247
 
217
248
  const result = {
218
249
  type,
@@ -227,32 +258,34 @@ function toMap (type, pairs, maxLength, timeBudgetReached) {
227
258
  // second containing the value of this entry of the Map.
228
259
 
229
260
  if (value[timeBudgetSym] === true) {
261
+ incomplete.reasons |= INCOMPLETE_REASON.TIMEOUT
230
262
  return [{ notCapturedReason: 'timeout' }, { notCapturedReason: 'timeout' }]
231
263
  }
232
264
  if (value.properties === undefined) {
265
+ incomplete.reasons |= INCOMPLETE_REASON.OTHER
233
266
  return [{ notCapturedReason: 'unknown' }, { notCapturedReason: 'unknown' }]
234
267
  }
235
268
 
236
269
  const shouldRedact = shouldRedactMapValue(value.properties[0])
237
- const key = getPropertyValue(value.properties[0], maxLength)
270
+ const key = getPropertyValue(value.properties[0], maxLength, incomplete)
238
271
  const val = shouldRedact
239
272
  ? notCapturedRedacted(value.properties[1].value.type)
240
- : getPropertyValue(value.properties[1], maxLength)
273
+ : getPropertyValue(value.properties[1], maxLength, incomplete)
241
274
  return [key, val]
242
275
  }),
243
276
  }
244
277
 
245
- setNotCaptureReasonOnCollection(result, pairs)
246
- setNotCaptureReasonOnTooLargeCollection(result, pairs)
278
+ setNotCaptureReasonOnCollection(result, pairs, incomplete)
279
+ setNotCaptureReasonOnTooLargeCollection(result, pairs, incomplete)
247
280
 
248
281
  return result
249
282
  }
250
283
 
251
- function toSet (type, values, maxLength, timeBudgetReached) {
252
- if (timeBudgetReached === true) return notCapturedTimeBudget(type)
253
- if (values === undefined) return notCapturedDepth(type)
284
+ function toSet (type, values, maxLength, timeBudgetReached, incomplete) {
285
+ if (timeBudgetReached === true) return notCapturedTimeBudget(type, incomplete)
286
+ if (values === undefined) return notCapturedDepth(type, incomplete)
254
287
  if (values.length > 0 && values.every(({ value }) => value[timeBudgetSym] === true)) {
255
- return notCapturedTimeBudget(type)
288
+ return notCapturedTimeBudget(type, incomplete)
256
289
  }
257
290
 
258
291
  const result = {
@@ -267,33 +300,42 @@ function toSet (type, values, maxLength, timeBudgetReached) {
267
300
  // children, of which there will always be exactly one, which contain the
268
301
  // actual value in this entry of the Set.
269
302
 
270
- if (value[timeBudgetSym] === true) return { notCapturedReason: 'timeout' }
271
- if (value.properties === undefined) return { notCapturedReason: 'unknown' }
303
+ if (value[timeBudgetSym] === true) {
304
+ incomplete.reasons |= INCOMPLETE_REASON.TIMEOUT
305
+ return { notCapturedReason: 'timeout' }
306
+ }
307
+ if (value.properties === undefined) {
308
+ incomplete.reasons |= INCOMPLETE_REASON.OTHER
309
+ return { notCapturedReason: 'unknown' }
310
+ }
272
311
 
273
- return getPropertyValue(value.properties[0], maxLength)
312
+ return getPropertyValue(value.properties[0], maxLength, incomplete)
274
313
  }),
275
314
  }
276
315
 
277
- setNotCaptureReasonOnCollection(result, values)
278
- setNotCaptureReasonOnTooLargeCollection(result, values)
316
+ setNotCaptureReasonOnCollection(result, values, incomplete)
317
+ setNotCaptureReasonOnTooLargeCollection(result, values, incomplete)
279
318
 
280
319
  return result
281
320
  }
282
321
 
283
- function toArrayBuffer (type, bytes, maxLength, timeBudgetReached) {
284
- if (timeBudgetReached === true) return notCapturedTimeBudget(type)
285
- if (bytes === undefined) return notCapturedDepth(type)
322
+ function toArrayBuffer (type, bytes, maxLength, timeBudgetReached, incomplete) {
323
+ if (timeBudgetReached === true) return notCapturedTimeBudget(type, incomplete)
324
+ if (bytes === undefined) return notCapturedDepth(type, incomplete)
286
325
 
287
326
  const size = bytes.length
288
327
 
289
- return size > maxLength
290
- ? {
291
- type,
292
- value: arrayBufferToString(bytes, maxLength),
293
- truncated: true,
294
- size: bytes.length,
295
- }
296
- : { type, value: arrayBufferToString(bytes, size) }
328
+ if (size > maxLength) {
329
+ incomplete.reasons |= INCOMPLETE_REASON.STRING_LENGTH
330
+ return {
331
+ type,
332
+ value: arrayBufferToString(bytes, maxLength),
333
+ truncated: true,
334
+ size: bytes.length,
335
+ }
336
+ }
337
+
338
+ return { type, value: arrayBufferToString(bytes, size) }
297
339
  }
298
340
 
299
341
  function arrayBufferToString (bytes, size) {
@@ -304,9 +346,8 @@ function arrayBufferToString (bytes, size) {
304
346
  return buf.toString()
305
347
  }
306
348
 
307
- function redact (prop, obj) {
308
- const name = getNormalizedNameFromProp(prop)
309
- return REDACTED_IDENTIFIERS.has(name) ? notCapturedRedacted(obj.type) : obj
349
+ function shouldRedactProperty (prop) {
350
+ return REDACTED_IDENTIFIERS.has(getNormalizedNameFromProp(prop))
310
351
  }
311
352
 
312
353
  function shouldRedactMapValue (key) {
@@ -323,15 +364,19 @@ function getNormalizedNameFromProp (prop) {
323
364
  return normalizeName(prop.name, prop.symbol !== undefined)
324
365
  }
325
366
 
326
- function setNotCaptureReasonOnCollection (result, collection) {
367
+ function setNotCaptureReasonOnCollection (result, collection, incomplete) {
327
368
  if (collection[collectionSizeSym] !== undefined) {
369
+ incomplete.reasons |= INCOMPLETE_REASON.COLLECTION_SIZE
328
370
  result.notCapturedReason = 'collectionSize'
329
371
  result.size = collection[collectionSizeSym]
330
372
  }
331
373
  }
332
374
 
333
- function setNotCaptureReasonOnTooLargeCollection (result, collection) {
375
+ function setNotCaptureReasonOnTooLargeCollection (result, collection, incomplete) {
334
376
  if (collection[largeCollectionSkipThresholdSym] !== undefined) {
377
+ // The collection was skipped entirely because it exceeded the skip threshold, which is a different limit than the
378
+ // `maxCollectionSize` limit that `collectionSize` represents
379
+ incomplete.reasons |= INCOMPLETE_REASON.OTHER
335
380
  result.notCapturedReason = `Large collection with too many elements (skip threshold: ${
336
381
  LARGE_OBJECT_SKIP_THRESHOLD
337
382
  })`
@@ -339,7 +384,8 @@ function setNotCaptureReasonOnTooLargeCollection (result, collection) {
339
384
  }
340
385
  }
341
386
 
342
- function notCapturedDepth (type) {
387
+ function notCapturedDepth (type, incomplete) {
388
+ incomplete.reasons |= INCOMPLETE_REASON.DEPTH
343
389
  return { type, notCapturedReason: 'depth' }
344
390
  }
345
391
 
@@ -347,6 +393,7 @@ function notCapturedRedacted (type) {
347
393
  return { type, notCapturedReason: 'redactedIdent' }
348
394
  }
349
395
 
350
- function notCapturedTimeBudget (type) {
396
+ function notCapturedTimeBudget (type, incomplete) {
397
+ incomplete.reasons |= INCOMPLETE_REASON.TIMEOUT
351
398
  return { type, notCapturedReason: 'timeout' }
352
399
  }
@@ -143,7 +143,6 @@ class PriorityQueue {
143
143
  *
144
144
  * @param {TreeNode} a - First node to compare
145
145
  * @param {TreeNode} b - Second node to compare
146
- * @returns {number} - > 0 if a has higher priority, < 0 if b has higher priority, 0 if equal
147
146
  */
148
147
  #compare (a, b) {
149
148
  const keyA = a.priorityKey
@@ -243,7 +242,6 @@ function parseJsonToTree (json) {
243
242
  *
244
243
  * @param {string} json - The JSON string to skip
245
244
  * @param {number} startIndex - The index to start skipping from
246
- * @returns {number} The index of the closing quote
247
245
  */
248
246
  function skipString (json, startIndex) {
249
247
  let index = startIndex + 1 // Skip opening quote
@@ -353,7 +351,6 @@ function selectNodesToPrune (root, bytesToRemove) {
353
351
  *
354
352
  * @param {string} json - The JSON string to rebuild
355
353
  * @param {Set<TreeNode>} prunedNodes - The set of nodes to replace with {"pruned":true}
356
- * @returns {string} The rebuilt JSON string
357
354
  */
358
355
  function rebuildJson (json, prunedNodes) {
359
356
  // Convert set to array and sort by start position (descending)
@@ -7,8 +7,10 @@ const { version } = require('../../../../../package.json')
7
7
  const TTLSet = require('../../../../../vendor/dist/ttl-set')
8
8
  const request = require('../../exporters/common/request')
9
9
  const FormData = require('../../exporters/common/form-data')
10
- const { DEBUGGER_DIAGNOSTICS_V1 } = require('../constants')
10
+ const { DEBUGGER_DIAGNOSTICS_V1, DIAGNOSTICS_QUEUE_MAX_BYTES } = require('../constants')
11
+ const { DROPPED_REASON, EVENT_TYPE } = require('../guardrail-metrics')
11
12
  const config = require('./config')
13
+ const guardrailMetrics = require('./guardrail-metrics')
12
14
  const JSONBuffer = require('./json-buffer')
13
15
  const log = require('./log')
14
16
  const getRequestOptions = require('./request-options')
@@ -28,8 +30,11 @@ const ddtags = buildTags(config, getHostname(), version, log)
28
30
 
29
31
  const cache = new TTLSet(60 * 60 * 1000) // 1 hour
30
32
 
33
+ // Diagnostics are queued separately from probe results so that large snapshots cannot starve them. Each diagnostic is
34
+ // a few hundred bytes, so this bound leaves room for thousands of pending status updates.
31
35
  const jsonBuffer = new JSONBuffer({
32
36
  size: config.maxTotalPayloadSize,
37
+ maxQueueBytes: DIAGNOSTICS_QUEUE_MAX_BYTES,
33
38
  timeout: config.dynamicInstrumentation.uploadIntervalSeconds * 1000,
34
39
  onFlush,
35
40
  })
@@ -81,15 +86,24 @@ function ackError (err, { id: probeId, version }) {
81
86
  stacktrace: err.stack,
82
87
  }
83
88
 
84
- send(payload)
89
+ return send(payload)
85
90
  })
86
91
  }
87
92
 
88
93
  function send (payload) {
89
- jsonBuffer.write(JSON.stringify(payload))
94
+ if (jsonBuffer.write(JSON.stringify(payload))) return true
95
+
96
+ const { probeId, status } = payload.debugger.diagnostics
97
+ log.debug('[debugger:devtools_client] Dropping %s status for probe %s: diagnostics queue is full', status, probeId)
98
+ guardrailMetrics.eventDropped(DROPPED_REASON.QUEUE_FULL, EVENT_TYPE.DIAGNOSTIC)
99
+ return false
90
100
  }
91
101
 
92
- function onFlush (payload) {
102
+ /**
103
+ * @param {string} payload - The payload to send
104
+ * @param {() => void} done - Releases the payload from the diagnostics queue
105
+ */
106
+ function onFlush (payload, done) {
93
107
  log.debug('[debugger:devtools_client] Flushing diagnostics payload buffer')
94
108
 
95
109
  const form = new FormData()
@@ -106,6 +120,7 @@ function onFlush (payload) {
106
120
  const options = getRequestOptions(config, path, form.getHeaders())
107
121
 
108
122
  request(form, options, (err) => {
123
+ done()
109
124
  if (err) log.error('[debugger:devtools_client] Error sending diagnostics payload', err)
110
125
  })
111
126
  }
@@ -123,6 +138,5 @@ function statusPayload (probeId, probeVersion, status) {
123
138
  function onlyUniqueUpdates (type, id, version, fn) {
124
139
  const key = `${type}-${id}-${version}`
125
140
  if (cache.has(key)) return
126
- fn()
127
- cache.add(key)
141
+ if (fn()) cache.add(key)
128
142
  }
@@ -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
+ }