dd-trace 6.16.0 → 6.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci/diagnose.js +0 -23
- package/ci/test-optimization-validation/approval-artifacts.js +0 -3
- package/ci/test-optimization-validation/approval.js +0 -6
- package/ci/test-optimization-validation/artifact-id.js +0 -1
- package/ci/test-optimization-validation/bounded-json.js +0 -1
- package/ci/test-optimization-validation/cli.js +0 -12
- package/ci/test-optimization-validation/command-output-policy.js +0 -1
- package/ci/test-optimization-validation/command-runner.js +0 -3
- package/ci/test-optimization-validation/environment.js +0 -4
- package/ci/test-optimization-validation/executable-approval.js +0 -1
- package/ci/test-optimization-validation/executable.js +0 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +0 -6
- package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
- package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
- package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
- package/ci/test-optimization-validation/generated-test-contract.js +0 -5
- package/ci/test-optimization-validation/generated-verifier.js +0 -1
- package/ci/test-optimization-validation/literal-environment.js +0 -1
- package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
- package/ci/test-optimization-validation/manifest-schema.js +0 -21
- package/ci/test-optimization-validation/offline-fixtures.js +0 -4
- package/ci/test-optimization-validation/plan-writer.js +0 -11
- package/ci/test-optimization-validation/preflight-runner.js +0 -1
- package/ci/test-optimization-validation/redaction.js +0 -4
- package/ci/test-optimization-validation/report-writer.js +0 -16
- package/ci/test-optimization-validation/runner-command.js +0 -3
- package/ci/test-optimization-validation/runner-contract.js +0 -4
- package/ci/test-optimization-validation/safe-files.js +0 -1
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +0 -1
- package/ci/test-optimization-validation/source-text.js +0 -3
- package/ci/vitest-no-worker-init-setup.mjs +41 -16
- package/index.d.ts +208 -0
- package/package.json +17 -16
- package/packages/datadog-esbuild/src/utils.js +0 -2
- package/packages/datadog-instrumentations/src/aerospike.js +28 -6
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -2
- package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +0 -2
- package/packages/datadog-instrumentations/src/console.js +97 -0
- package/packages/datadog-instrumentations/src/cucumber.js +77 -12
- package/packages/datadog-instrumentations/src/cypress-config.js +27 -38
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +6 -0
- package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
- package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -6
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +0 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
- package/packages/datadog-instrumentations/src/helpers/register.js +24 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +111 -26
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +41 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -17
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +55 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +28 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +20 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +0 -5
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +165 -51
- package/packages/datadog-instrumentations/src/knex.js +0 -1
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
- package/packages/datadog-instrumentations/src/mariadb.js +0 -2
- package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -26
- package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
- package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
- package/packages/datadog-instrumentations/src/mysql2.js +0 -2
- package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
- package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
- package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
- package/packages/datadog-instrumentations/src/openai.js +51 -12
- package/packages/datadog-instrumentations/src/oracledb.js +9 -3
- package/packages/datadog-instrumentations/src/playwright-reporter.js +6 -20
- package/packages/datadog-instrumentations/src/playwright.js +280 -56
- package/packages/datadog-instrumentations/src/{azure-cosmos.js → postgres.js} +1 -1
- package/packages/datadog-instrumentations/src/router.js +0 -2
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
- package/packages/datadog-instrumentations/src/sequelize.js +0 -1
- package/packages/datadog-instrumentations/src/supabase.js +15 -0
- package/packages/datadog-instrumentations/src/undici.js +7 -3
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
- package/packages/datadog-instrumentations/src/vitest-main.js +121 -30
- package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
- package/packages/datadog-instrumentations/src/vitest-worker.js +49 -13
- package/packages/datadog-instrumentations/src/webdriverio.js +44 -47
- package/packages/datadog-instrumentations/src/ws.js +41 -2
- package/packages/datadog-plugin-ai/src/utils.js +0 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +0 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +0 -1
- package/packages/datadog-plugin-cucumber/src/index.js +9 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +211 -85
- package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
- package/packages/datadog-plugin-cypress/src/index.js +3 -1
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
- package/packages/datadog-plugin-cypress/src/support.js +52 -7
- package/packages/datadog-plugin-graphql/src/resolve-error.js +1 -2
- package/packages/datadog-plugin-graphql/src/utils.js +0 -5
- package/packages/datadog-plugin-jest/src/index.js +12 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
- package/packages/datadog-plugin-mocha/src/index.js +38 -11
- package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
- package/packages/datadog-plugin-openai/src/index.js +16 -1
- package/packages/datadog-plugin-openai/src/realtime.js +144 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
- package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
- package/packages/datadog-plugin-playwright/src/index.js +81 -9
- package/packages/datadog-plugin-postgres/src/index.js +167 -0
- package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
- package/packages/datadog-plugin-redis/src/index.js +15 -1
- package/packages/datadog-plugin-supabase/src/error.js +18 -0
- package/packages/datadog-plugin-supabase/src/index.js +21 -0
- package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
- package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
- package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
- package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
- package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
- package/packages/datadog-plugin-supabase/src/url.js +17 -0
- package/packages/datadog-plugin-vitest/src/index.js +9 -0
- package/packages/datadog-plugin-ws/src/producer.js +1 -1
- package/packages/datadog-plugin-ws/src/util.js +0 -4
- package/packages/datadog-shimmer/src/shimmer.js +14 -10
- package/packages/datadog-turbopack/index.js +4 -7
- package/packages/datadog-turbopack/src/loader.js +6 -18
- package/packages/datadog-webpack/src/loader.js +0 -1
- package/packages/dd-trace/src/agent/info.js +5 -4
- package/packages/dd-trace/src/aiguard/client.js +2 -3
- package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
- package/packages/dd-trace/src/aiguard/index.js +2 -2
- package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
- package/packages/dd-trace/src/aiguard/integrations/openai.js +81 -7
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
- package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
- package/packages/dd-trace/src/aiguard/messages/utils.js +0 -1
- package/packages/dd-trace/src/aiguard/redaction.js +0 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/activation.js +1 -1
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
- package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
- package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
- package/packages/dd-trace/src/appsec/index.js +2 -2
- package/packages/dd-trace/src/appsec/lambda.js +71 -2
- package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
- package/packages/dd-trace/src/appsec/remote_config.js +8 -6
- package/packages/dd-trace/src/appsec/reporter.js +10 -6
- package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
- package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
- package/packages/dd-trace/src/appsec/waf/index.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
- package/packages/dd-trace/src/carrier.js +0 -5
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
- package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
- package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +88 -82
- package/packages/dd-trace/src/config/helper.js +0 -1
- package/packages/dd-trace/src/config/index.js +31 -30
- package/packages/dd-trace/src/config/parsers.js +34 -6
- package/packages/dd-trace/src/config/remote_config.js +25 -109
- package/packages/dd-trace/src/config/supported-configurations.json +150 -10
- package/packages/dd-trace/src/datastreams/encoding.js +0 -1
- package/packages/dd-trace/src/datastreams/pathway.js +0 -1
- package/packages/dd-trace/src/debugger/constants.js +10 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +62 -10
- package/packages/dd-trace/src/debugger/devtools_client/config.js +4 -1
- package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
- package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
- package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +21 -8
- package/packages/dd-trace/src/debugger/devtools_client/send.js +41 -15
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +5 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
- package/packages/dd-trace/src/debugger/devtools_client/status.js +21 -7
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
- package/packages/dd-trace/src/debugger/index.js +51 -3
- package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
- package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
- package/packages/dd-trace/src/dogstatsd.js +0 -23
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
- package/packages/dd-trace/src/encode/0.4.js +0 -7
- package/packages/dd-trace/src/encode/0.5.js +0 -2
- package/packages/dd-trace/src/encode/tags-processors.js +0 -1
- package/packages/dd-trace/src/evp_proxy/constants.js +2 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +17 -13
- package/packages/dd-trace/src/evp_proxy/discovery.js +14 -11
- package/packages/dd-trace/src/evp_proxy/path.js +21 -9
- package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
- package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
- package/packages/dd-trace/src/exporters/common/request.js +1 -0
- package/packages/dd-trace/src/exporters/common/url.js +33 -4
- package/packages/dd-trace/src/exporters/common/writer.js +0 -2
- package/packages/dd-trace/src/id.js +0 -11
- package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
- package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
- package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
- package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
- package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
- package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
- package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
- package/packages/dd-trace/src/llmobs/index.js +1 -3
- package/packages/dd-trace/src/llmobs/noop.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
- package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
- package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
- package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +647 -0
- package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +158 -0
- package/packages/dd-trace/src/llmobs/sdk.js +21 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
- package/packages/dd-trace/src/llmobs/tagger.js +39 -11
- package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
- package/packages/dd-trace/src/llmobs/util.js +0 -34
- package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/log/channels.js +3 -0
- package/packages/dd-trace/src/log/index.js +23 -5
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
- package/packages/dd-trace/src/msgpack/chunk.js +0 -1
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
- package/packages/dd-trace/src/openfeature/configuration_source.js +4 -1
- package/packages/dd-trace/src/openfeature/encoding.js +0 -2
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
- package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -3
- package/packages/dd-trace/src/openfeature/index.js +6 -6
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
- package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
- package/packages/dd-trace/src/openfeature/writers/base.js +90 -23
- package/packages/dd-trace/src/openfeature/writers/exposures.js +4 -5
- package/packages/dd-trace/src/openfeature/writers/util.js +104 -37
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +0 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -14
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +30 -13
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
- package/packages/dd-trace/src/opentracing/span_context.js +0 -1
- package/packages/dd-trace/src/opentracing/tracer.js +3 -3
- package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
- package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
- package/packages/dd-trace/src/plugin_manager.js +27 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -2
- package/packages/dd-trace/src/plugins/database.js +2 -6
- package/packages/dd-trace/src/plugins/index.js +7 -0
- package/packages/dd-trace/src/plugins/plugin.js +0 -4
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/git.js +1 -3
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
- package/packages/dd-trace/src/plugins/util/llm.js +0 -2
- package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
- package/packages/dd-trace/src/plugins/util/test.js +26 -20
- package/packages/dd-trace/src/plugins/util/url.js +0 -5
- package/packages/dd-trace/src/priority_sampler.js +0 -6
- package/packages/dd-trace/src/process-tags/index.js +0 -1
- package/packages/dd-trace/src/profiler.js +132 -35
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
- package/packages/dd-trace/src/profiling/profiler.js +101 -41
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
- package/packages/dd-trace/src/propagation-hash/index.js +0 -1
- package/packages/dd-trace/src/proxy.js +23 -47
- package/packages/dd-trace/src/random_sampler.js +0 -4
- package/packages/dd-trace/src/rate_limiter.js +0 -4
- package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
- package/packages/dd-trace/src/remote_config/index.js +0 -1
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
- package/packages/dd-trace/src/sampler.js +0 -4
- package/packages/dd-trace/src/sampling_rule.js +0 -7
- package/packages/dd-trace/src/serverless/vercel.js +0 -1
- package/packages/dd-trace/src/serverless.js +0 -1
- package/packages/dd-trace/src/service-naming/index.js +0 -1
- package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
- package/packages/dd-trace/src/span_processor.js +2 -2
- package/packages/dd-trace/src/span_sampler.js +0 -1
- package/packages/dd-trace/src/standalone/product.js +2 -2
- package/packages/dd-trace/src/standalone/tracesource.js +0 -1
- package/packages/dd-trace/src/startup-log.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
- package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
- package/packages/dd-trace/src/telemetry/metrics.js +0 -2
- package/packages/dd-trace/src/telemetry/send-data.js +2 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
- package/packages/dd-trace/src/tracer.js +5 -4
- package/packages/dd-trace/src/util.js +13 -1
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/packages/datadog-instrumentations/src/bullmq.js +0 -11
- package/packages/datadog-instrumentations/src/langchain.js +0 -7
- package/packages/datadog-instrumentations/src/langgraph.js +0 -7
- package/packages/datadog-instrumentations/src/mercurius.js +0 -11
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Guardrail telemetry counters shared between the main thread and the debugger worker thread.
|
|
5
|
+
*
|
|
6
|
+
* Guardrail decisions are made in two places: the probe sampler that runs inside the V8 breakpoint condition on the
|
|
7
|
+
* main thread (rate limits) and the debugger worker (capture limits, queue overflow). Neither should pay for a message
|
|
8
|
+
* or a telemetry API call per skipped, dropped or incomplete event, so both increment counters in a shared buffer using
|
|
9
|
+
* atomics, and the main thread periodically drains the counters into instrumentation telemetry metrics.
|
|
10
|
+
*
|
|
11
|
+
* The metric names, tags and reason codes are defined by the "Debugger Observability for GA" specification and are
|
|
12
|
+
* shared with the other tracers. All metrics are counts in the `live_debugger` telemetry namespace.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const TELEMETRY_NAMESPACE = 'live_debugger'
|
|
16
|
+
|
|
17
|
+
const EVENT_TYPE = Object.freeze({
|
|
18
|
+
SNAPSHOT: 0,
|
|
19
|
+
LOG: 1,
|
|
20
|
+
DIAGNOSTIC: 2,
|
|
21
|
+
})
|
|
22
|
+
const EVENT_TYPE_NAMES = ['snapshot', 'log', 'diagnostic']
|
|
23
|
+
|
|
24
|
+
/** Attempts stopped before event creation (`events.skipped`) */
|
|
25
|
+
const SKIPPED_REASON = Object.freeze({
|
|
26
|
+
RATE_LIMIT_GLOBAL: 0,
|
|
27
|
+
RATE_LIMIT_PROBE: 1,
|
|
28
|
+
EVALUATION_TIMEOUT: 2,
|
|
29
|
+
})
|
|
30
|
+
const SKIPPED_REASON_NAMES = ['rateLimitGlobal', 'rateLimitProbe', 'evaluationTimeout']
|
|
31
|
+
|
|
32
|
+
/** Complete events dropped before transport (`events.dropped`) */
|
|
33
|
+
const DROPPED_REASON = Object.freeze({
|
|
34
|
+
QUEUE_FULL: 0,
|
|
35
|
+
// The spec allows either trimming oversized payloads or dropping them whole. This tracer trims, and reports
|
|
36
|
+
// `capture.incomplete{payloadTooLarge}` instead, so this reason exists for spec parity but is never emitted.
|
|
37
|
+
PAYLOAD_TOO_LARGE: 1,
|
|
38
|
+
})
|
|
39
|
+
const DROPPED_REASON_NAMES = ['queueFull', 'payloadTooLarge']
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Capture enforced limits (`capture.incomplete`). These are bit flags so that the reasons hit while producing a single
|
|
43
|
+
* event can be accumulated in a bitmask and reported once per event per reason.
|
|
44
|
+
*/
|
|
45
|
+
const INCOMPLETE_REASON = Object.freeze({
|
|
46
|
+
RUNTIME_ERROR: 1 << 0,
|
|
47
|
+
TIMEOUT: 1 << 1,
|
|
48
|
+
DEPTH: 1 << 2,
|
|
49
|
+
FIELD_COUNT: 1 << 3,
|
|
50
|
+
COLLECTION_SIZE: 1 << 4,
|
|
51
|
+
STRING_LENGTH: 1 << 5,
|
|
52
|
+
PAYLOAD_TOO_LARGE: 1 << 6,
|
|
53
|
+
OTHER: 1 << 7,
|
|
54
|
+
})
|
|
55
|
+
const INCOMPLETE_REASON_NAMES = [
|
|
56
|
+
'runtimeError', 'timeout', 'depth', 'fieldCount', 'collectionSize', 'stringLength', 'payloadTooLarge', 'other',
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
const EVENT_TYPE_COUNT = EVENT_TYPE_NAMES.length
|
|
60
|
+
const SKIPPED_OFFSET = 0
|
|
61
|
+
const DROPPED_OFFSET = SKIPPED_OFFSET + SKIPPED_REASON_NAMES.length * EVENT_TYPE_COUNT
|
|
62
|
+
const INCOMPLETE_OFFSET = DROPPED_OFFSET + DROPPED_REASON_NAMES.length * EVENT_TYPE_COUNT
|
|
63
|
+
const SLOT_COUNT = INCOMPLETE_OFFSET + INCOMPLETE_REASON_NAMES.length * EVENT_TYPE_COUNT
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @typedef {object} MetricSlot
|
|
67
|
+
* @property {string} metric - The telemetry metric name
|
|
68
|
+
* @property {string[]} tags - The telemetry tags, sorted
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/** @type {MetricSlot[]} */
|
|
72
|
+
const SLOTS = []
|
|
73
|
+
addSlots('events.skipped', SKIPPED_REASON_NAMES)
|
|
74
|
+
addSlots('events.dropped', DROPPED_REASON_NAMES)
|
|
75
|
+
addSlots('capture.incomplete', INCOMPLETE_REASON_NAMES)
|
|
76
|
+
|
|
77
|
+
class GuardrailMetrics {
|
|
78
|
+
/** @type {Int32Array} */
|
|
79
|
+
#counters
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {SharedArrayBuffer} buffer - A buffer created with {@link GuardrailMetrics.createBuffer}
|
|
83
|
+
*/
|
|
84
|
+
constructor (buffer) {
|
|
85
|
+
this.#counters = new Int32Array(buffer)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create the shared buffer backing the counters.
|
|
90
|
+
*
|
|
91
|
+
* @returns {SharedArrayBuffer}
|
|
92
|
+
*/
|
|
93
|
+
static createBuffer () {
|
|
94
|
+
return new SharedArrayBuffer(SLOT_COUNT * Int32Array.BYTES_PER_ELEMENT)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Record an attempt that was stopped before an event was created.
|
|
99
|
+
*
|
|
100
|
+
* @param {number} reason - One of {@link SKIPPED_REASON}
|
|
101
|
+
* @param {number} eventType - One of {@link EVENT_TYPE}
|
|
102
|
+
*/
|
|
103
|
+
eventSkipped (reason, eventType) {
|
|
104
|
+
Atomics.add(this.#counters, SKIPPED_OFFSET + reason * EVENT_TYPE_COUNT + eventType, 1)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Record a complete event that was dropped before transport.
|
|
109
|
+
*
|
|
110
|
+
* @param {number} reason - One of {@link DROPPED_REASON}
|
|
111
|
+
* @param {number} eventType - One of {@link EVENT_TYPE}
|
|
112
|
+
* @param {number} [count] - The number of dropped events
|
|
113
|
+
*/
|
|
114
|
+
eventDropped (reason, eventType, count = 1) {
|
|
115
|
+
Atomics.add(this.#counters, DROPPED_OFFSET + reason * EVENT_TYPE_COUNT + eventType, count)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Record the capture limits enforced while producing a single event, once per reason.
|
|
120
|
+
*
|
|
121
|
+
* @param {number} reasons - A bitmask of {@link INCOMPLETE_REASON} flags
|
|
122
|
+
* @param {number} eventType - One of {@link EVENT_TYPE}
|
|
123
|
+
*/
|
|
124
|
+
captureIncomplete (reasons, eventType) {
|
|
125
|
+
for (let bit = 0; reasons !== 0; bit++, reasons >>>= 1) {
|
|
126
|
+
if ((reasons & 1) === 1) {
|
|
127
|
+
Atomics.add(this.#counters, INCOMPLETE_OFFSET + bit * EVENT_TYPE_COUNT + eventType, 1)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Reset all counters and report the ones that were non-zero.
|
|
134
|
+
*
|
|
135
|
+
* @param {(metric: string, tags: string[], count: number) => void} report - Called once per non-zero counter
|
|
136
|
+
*/
|
|
137
|
+
drain (report) {
|
|
138
|
+
for (let i = 0; i < SLOT_COUNT; i++) {
|
|
139
|
+
const count = Atomics.exchange(this.#counters, i, 0)
|
|
140
|
+
if (count !== 0) {
|
|
141
|
+
const { metric, tags } = SLOTS[i]
|
|
142
|
+
report(metric, tags, count)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {string} metric
|
|
150
|
+
* @param {string[]} reasonNames
|
|
151
|
+
*/
|
|
152
|
+
function addSlots (metric, reasonNames) {
|
|
153
|
+
for (const reason of reasonNames) {
|
|
154
|
+
for (const eventType of EVENT_TYPE_NAMES) {
|
|
155
|
+
SLOTS.push({ metric, tags: [`event_type:${eventType}`, `reason:${reason}`] })
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = {
|
|
161
|
+
DROPPED_REASON,
|
|
162
|
+
EVENT_TYPE,
|
|
163
|
+
GuardrailMetrics,
|
|
164
|
+
INCOMPLETE_REASON,
|
|
165
|
+
SKIPPED_REASON,
|
|
166
|
+
TELEMETRY_NAMESPACE,
|
|
167
|
+
}
|
|
@@ -4,15 +4,19 @@ const { readFile } = require('fs')
|
|
|
4
4
|
const { types } = require('util')
|
|
5
5
|
const { join } = require('path')
|
|
6
6
|
const { Worker, MessageChannel, threadId: parentThreadId } = require('worker_threads')
|
|
7
|
+
const dc = require('dc-polyfill')
|
|
7
8
|
const log = require('../log')
|
|
8
9
|
const { fetchAgentInfo } = require('../agent/info')
|
|
10
|
+
const telemetryMetrics = require('../telemetry/metrics')
|
|
9
11
|
const getDebuggerConfig = require('./config')
|
|
10
12
|
const {
|
|
11
13
|
DEBUGGER_DIAGNOSTICS_V1,
|
|
12
14
|
DEBUGGER_INPUT_DIRECT,
|
|
13
15
|
DEBUGGER_INPUT_V2,
|
|
16
|
+
GUARDRAIL_METRICS_FLUSH_INTERVAL_MS,
|
|
14
17
|
INSPECT_SEGMENT_GLOBAL_PROPERTY,
|
|
15
18
|
} = require('./constants')
|
|
19
|
+
const { GuardrailMetrics, TELEMETRY_NAMESPACE } = require('./guardrail-metrics')
|
|
16
20
|
const { installProbeSampler, uninstallProbeSampler } = require('./probe_sampler')
|
|
17
21
|
|
|
18
22
|
/**
|
|
@@ -23,12 +27,20 @@ const { installProbeSampler, uninstallProbeSampler } = require('./probe_sampler'
|
|
|
23
27
|
* @typedef {import('../remote_config')} RemoteConfig
|
|
24
28
|
*/
|
|
25
29
|
|
|
30
|
+
// Published by telemetry right before it sends its final metrics on process exit. The flush interval timer is unref'ed
|
|
31
|
+
// and the worker does not keep the process alive, so without this hook everything counted since the last tick would
|
|
32
|
+
// be lost when the application exits on its own.
|
|
33
|
+
const TELEMETRY_APP_CLOSING_CHANNEL = 'datadog:telemetry:app-closing'
|
|
34
|
+
|
|
26
35
|
let worker = null
|
|
27
36
|
let configChannel = null
|
|
28
37
|
let ackId = 0
|
|
29
38
|
let rcAckCallbacks = null
|
|
30
39
|
let rc = null
|
|
31
40
|
let inputPath = null
|
|
41
|
+
/** @type {GuardrailMetrics | null} */
|
|
42
|
+
let guardrailMetrics = null
|
|
43
|
+
let guardrailMetricsTimer = null
|
|
32
44
|
|
|
33
45
|
// eslint-disable-next-line eslint-rules/eslint-process-env
|
|
34
46
|
const { NODE_OPTIONS, ...env } = process.env
|
|
@@ -43,7 +55,6 @@ module.exports = {
|
|
|
43
55
|
/**
|
|
44
56
|
* Check if the Debugger worker is currently running
|
|
45
57
|
*
|
|
46
|
-
* @returns {boolean} True if the worker is started, false otherwise
|
|
47
58
|
*/
|
|
48
59
|
function isStarted () {
|
|
49
60
|
return worker !== null
|
|
@@ -77,9 +88,15 @@ function start (config, rcInstance) {
|
|
|
77
88
|
debuggerGlobals.utilTypes = types
|
|
78
89
|
debuggerGlobals[INSPECT_SEGMENT_GLOBAL_PROPERTY] = require('./inspect-segment')
|
|
79
90
|
|
|
80
|
-
const
|
|
91
|
+
const guardrailMetricsBuffer = GuardrailMetrics.createBuffer()
|
|
92
|
+
guardrailMetrics = new GuardrailMetrics(guardrailMetricsBuffer)
|
|
93
|
+
guardrailMetricsTimer = setInterval(flushGuardrailMetrics, GUARDRAIL_METRICS_FLUSH_INTERVAL_MS)
|
|
94
|
+
guardrailMetricsTimer.unref?.()
|
|
95
|
+
dc.subscribe(TELEMETRY_APP_CLOSING_CHANNEL, flushGuardrailMetrics)
|
|
96
|
+
|
|
97
|
+
const probeSamplerBuffer = installProbeSampler(guardrailMetrics)
|
|
81
98
|
|
|
82
|
-
readProbeFile(config.dynamicInstrumentation.
|
|
99
|
+
readProbeFile(config.dynamicInstrumentation.DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE, (probes) => {
|
|
83
100
|
const action = 'apply'
|
|
84
101
|
for (const probe of probes) {
|
|
85
102
|
probeChannel.port2.postMessage({ action, probe })
|
|
@@ -125,6 +142,7 @@ function start (config, rcInstance) {
|
|
|
125
142
|
logPort: logChannel.port1,
|
|
126
143
|
configPort: configChannel.port1,
|
|
127
144
|
probeSamplerBuffer,
|
|
145
|
+
guardrailMetricsBuffer,
|
|
128
146
|
},
|
|
129
147
|
transferList: [probeChannel.port1, logChannel.port1, configChannel.port1],
|
|
130
148
|
}
|
|
@@ -137,6 +155,11 @@ function start (config, rcInstance) {
|
|
|
137
155
|
)
|
|
138
156
|
})
|
|
139
157
|
|
|
158
|
+
const threadPausedMetric = telemetryMetrics.manager.namespace(TELEMETRY_NAMESPACE)
|
|
159
|
+
.distribution('execution.pause.duration')
|
|
160
|
+
worker.on('message', (/** @type {{ type: string, durationMs: number }} */ { type, durationMs }) => {
|
|
161
|
+
if (type === 'thread-paused') threadPausedMetric.track(durationMs)
|
|
162
|
+
})
|
|
140
163
|
worker.on('error', (err) => log.error('[debugger] worker thread error', err))
|
|
141
164
|
worker.on('messageerror', (err) => log.error('[debugger] received "messageerror" from worker', err))
|
|
142
165
|
|
|
@@ -211,6 +234,20 @@ function cleanup (error) {
|
|
|
211
234
|
configChannel = null
|
|
212
235
|
inputPath = null
|
|
213
236
|
|
|
237
|
+
if (guardrailMetricsTimer !== null) {
|
|
238
|
+
clearInterval(guardrailMetricsTimer)
|
|
239
|
+
guardrailMetricsTimer = null
|
|
240
|
+
}
|
|
241
|
+
if (guardrailMetrics !== null) {
|
|
242
|
+
dc.unsubscribe(TELEMETRY_APP_CLOSING_CHANNEL, flushGuardrailMetrics)
|
|
243
|
+
// Report what the worker counted up until it was stopped. Known limitation: `Worker#terminate()` interrupts the
|
|
244
|
+
// worker asynchronously, so anything it counts between this drain and its actual termination is lost. That only
|
|
245
|
+
// concerns events still sitting in the worker's upload buffer, which die with the worker anyway, so it isn't worth
|
|
246
|
+
// deferring the drain until the worker has exited.
|
|
247
|
+
flushGuardrailMetrics()
|
|
248
|
+
guardrailMetrics = null
|
|
249
|
+
}
|
|
250
|
+
|
|
214
251
|
// Call any pending ack callbacks
|
|
215
252
|
// Pass error for unexpected exits, or undefined for graceful shutdown
|
|
216
253
|
if (rcAckCallbacks) {
|
|
@@ -223,6 +260,17 @@ function cleanup (error) {
|
|
|
223
260
|
}
|
|
224
261
|
}
|
|
225
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Convert the guardrail counters accumulated by the probe sampler and the worker into telemetry metrics.
|
|
265
|
+
*/
|
|
266
|
+
function flushGuardrailMetrics () {
|
|
267
|
+
if (guardrailMetrics === null) return
|
|
268
|
+
const namespace = telemetryMetrics.manager.namespace(TELEMETRY_NAMESPACE)
|
|
269
|
+
guardrailMetrics.drain((metric, tags, count) => {
|
|
270
|
+
namespace.count(metric, tags).inc(count)
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
226
274
|
/**
|
|
227
275
|
* Detect which debugger endpoint is available on the agent
|
|
228
276
|
*
|
|
@@ -34,7 +34,6 @@ module.exports = inspectSegment
|
|
|
34
34
|
* truncates objects before inspecting them.
|
|
35
35
|
*
|
|
36
36
|
* @param {unknown} value
|
|
37
|
-
* @returns {string}
|
|
38
37
|
*/
|
|
39
38
|
function inspectSegment (value) {
|
|
40
39
|
if (value === null || (typeof value !== 'object' && typeof value !== 'function')) {
|
|
@@ -99,7 +98,6 @@ function inspectSegment (value) {
|
|
|
99
98
|
*
|
|
100
99
|
* @param {Collection} value
|
|
101
100
|
* @param {boolean} isMap
|
|
102
|
-
* @returns {string}
|
|
103
101
|
*/
|
|
104
102
|
function inspectCollection (value, isMap) {
|
|
105
103
|
if (NODE_MAJOR !== 18) return inspect(value, segmentInspectOptions)
|
|
@@ -137,7 +135,6 @@ function inspectCollection (value, isMap) {
|
|
|
137
135
|
* Determine whether inspecting a value could invoke a proxy trap or toStringTag getter.
|
|
138
136
|
*
|
|
139
137
|
* @param {unknown} value
|
|
140
|
-
* @returns {boolean}
|
|
141
138
|
*/
|
|
142
139
|
function inspectionCanRunUserCode (value) {
|
|
143
140
|
const type = typeof value
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { types } = require('node:util')
|
|
4
|
+
|
|
3
5
|
const { MAX_SNAPSHOTS_PER_SECOND_GLOBALLY } = require('./devtools_client/defaults')
|
|
6
|
+
const { MAX_MESSAGE_LENGTH } = require('./constants')
|
|
7
|
+
const { EVENT_TYPE, SKIPPED_REASON } = require('./guardrail-metrics')
|
|
4
8
|
const {
|
|
9
|
+
CONDITION_ERROR_FLAG,
|
|
10
|
+
CONDITION_ERROR_THROTTLE_NS,
|
|
5
11
|
DD_TRACE_SYMBOL,
|
|
6
12
|
MAX_SAMPLED_PROBES_PER_PAUSE,
|
|
7
13
|
PROBE_SAMPLER_SYMBOL,
|
|
@@ -22,12 +28,20 @@ module.exports = {
|
|
|
22
28
|
/**
|
|
23
29
|
* Install the runtime sampler in the debuggee context.
|
|
24
30
|
*
|
|
31
|
+
* @param {import('./guardrail-metrics').GuardrailMetrics} guardrailMetrics - Counters for skipped probe hits.
|
|
25
32
|
* @returns {SharedArrayBuffer} The shared sampler buffer to pass to the debugger worker.
|
|
26
33
|
*/
|
|
27
|
-
function installProbeSampler () {
|
|
34
|
+
function installProbeSampler (guardrailMetrics) {
|
|
28
35
|
const buffer = createProbeSamplerBuffer()
|
|
29
36
|
|
|
30
37
|
const lastCaptureNsByProbeId = new Map()
|
|
38
|
+
/**
|
|
39
|
+
* Probes whose condition recently failed to evaluate, keyed by probe id. The error is kept until the worker picks it
|
|
40
|
+
* up for the error result.
|
|
41
|
+
*
|
|
42
|
+
* @type {Map<string, { throttledUntilNs: bigint, error: string | undefined }>}
|
|
43
|
+
*/
|
|
44
|
+
const conditionErrorByProbeId = new Map()
|
|
31
45
|
const sampledProbeIndexes = new Int32Array(buffer)
|
|
32
46
|
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 0)
|
|
33
47
|
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 0)
|
|
@@ -44,27 +58,29 @@ function installProbeSampler () {
|
|
|
44
58
|
* @param {string} probeId - The probe id.
|
|
45
59
|
* @param {bigint} nsBetweenSampling - Minimum nanoseconds between samples for this probe.
|
|
46
60
|
* @param {boolean} isSnapshotProducingProbe - Whether this probe counts toward the global snapshot sample limit.
|
|
47
|
-
* @returns {boolean} Whether this probe should make the breakpoint condition pause.
|
|
48
61
|
*/
|
|
49
62
|
makeSampleDecision (probeIndex, probeId, nsBetweenSampling, isSnapshotProducingProbe) {
|
|
50
63
|
const now = process.hrtime.bigint()
|
|
51
64
|
const lastCaptureNs = lastCaptureNsByProbeId.get(probeId)
|
|
52
|
-
if (lastCaptureNs !== undefined && now - lastCaptureNs < nsBetweenSampling)
|
|
65
|
+
if (lastCaptureNs !== undefined && now - lastCaptureNs < nsBetweenSampling) {
|
|
66
|
+
guardrailMetrics.eventSkipped(
|
|
67
|
+
SKIPPED_REASON.RATE_LIMIT_PROBE,
|
|
68
|
+
isSnapshotProducingProbe === true ? EVENT_TYPE.SNAPSHOT : EVENT_TYPE.LOG
|
|
69
|
+
)
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
53
72
|
|
|
54
73
|
let shouldResetGlobalSnapshotRateWindow = false
|
|
55
74
|
if (isSnapshotProducingProbe === true) {
|
|
56
75
|
if (now - globalSnapshotSamplingRateWindowStart > oneSecondNs) {
|
|
57
76
|
shouldResetGlobalSnapshotRateWindow = true
|
|
58
77
|
} else if (snapshotsSampledWithinTheLastSecond >= MAX_SNAPSHOTS_PER_SECOND_GLOBALLY) {
|
|
78
|
+
guardrailMetrics.eventSkipped(SKIPPED_REASON.RATE_LIMIT_GLOBAL, EVENT_TYPE.SNAPSHOT)
|
|
59
79
|
return false
|
|
60
80
|
}
|
|
61
81
|
}
|
|
62
82
|
|
|
63
|
-
|
|
64
|
-
if (sampledProbeCount >= MAX_SAMPLED_PROBES_PER_PAUSE) {
|
|
65
|
-
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 1)
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
83
|
+
if (!storeSampledProbeIndex(probeIndex)) return false
|
|
68
84
|
|
|
69
85
|
if (isSnapshotProducingProbe === true) {
|
|
70
86
|
if (shouldResetGlobalSnapshotRateWindow === true) {
|
|
@@ -76,10 +92,56 @@ function installProbeSampler () {
|
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
lastCaptureNsByProbeId.set(probeId, now)
|
|
79
|
-
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + sampledProbeCount, probeIndex)
|
|
80
95
|
return true
|
|
81
96
|
},
|
|
82
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Decide if a probe's condition should be evaluated, or skipped because a recent evaluation error throttled it.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} probeId - The probe id.
|
|
102
|
+
*/
|
|
103
|
+
shouldEvaluateCondition (probeId) {
|
|
104
|
+
const state = conditionErrorByProbeId.get(probeId)
|
|
105
|
+
if (state === undefined) return true
|
|
106
|
+
if (process.hrtime.bigint() < state.throttledUntilNs) return false
|
|
107
|
+
conditionErrorByProbeId.delete(probeId)
|
|
108
|
+
return true
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Record that a probe's condition threw, throttle the probe, and request a pause if the error can be reported.
|
|
113
|
+
*
|
|
114
|
+
* Error results bypass the per-probe and global rate limits: they are rate limited by the throttle instead, which
|
|
115
|
+
* allows one error result per probe per window. If the shared buffer is full, the error is dropped but the throttle
|
|
116
|
+
* is retained.
|
|
117
|
+
*
|
|
118
|
+
* @param {number} probeIndex - The worker-side probe sampling index.
|
|
119
|
+
* @param {string} probeId - The probe id.
|
|
120
|
+
* @param {unknown} error - The value thrown by the condition.
|
|
121
|
+
*/
|
|
122
|
+
conditionError (probeIndex, probeId, error) {
|
|
123
|
+
const stored = storeSampledProbeIndex(probeIndex | CONDITION_ERROR_FLAG)
|
|
124
|
+
conditionErrorByProbeId.set(probeId, {
|
|
125
|
+
throttledUntilNs: process.hrtime.bigint() + CONDITION_ERROR_THROTTLE_NS,
|
|
126
|
+
error: stored ? describeError(error) : undefined,
|
|
127
|
+
})
|
|
128
|
+
return stored
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Hand over the recorded condition error for a probe to the worker. Called by the worker on the paused thread.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} probeId - The probe id.
|
|
135
|
+
* @returns {string | undefined} The error description, if any.
|
|
136
|
+
*/
|
|
137
|
+
takeConditionError (probeId) {
|
|
138
|
+
const state = conditionErrorByProbeId.get(probeId)
|
|
139
|
+
if (state === undefined) return
|
|
140
|
+
const { error } = state
|
|
141
|
+
state.error = undefined
|
|
142
|
+
return error
|
|
143
|
+
},
|
|
144
|
+
|
|
83
145
|
/**
|
|
84
146
|
* Remove cached sampling state for a probe.
|
|
85
147
|
*
|
|
@@ -87,9 +149,25 @@ function installProbeSampler () {
|
|
|
87
149
|
*/
|
|
88
150
|
remove (probeId) {
|
|
89
151
|
lastCaptureNsByProbeId.delete(probeId)
|
|
152
|
+
conditionErrorByProbeId.delete(probeId)
|
|
90
153
|
},
|
|
91
154
|
}
|
|
92
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Hand a sampled probe index over to the worker for the upcoming pause.
|
|
158
|
+
*
|
|
159
|
+
* @param {number} value - The probe sampling index, possibly with flags set.
|
|
160
|
+
*/
|
|
161
|
+
function storeSampledProbeIndex (value) {
|
|
162
|
+
const sampledProbeCount = Atomics.add(sampledProbeIndexes, SAMPLED_PROBE_COUNT_INDEX, 1)
|
|
163
|
+
if (sampledProbeCount >= MAX_SAMPLED_PROBES_PER_PAUSE) {
|
|
164
|
+
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_OVERFLOW_INDEX, 1)
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
Atomics.store(sampledProbeIndexes, SAMPLED_PROBE_INDEXES_START + sampledProbeCount, value)
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
|
|
93
171
|
return buffer
|
|
94
172
|
}
|
|
95
173
|
|
|
@@ -100,6 +178,52 @@ function uninstallProbeSampler () {
|
|
|
100
178
|
delete ddTraceGlobal[Symbol.for(PROBE_SAMPLER_SYMBOL)]
|
|
101
179
|
}
|
|
102
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Describe a value thrown by a probe condition without invoking user code. Conditions can throw anything, including
|
|
183
|
+
* errors with accessors and proxies whose traps have side effects.
|
|
184
|
+
*
|
|
185
|
+
* @param {unknown} error - The thrown value.
|
|
186
|
+
*/
|
|
187
|
+
function describeError (error) {
|
|
188
|
+
if (typeof error === 'string') return truncateErrorDescription(error)
|
|
189
|
+
if (error === null || (typeof error !== 'object' && typeof error !== 'function')) {
|
|
190
|
+
return 'Unknown evaluation error'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const name = getErrorStringProperty(error, 'name')
|
|
194
|
+
const message = getErrorStringProperty(error, 'message')
|
|
195
|
+
if (name === undefined) return truncateErrorDescription(message ?? 'Unknown evaluation error')
|
|
196
|
+
if (message === undefined) return truncateErrorDescription(name)
|
|
197
|
+
|
|
198
|
+
return truncateErrorDescription(`${name}: ${message}`)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Bound an error description before retaining or transferring it.
|
|
203
|
+
*
|
|
204
|
+
* @param {string} description - The description to bound.
|
|
205
|
+
*/
|
|
206
|
+
function truncateErrorDescription (description) {
|
|
207
|
+
return description.length > MAX_MESSAGE_LENGTH
|
|
208
|
+
? `${description.slice(0, MAX_MESSAGE_LENGTH)}…`
|
|
209
|
+
: description
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Read a string-valued error property without invoking accessors or proxy traps.
|
|
214
|
+
*
|
|
215
|
+
* @param {object} error - The thrown object to inspect.
|
|
216
|
+
* @param {'name' | 'message'} property - The property to find.
|
|
217
|
+
*/
|
|
218
|
+
function getErrorStringProperty (error, property) {
|
|
219
|
+
while (error !== null) {
|
|
220
|
+
if (types.isProxy(error)) return
|
|
221
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, property)
|
|
222
|
+
if (descriptor !== undefined) return typeof descriptor.value === 'string' ? descriptor.value : undefined
|
|
223
|
+
error = Object.getPrototypeOf(error)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
103
227
|
/**
|
|
104
228
|
* Create the shared buffer used to hand sampled probe indexes from breakpoint conditions to the debugger worker.
|
|
105
229
|
*
|
|
@@ -3,13 +3,43 @@
|
|
|
3
3
|
const DD_TRACE_SYMBOL = 'dd-trace'
|
|
4
4
|
const PROBE_SAMPLER_SYMBOL = 'dd-trace.debugger.probeSampler'
|
|
5
5
|
|
|
6
|
-
// Shared sampler contract used by the main debugger bootstrap and the devtools worker.
|
|
6
|
+
// Shared sampler contract used by the main debugger bootstrap and the devtools worker. The debuggee hands sampled
|
|
7
|
+
// probes to the worker through a shared Int32Array, drained by the worker on every pause:
|
|
8
|
+
//
|
|
9
|
+
// +----------+----------+----------+----------+- - - -+------------+
|
|
10
|
+
// slot | 0 | 1 | 2 | 3 | | 2+MAX-1 |
|
|
11
|
+
// holds | count | overflow | value | value | ... | value |
|
|
12
|
+
// +----------+----------+----------+----------+- - - -+------------+
|
|
13
|
+
// ^COUNT ^OVERFLOW ^INDEXES_START
|
|
7
14
|
const MAX_SAMPLED_PROBES_PER_PAUSE = 256
|
|
8
15
|
const SAMPLED_PROBE_COUNT_INDEX = 0
|
|
9
16
|
const SAMPLED_PROBE_OVERFLOW_INDEX = 1
|
|
10
17
|
const SAMPLED_PROBE_INDEXES_START = 2
|
|
11
18
|
|
|
19
|
+
// Each value above is a probe sampling index, with this flag set in bit 30 when the pause is for reporting a
|
|
20
|
+
// condition evaluation error instead of a probe result. Bit 31 stays unused because the array is signed:
|
|
21
|
+
//
|
|
22
|
+
// +--------+------------+--------------------------------------------+
|
|
23
|
+
// bit | 31 | 30 | 29..0 |
|
|
24
|
+
// means | unused | CONDITION_ | samplingIndex |
|
|
25
|
+
// | (sign) | ERROR_FLAG | |
|
|
26
|
+
// +--------+------------+--------------------------------------------+
|
|
27
|
+
//
|
|
28
|
+
// 0x0000_0007 probe #7 matched -> worker captures as usual
|
|
29
|
+
// 0x4000_0007 probe #7's condition threw -> worker takes the recorded error, captures nothing
|
|
30
|
+
//
|
|
31
|
+
// write: probeIndex | CONDITION_ERROR_FLAG
|
|
32
|
+
// read: value & ~CONDITION_ERROR_FLAG -> the sampling index
|
|
33
|
+
// value & CONDITION_ERROR_FLAG -> non-zero when the condition threw
|
|
34
|
+
const CONDITION_ERROR_FLAG = 1 << 30
|
|
35
|
+
|
|
36
|
+
// A probe whose condition failed to evaluate is not evaluated again for this long. One error result is reported per
|
|
37
|
+
// window, so a probe with a broken condition stays visible without repeatedly paying for the failing evaluation.
|
|
38
|
+
const CONDITION_ERROR_THROTTLE_NS = 5n * 60n * 1_000_000_000n // 5 minutes
|
|
39
|
+
|
|
12
40
|
module.exports = {
|
|
41
|
+
CONDITION_ERROR_FLAG,
|
|
42
|
+
CONDITION_ERROR_THROTTLE_NS,
|
|
13
43
|
DD_TRACE_SYMBOL,
|
|
14
44
|
MAX_SAMPLED_PROBES_PER_PAUSE,
|
|
15
45
|
PROBE_SAMPLER_SYMBOL,
|