dd-trace 6.20.1 → 6.22.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 +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +116 -17
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +13 -11
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +48 -33
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +64 -11
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +122 -11
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +127 -38
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/playwright.js +3 -0
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +9 -17
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +2 -2
- package/packages/datadog-instrumentations/src/webdriverio.js +118 -21
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +18 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +1 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +223 -39
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +453 -0
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +10 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +35 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/debugger/index.js +24 -2
- package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +2 -5
- package/packages/dd-trace/src/exporters/common/url.js +1 -1
- package/packages/dd-trace/src/id.js +15 -5
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +13 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +23 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +18 -8
- package/packages/dd-trace/src/llmobs/tagger.js +56 -3
- package/packages/dd-trace/src/llmobs/util.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/base.js +7 -6
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
- package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
- package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +59 -3
- package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +81 -15
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/util/git.js +9 -25
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/test.js +48 -4
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
- package/packages/dd-trace/src/proxy.js +3 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +10 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/storage-channels.js +112 -46
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/util.js +29 -0
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
|
@@ -22,6 +22,7 @@ const { getLocalStateForCallFrame, evaluateCaptureExpressions } = require('./sna
|
|
|
22
22
|
const send = require('./send')
|
|
23
23
|
const { getStackFromCallFrames } = require('./state')
|
|
24
24
|
const { ackEmitting } = require('./status')
|
|
25
|
+
const WorkerError = require('./worker-error')
|
|
25
26
|
const config = require('./config')
|
|
26
27
|
const log = require('./log')
|
|
27
28
|
|
|
@@ -51,9 +52,10 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
51
52
|
|
|
52
53
|
if (params.reason !== 'other') {
|
|
53
54
|
// This error should not be caught, and should exit the worker thread, effectively stopping the debugging session
|
|
54
|
-
throw
|
|
55
|
-
reason:
|
|
56
|
-
|
|
55
|
+
throw new WorkerError(
|
|
56
|
+
`Unexpected Debugger.paused reason: ${params.reason}`,
|
|
57
|
+
WORKER_ERROR_REASON.UNEXPECTED_PAUSE_REASON
|
|
58
|
+
)
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
let maxReferenceDepth = 0
|
|
@@ -4,6 +4,7 @@ const { workerData: { probePort } } = require('node:worker_threads')
|
|
|
4
4
|
const { WORKER_ERROR_REASON } = require('../constants')
|
|
5
5
|
const { addBreakpoint, removeBreakpoint, modifyBreakpoint } = require('./breakpoints')
|
|
6
6
|
const { ackReceived, ackInstalled, ackError } = require('./status')
|
|
7
|
+
const WorkerError = require('./worker-error')
|
|
7
8
|
const log = require('./log')
|
|
8
9
|
|
|
9
10
|
// Example log line probe with captureSnapshot (simplified):
|
|
@@ -74,20 +75,23 @@ async function processMsg (action, probe) {
|
|
|
74
75
|
if (action !== 'unapply') ackReceived(probe)
|
|
75
76
|
|
|
76
77
|
if (probe.type !== 'LOG_PROBE') {
|
|
77
|
-
throw
|
|
78
|
-
`Unsupported probe type: ${probe.type} (id: ${probe.id}, version: ${probe.version})
|
|
79
|
-
|
|
78
|
+
throw new WorkerError(
|
|
79
|
+
`Unsupported probe type: ${probe.type} (id: ${probe.id}, version: ${probe.version})`,
|
|
80
|
+
WORKER_ERROR_REASON.UNSUPPORTED_PROBE_TYPE
|
|
81
|
+
)
|
|
80
82
|
}
|
|
81
83
|
if (!probe.where.sourceFile && !probe.where.lines) {
|
|
82
|
-
throw
|
|
84
|
+
throw new WorkerError(
|
|
83
85
|
// eslint-disable-next-line @stylistic/max-len
|
|
84
|
-
`Unsupported probe insertion point! Only line-based probes are supported (id: ${probe.id}, version: ${probe.version})
|
|
85
|
-
|
|
86
|
+
`Unsupported probe insertion point! Only line-based probes are supported (id: ${probe.id}, version: ${probe.version})`,
|
|
87
|
+
WORKER_ERROR_REASON.UNSUPPORTED_INSERTION_POINT
|
|
88
|
+
)
|
|
86
89
|
}
|
|
87
90
|
if (probe.captureSnapshot && probe.captureExpressions?.length > 0) {
|
|
88
|
-
throw
|
|
89
|
-
`Cannot set both captureSnapshot and captureExpressions (probe: ${probe.id}, version: ${probe.version})
|
|
90
|
-
|
|
91
|
+
throw new WorkerError(
|
|
92
|
+
`Cannot set both captureSnapshot and captureExpressions (probe: ${probe.id}, version: ${probe.version})`,
|
|
93
|
+
WORKER_ERROR_REASON.CONFLICTING_CAPTURE_OPTIONS
|
|
94
|
+
)
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
switch (action) {
|
|
@@ -103,8 +107,9 @@ async function processMsg (action, probe) {
|
|
|
103
107
|
ackInstalled(probe)
|
|
104
108
|
break
|
|
105
109
|
default:
|
|
106
|
-
throw
|
|
107
|
-
`Cannot process probe ${probe.id} (version: ${probe.version}) - unknown remote configuration action: ${action}
|
|
108
|
-
|
|
110
|
+
throw new WorkerError(
|
|
111
|
+
`Cannot process probe ${probe.id} (version: ${probe.version}) - unknown remote configuration action: ${action}`,
|
|
112
|
+
WORKER_ERROR_REASON.UNKNOWN_REMOTE_CONFIG_ACTION
|
|
113
|
+
)
|
|
109
114
|
}
|
|
110
115
|
}
|
|
@@ -17,8 +17,12 @@ const buildTags = require('./tags')
|
|
|
17
17
|
|
|
18
18
|
module.exports = send
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
// The backend caps a single debugger event at 1 MiB, but measures the CBOR-encoded event *after* the
|
|
21
|
+
// intake has enriched it with host tags, `ddtags` and other metadata we cannot size from here. Leave
|
|
22
|
+
// the same 4 KiB of headroom as dd-trace-go: crossing the backend cap is worse than pruning, since
|
|
23
|
+
// its truncation destroys the log message before it drops the snapshot.
|
|
24
|
+
const BACKEND_ENRICHMENT_HEADROOM_BYTES = 4 * 1024
|
|
25
|
+
const MAX_LOG_PAYLOAD_SIZE_BYTES = 1024 * 1024 - BACKEND_ENRICHMENT_HEADROOM_BYTES
|
|
22
26
|
|
|
23
27
|
const ddsource = 'dd_debugger'
|
|
24
28
|
const hostname = getHostname()
|
|
@@ -57,6 +61,7 @@ function send (message, logger, dd, snapshot, processTags, eventType, incomplete
|
|
|
57
61
|
ddsource,
|
|
58
62
|
hostname,
|
|
59
63
|
service,
|
|
64
|
+
timestamp: snapshot.timestamp,
|
|
60
65
|
message,
|
|
61
66
|
logger,
|
|
62
67
|
dd,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** @typedef {typeof import('../constants').WORKER_ERROR_REASON} WorkerErrorReasons */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* An error carrying a failure reason for telemetry. Its prototype does not survive the worker boundary, so the main
|
|
7
|
+
* thread must read `reason` instead of checking `instanceof`. `name` is left as `Error` because structured cloning
|
|
8
|
+
* only keeps built-in error names, and telemetry should report the same name however the error leaves the worker.
|
|
9
|
+
*/
|
|
10
|
+
class WorkerError extends Error {
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} message - The error message
|
|
13
|
+
* @param {WorkerErrorReasons[keyof WorkerErrorReasons]} reason - The failure reason reported in telemetry
|
|
14
|
+
*/
|
|
15
|
+
constructor (message, reason) {
|
|
16
|
+
super(message)
|
|
17
|
+
this.reason = reason
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = WorkerError
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* atomics, and the main thread periodically drains the counters into instrumentation telemetry metrics.
|
|
10
10
|
*
|
|
11
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 `
|
|
12
|
+
* shared with the other tracers. All metrics are counts in the `debugger` telemetry namespace.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const TELEMETRY_NAMESPACE = '
|
|
15
|
+
const TELEMETRY_NAMESPACE = 'debugger'
|
|
16
16
|
|
|
17
17
|
const EVENT_TYPE = Object.freeze({
|
|
18
18
|
SNAPSHOT: 0,
|
|
@@ -8,6 +8,7 @@ const dc = require('dc-polyfill')
|
|
|
8
8
|
const log = require('../log')
|
|
9
9
|
const { fetchAgentInfo } = require('../agent/info')
|
|
10
10
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
11
|
+
const { logGenericError } = require('../startup-log')
|
|
11
12
|
const getDebuggerConfig = require('./config')
|
|
12
13
|
const {
|
|
13
14
|
DEBUGGER_DIAGNOSTICS_V1,
|
|
@@ -15,6 +16,7 @@ const {
|
|
|
15
16
|
DEBUGGER_INPUT_V2,
|
|
16
17
|
INSPECT_SEGMENT_GLOBAL_PROPERTY,
|
|
17
18
|
METRICS_FLUSH_INTERVAL_MS,
|
|
19
|
+
WORKER_ERROR_REASON,
|
|
18
20
|
} = require('./constants')
|
|
19
21
|
const { GuardrailMetrics, TELEMETRY_NAMESPACE } = require('./guardrail-metrics')
|
|
20
22
|
const { PauseDurationHistogram } = require('./pause-duration-histogram')
|
|
@@ -33,6 +35,8 @@ const { configureProbeSampler, installProbeSampler, uninstallProbeSampler } = re
|
|
|
33
35
|
// be lost when the application exits on its own.
|
|
34
36
|
const TELEMETRY_APP_CLOSING_CHANNEL = 'datadog:telemetry:app-closing'
|
|
35
37
|
|
|
38
|
+
const WORKER_FILENAME = join(__dirname, 'devtools_client', 'index.js')
|
|
39
|
+
|
|
36
40
|
let worker = null
|
|
37
41
|
let configChannel = null
|
|
38
42
|
let ackId = 0
|
|
@@ -139,7 +143,7 @@ function start (config, rcInstance) {
|
|
|
139
143
|
inputPath = _inputPath
|
|
140
144
|
|
|
141
145
|
worker = new Worker(
|
|
142
|
-
|
|
146
|
+
WORKER_FILENAME,
|
|
143
147
|
{
|
|
144
148
|
name: 'dd-debugger',
|
|
145
149
|
execArgv: [], // Avoid worker thread inheriting the `-r` command line argument
|
|
@@ -165,7 +169,7 @@ function start (config, rcInstance) {
|
|
|
165
169
|
)
|
|
166
170
|
})
|
|
167
171
|
|
|
168
|
-
worker.on('error',
|
|
172
|
+
worker.on('error', onWorkerError)
|
|
169
173
|
worker.on('messageerror', (err) => log.error('[debugger] received "messageerror" from worker', err))
|
|
170
174
|
|
|
171
175
|
worker.once('exit', (code) => {
|
|
@@ -186,6 +190,24 @@ function start (config, rcInstance) {
|
|
|
186
190
|
})
|
|
187
191
|
}
|
|
188
192
|
|
|
193
|
+
/**
|
|
194
|
+
* @param {Error & { code?: unknown, requireStack?: unknown }} error - The error emitted by the worker thread
|
|
195
|
+
*/
|
|
196
|
+
function onWorkerError (error) {
|
|
197
|
+
// Node.js reports an empty require stack when the module it cannot find is the worker entry point itself, rather
|
|
198
|
+
// than one of its dependencies. This happens when the dd-trace files are not on disk where dd-trace expects them,
|
|
199
|
+
// e.g. because dd-trace was bundled into the application, or because some of its files were not deployed.
|
|
200
|
+
if (error.code === 'MODULE_NOT_FOUND' && Array.isArray(error.requireStack) && error.requireStack.length === 0) {
|
|
201
|
+
logGenericError(
|
|
202
|
+
`Dynamic Instrumentation is disabled, because its worker thread could not load ${WORKER_FILENAME}. ` +
|
|
203
|
+
'Make sure dd-trace is installed as regular files on disk and is not bundled into the application.'
|
|
204
|
+
)
|
|
205
|
+
logWorkerError(error, WORKER_ERROR_REASON.WORKER_ENTRY_POINT_NOT_FOUND)
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
logWorkerError(error)
|
|
209
|
+
}
|
|
210
|
+
|
|
189
211
|
/**
|
|
190
212
|
* Failure metadata belongs in the telemetry message; exception messages remain in the redacted cause.
|
|
191
213
|
*
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { types } = require('node:util')
|
|
4
4
|
|
|
5
|
+
const { storage } = require('../../../datadog-core')
|
|
5
6
|
const { MAX_SNAPSHOTS_PER_SECOND_GLOBALLY } = require('./devtools_client/defaults')
|
|
6
7
|
const { MAX_MESSAGE_LENGTH } = require('./constants')
|
|
7
8
|
const { EVENT_TYPE, SKIPPED_REASON } = require('./guardrail-metrics')
|
|
@@ -32,6 +33,20 @@ const ddTraceGlobal = /** @type {Record<symbol, SharedArrayBuffer | object | und
|
|
|
32
33
|
* @property {{ DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT_MS: number }} dynamicInstrumentation
|
|
33
34
|
*/
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The trace object shared by all spans of a trace in this process (`DatadogSpanContext#_trace`).
|
|
38
|
+
*
|
|
39
|
+
* @typedef {{ started: object[] }} LocalTrace
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The span active in the current async context, as stored in the legacy storage by the tracer.
|
|
44
|
+
*
|
|
45
|
+
* @typedef {{ context: () => { _trace: LocalTrace } }} ActiveSpan
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const legacyStorage = storage('legacy')
|
|
49
|
+
|
|
35
50
|
let evaluationTimeoutNs = 0n
|
|
36
51
|
|
|
37
52
|
module.exports = {
|
|
@@ -63,6 +78,14 @@ function installProbeSampler (guardrailMetrics, config) {
|
|
|
63
78
|
const buffer = createProbeSamplerBuffer()
|
|
64
79
|
|
|
65
80
|
const lastCaptureNsByProbeId = new Map()
|
|
81
|
+
/**
|
|
82
|
+
* The coordinated sampling decision of each trace in which a snapshot-producing probe was hit: `false` if the trace
|
|
83
|
+
* was dropped, otherwise the sampling indexes of the probes that already emitted a snapshot in it. Keyed weakly, so
|
|
84
|
+
* a decision is released together with its trace.
|
|
85
|
+
*
|
|
86
|
+
* @type {WeakMap<LocalTrace, Set<number> | false>}
|
|
87
|
+
*/
|
|
88
|
+
const emittedProbesByTrace = new WeakMap()
|
|
66
89
|
/**
|
|
67
90
|
* Probes that are skipped at entry because a recent evaluation failed or exceeded its time budget. One error result
|
|
68
91
|
* is reported per throttle window, so the probe stays visible without repeatedly paying for the evaluation.
|
|
@@ -219,7 +242,10 @@ function installProbeSampler (guardrailMetrics, config) {
|
|
|
219
242
|
}
|
|
220
243
|
|
|
221
244
|
/**
|
|
222
|
-
*
|
|
245
|
+
* Decide if a probe hit should be sampled and store the sampled probe index for the debugger worker.
|
|
246
|
+
*
|
|
247
|
+
* Snapshot-producing probes hit within an active trace share one sampling decision per trace, so a trace emits the
|
|
248
|
+
* snapshots of all of its probes or none of them. Other hits are sampled independently.
|
|
223
249
|
*
|
|
224
250
|
* @param {number} probeIndex - The worker-side probe sampling index.
|
|
225
251
|
* @param {string} probeId - The probe id.
|
|
@@ -228,29 +254,100 @@ function installProbeSampler (guardrailMetrics, config) {
|
|
|
228
254
|
* @param {boolean} isSnapshotProducingProbe - Whether this probe counts toward the global snapshot sample limit.
|
|
229
255
|
*/
|
|
230
256
|
function sample (probeIndex, probeId, now, nsBetweenSampling, isSnapshotProducingProbe) {
|
|
257
|
+
if (isSnapshotProducingProbe === true) {
|
|
258
|
+
const span = /** @type {ActiveSpan | null | undefined} */ (legacyStorage.getStore()?.span)
|
|
259
|
+
const trace = span?.context()._trace
|
|
260
|
+
// The span processor empties `started` once all spans of a trace have finished. A probe hit under such a trace
|
|
261
|
+
// runs in a context that outlived it, e.g. a connection pool or event emitter callback bound to a request that
|
|
262
|
+
// has since ended. Coordinating that hit would let a decision made for the old request silence the probe, or
|
|
263
|
+
// limit it to a single snapshot, for as long as the context lives, so it's sampled independently instead.
|
|
264
|
+
if (trace !== undefined && trace.started.length !== 0) {
|
|
265
|
+
return sampleInTrace(trace, probeIndex, probeId, now, nsBetweenSampling)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (isRateLimited(probeId, now, nsBetweenSampling, isSnapshotProducingProbe)) return false
|
|
270
|
+
return commitSample(probeIndex, probeId, now, isSnapshotProducingProbe)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Sample a snapshot-producing probe hit using the sampling decision of its trace. The first such probe hit in the
|
|
275
|
+
* trace makes the decision for all of them, subject to its per-probe rate limit and the global snapshot rate limit.
|
|
276
|
+
* If the trace is sampled, each probe then emits once in it without being subject to the rate limits, so the trace
|
|
277
|
+
* emits a complete set of snapshots. Those snapshots still count toward the rate limits, which keeps the overall
|
|
278
|
+
* volume close to the limits by making later traces less likely to be sampled.
|
|
279
|
+
*
|
|
280
|
+
* @param {LocalTrace} trace - The trace the probe was hit in.
|
|
281
|
+
* @param {number} probeIndex - The worker-side probe sampling index.
|
|
282
|
+
* @param {string} probeId - The probe id.
|
|
283
|
+
* @param {bigint} now - The current time.
|
|
284
|
+
* @param {bigint} nsBetweenSampling - Minimum nanoseconds between samples for this probe.
|
|
285
|
+
*/
|
|
286
|
+
function sampleInTrace (trace, probeIndex, probeId, now, nsBetweenSampling) {
|
|
287
|
+
const emittedProbes = emittedProbesByTrace.get(trace)
|
|
288
|
+
|
|
289
|
+
if (emittedProbes === undefined) {
|
|
290
|
+
if (isRateLimited(probeId, now, nsBetweenSampling, true)) {
|
|
291
|
+
emittedProbesByTrace.set(trace, false)
|
|
292
|
+
return false
|
|
293
|
+
}
|
|
294
|
+
// If the sampled probe can't be handed over, no decision is made, so the next probe hit in the trace makes it
|
|
295
|
+
if (!commitSample(probeIndex, probeId, now, true)) return false
|
|
296
|
+
emittedProbesByTrace.set(trace, new Set([probeIndex]))
|
|
297
|
+
return true
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (emittedProbes === false || emittedProbes.has(probeIndex)) {
|
|
301
|
+
guardrailMetrics.eventSkipped(SKIPPED_REASON.RATE_LIMIT_PROBE, EVENT_TYPE.SNAPSHOT)
|
|
302
|
+
return false
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!commitSample(probeIndex, probeId, now, true)) return false
|
|
306
|
+
emittedProbes.add(probeIndex)
|
|
307
|
+
return true
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Check the per-probe and global rate limits, recording the skip if a limit is reached.
|
|
312
|
+
*
|
|
313
|
+
* @param {string} probeId - The probe id.
|
|
314
|
+
* @param {bigint} now - The current time.
|
|
315
|
+
* @param {bigint} nsBetweenSampling - Minimum nanoseconds between samples for this probe.
|
|
316
|
+
* @param {boolean} isSnapshotProducingProbe - Whether this probe is subject to the global snapshot sample limit.
|
|
317
|
+
*/
|
|
318
|
+
function isRateLimited (probeId, now, nsBetweenSampling, isSnapshotProducingProbe) {
|
|
231
319
|
const lastCaptureNs = lastCaptureNsByProbeId.get(probeId)
|
|
232
320
|
if (lastCaptureNs !== undefined && now - lastCaptureNs < nsBetweenSampling) {
|
|
233
321
|
guardrailMetrics.eventSkipped(
|
|
234
322
|
SKIPPED_REASON.RATE_LIMIT_PROBE,
|
|
235
323
|
isSnapshotProducingProbe === true ? EVENT_TYPE.SNAPSHOT : EVENT_TYPE.LOG
|
|
236
324
|
)
|
|
237
|
-
return
|
|
325
|
+
return true
|
|
238
326
|
}
|
|
239
327
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
guardrailMetrics.eventSkipped(SKIPPED_REASON.RATE_LIMIT_GLOBAL, EVENT_TYPE.SNAPSHOT)
|
|
246
|
-
return false
|
|
247
|
-
}
|
|
328
|
+
if (isSnapshotProducingProbe === true &&
|
|
329
|
+
now - globalSnapshotSamplingRateWindowStart <= oneSecondNs &&
|
|
330
|
+
snapshotsSampledWithinTheLastSecond >= MAX_SNAPSHOTS_PER_SECOND_GLOBALLY) {
|
|
331
|
+
guardrailMetrics.eventSkipped(SKIPPED_REASON.RATE_LIMIT_GLOBAL, EVENT_TYPE.SNAPSHOT)
|
|
332
|
+
return true
|
|
248
333
|
}
|
|
249
334
|
|
|
335
|
+
return false
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Store the sampled probe index for the debugger worker and count the sample toward the rate limits.
|
|
340
|
+
*
|
|
341
|
+
* @param {number} probeIndex - The worker-side probe sampling index.
|
|
342
|
+
* @param {string} probeId - The probe id.
|
|
343
|
+
* @param {bigint} now - The current time.
|
|
344
|
+
* @param {boolean} isSnapshotProducingProbe - Whether this probe counts toward the global snapshot sample limit.
|
|
345
|
+
*/
|
|
346
|
+
function commitSample (probeIndex, probeId, now, isSnapshotProducingProbe) {
|
|
250
347
|
if (!storeSampledProbeIndex(probeIndex)) return false
|
|
251
348
|
|
|
252
349
|
if (isSnapshotProducingProbe === true) {
|
|
253
|
-
if (
|
|
350
|
+
if (now - globalSnapshotSamplingRateWindowStart > oneSecondNs) {
|
|
254
351
|
snapshotsSampledWithinTheLastSecond = 1
|
|
255
352
|
globalSnapshotSamplingRateWindowStart = now
|
|
256
353
|
} else {
|
|
@@ -533,7 +533,7 @@ class DogStatsDClient {
|
|
|
533
533
|
for (const [key, value] of Object.entries(config.tags)) {
|
|
534
534
|
// Skip runtime-id unless enabled as cardinality may be too high
|
|
535
535
|
if (typeof value === 'string' && (key !== 'runtime-id' || config.runtimeMetricsRuntimeId)) {
|
|
536
|
-
// https://docs.datadoghq.com/tagging/#
|
|
536
|
+
// https://docs.datadoghq.com/getting_started/tagging/#define-tags
|
|
537
537
|
const valueStripped = value.replaceAll(/[^a-z0-9_:./-]/ig, '_')
|
|
538
538
|
|
|
539
539
|
tags.push(`${key}:${valueStripped}`)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// From agent truncators: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/agent/truncator.go
|
|
4
4
|
|
|
5
|
-
// Values from: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/truncate.go
|
|
5
|
+
// Values from: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize/truncate.go
|
|
6
6
|
// MAX_RESOURCE_NAME_LENGTH the maximum length a span resource can have
|
|
7
7
|
const MAX_RESOURCE_NAME_LENGTH = 5000
|
|
8
8
|
// MAX_META_KEY_LENGTH the maximum length of metadata key
|
|
@@ -15,7 +15,7 @@ const MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION = 5000
|
|
|
15
15
|
const MAX_METRIC_KEY_LENGTH = MAX_META_KEY_LENGTH
|
|
16
16
|
|
|
17
17
|
// From agent normalizer:
|
|
18
|
-
// https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
|
|
18
|
+
// https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize/normalize.go
|
|
19
19
|
// DEFAULT_SPAN_NAME is the default name we assign a span if it's missing and we have no reasonable fallback
|
|
20
20
|
const DEFAULT_SPAN_NAME = 'unnamed_operation'
|
|
21
21
|
// DEFAULT_SERVICE_NAME is the default name we assign a service if it's missing and we have no reasonable fallback
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const { Readable } = require('stream')
|
|
7
7
|
const http = require('http')
|
|
8
8
|
const https = require('https')
|
|
9
|
+
const { URL } = require('url')
|
|
9
10
|
const zlib = require('zlib')
|
|
10
11
|
|
|
11
12
|
const { storage } = require('../../../../datadog-core')
|
|
@@ -85,11 +86,7 @@ function request (data, options, callback) {
|
|
|
85
86
|
const timeout = options.timeout || 2000
|
|
86
87
|
const isSecure = options.protocol === 'https:'
|
|
87
88
|
const client = isSecure ? https : http
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!Array.isArray(data)) {
|
|
91
|
-
dataArray = [data]
|
|
92
|
-
}
|
|
89
|
+
const dataArray = Array.isArray(data) ? data : [data]
|
|
93
90
|
const contentLength = byteLength(dataArray)
|
|
94
91
|
options.headers['Content-Length'] = contentLength
|
|
95
92
|
|
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const { randomFillSync } = require('crypto')
|
|
4
4
|
|
|
5
|
-
const { channel } = require('dc-polyfill')
|
|
6
|
-
|
|
7
5
|
const UINT_MAX = 4_294_967_296
|
|
8
6
|
|
|
9
7
|
const data = new Uint8Array(8 * 8192)
|
|
10
8
|
const zeroId = new Uint8Array(8)
|
|
9
|
+
// Reused for hex encoding: toString('hex') copies out, so the bytes need not outlive the call.
|
|
10
|
+
const hexScratch = Buffer.alloc(16)
|
|
11
11
|
|
|
12
12
|
let batch = 0
|
|
13
13
|
|
|
14
|
-
channel('datadog:identity:update').subscribe(reseed)
|
|
15
|
-
|
|
16
14
|
// Internal representation of a trace or span ID.
|
|
17
15
|
class Identifier {
|
|
18
16
|
/** @type {number[] | Uint8Array} */
|
|
@@ -39,7 +37,7 @@ class Identifier {
|
|
|
39
37
|
*/
|
|
40
38
|
toString (radix = 16) {
|
|
41
39
|
if (radix === 16) {
|
|
42
|
-
this.#stringHex ??=
|
|
40
|
+
this.#stringHex ??= toHexString(this.#buffer)
|
|
43
41
|
return this.#stringHex
|
|
44
42
|
}
|
|
45
43
|
if (radix === 10) {
|
|
@@ -194,6 +192,17 @@ function toNumberString (buffer, radix) {
|
|
|
194
192
|
return str
|
|
195
193
|
}
|
|
196
194
|
|
|
195
|
+
// Same string as Buffer.from(buffer).toString('hex'), without allocating a Buffer per call.
|
|
196
|
+
/**
|
|
197
|
+
* @param {number[] | Uint8Array} buffer
|
|
198
|
+
*/
|
|
199
|
+
function toHexString (buffer) {
|
|
200
|
+
const length = buffer.length
|
|
201
|
+
if (length > hexScratch.length) return Buffer.from(buffer).toString('hex')
|
|
202
|
+
for (let i = 0; i < length; i++) hexScratch[i] = buffer[i]
|
|
203
|
+
return hexScratch.toString('hex', 0, length)
|
|
204
|
+
}
|
|
205
|
+
|
|
197
206
|
// Simple pseudo-random 64-bit ID generator.
|
|
198
207
|
/**
|
|
199
208
|
* @returns {number[] | Uint8Array}
|
|
@@ -267,3 +276,4 @@ module.exports = function createIdentifier (value, radix) {
|
|
|
267
276
|
}
|
|
268
277
|
|
|
269
278
|
module.exports.Identifier = Identifier
|
|
279
|
+
module.exports.reseed = reseed
|