dd-trace 6.10.0 → 6.11.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/README.md +0 -17
- package/index.d.ts +108 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +11 -8
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/cucumber.js +26 -11
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/jest.js +25 -17
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
- package/packages/datadog-instrumentations/src/mocha/utils.js +130 -17
- package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
- package/packages/datadog-instrumentations/src/playwright.js +149 -2
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-mocha/src/index.js +56 -1
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +6 -0
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +14 -0
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -2
- package/packages/dd-trace/src/datastreams/processor.js +15 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
- package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
- package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +3 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/jest.js +4 -3
- package/packages/dd-trace/src/plugins/util/test.js +9 -9
- package/packages/dd-trace/src/plugins/util/web.js +3 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -127,7 +127,7 @@ class TestWorkerCiVisibilityExporter {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* @param {() => void} [onDone]
|
|
130
|
+
* @param {(error?: Error) => void} [onDone]
|
|
131
131
|
*/
|
|
132
132
|
flush (onDone) {
|
|
133
133
|
if (!onDone) {
|
|
@@ -141,9 +141,11 @@ class TestWorkerCiVisibilityExporter {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
let pendingWriters = this._telemetryWriter ? 4 : 3
|
|
144
|
-
|
|
144
|
+
let flushError
|
|
145
|
+
const onWriterFlushed = (error) => {
|
|
146
|
+
flushError ||= error
|
|
145
147
|
pendingWriters--
|
|
146
|
-
if (pendingWriters === 0) onDone()
|
|
148
|
+
if (pendingWriters === 0) onDone(flushError)
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
this._writer.flush(onWriterFlushed)
|
|
@@ -21,7 +21,7 @@ class Writer {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* @param {() => void} [onDone]
|
|
24
|
+
* @param {(error?: Error) => void} [onDone]
|
|
25
25
|
*/
|
|
26
26
|
flush (onDone) {
|
|
27
27
|
const count = this._encoder.count()
|
|
@@ -69,8 +69,9 @@ class Writer {
|
|
|
69
69
|
|
|
70
70
|
// child_process workers (jest default, cucumber)
|
|
71
71
|
if (process.send) {
|
|
72
|
-
process.send(payload, () => {
|
|
73
|
-
|
|
72
|
+
process.send(payload, (error) => {
|
|
73
|
+
if (error) log.error('Error sending message to parent process', error)
|
|
74
|
+
onDone(error)
|
|
74
75
|
})
|
|
75
76
|
return
|
|
76
77
|
}
|
|
@@ -82,9 +83,12 @@ class Writer {
|
|
|
82
83
|
parentPort.postMessage(payload)
|
|
83
84
|
} catch (error) {
|
|
84
85
|
log.error('Error posting message to parent port', error)
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
onDone(error)
|
|
87
|
+
return
|
|
87
88
|
}
|
|
89
|
+
// postMessage has no acknowledgement callback. Completion means the
|
|
90
|
+
// message was accepted by the local port, not processed by the parent.
|
|
91
|
+
onDone()
|
|
88
92
|
return
|
|
89
93
|
}
|
|
90
94
|
|
|
@@ -34,10 +34,11 @@ function parseSkippableSuitesResponse (
|
|
|
34
34
|
if (validateRequiredFields) {
|
|
35
35
|
validateSkippableTestsResponse(parsedResponse, { validationMode })
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
let coverage
|
|
38
38
|
const coverageByFilename = parsedResponse.meta?.coverage
|
|
39
39
|
if (coverageByFilename) {
|
|
40
40
|
for (const [filename, bitmap] of Object.entries(coverageByFilename)) {
|
|
41
|
+
coverage ??= {}
|
|
41
42
|
coverage[filename.replaceAll('\\', '/')] = bitmap
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const EPOCH_SECONDS_THRESHOLD = 1_000_000_000
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the delay requested by a Test Optimization intake rate-limit response.
|
|
7
|
+
*
|
|
8
|
+
* @param {import('node:http').IncomingHttpHeaders} [headers]
|
|
9
|
+
* @returns {number}
|
|
10
|
+
*/
|
|
11
|
+
function getRateLimitResetDelay (headers) {
|
|
12
|
+
let retryAfter = headers?.['retry-after']
|
|
13
|
+
if (Array.isArray(retryAfter)) retryAfter = retryAfter[0]
|
|
14
|
+
if (typeof retryAfter === 'string' && retryAfter.trim() !== '') {
|
|
15
|
+
const delaySeconds = Number(retryAfter)
|
|
16
|
+
if (Number.isFinite(delaySeconds)) {
|
|
17
|
+
if (delaySeconds >= 0) return delaySeconds * 1000
|
|
18
|
+
} else {
|
|
19
|
+
const resetTimestamp = Date.parse(retryAfter)
|
|
20
|
+
if (Number.isFinite(resetTimestamp)) return Math.max(0, resetTimestamp - Date.now())
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let reset = headers?.['x-ratelimit-reset']
|
|
25
|
+
if (Array.isArray(reset)) reset = reset[0]
|
|
26
|
+
if (typeof reset !== 'string' || reset.trim() === '') return NaN
|
|
27
|
+
|
|
28
|
+
const resetSeconds = Number(reset)
|
|
29
|
+
if (!Number.isFinite(resetSeconds) || resetSeconds < 0) return NaN
|
|
30
|
+
|
|
31
|
+
// Datadog defines this header as delay seconds. Preserve compatibility with
|
|
32
|
+
// realistic Unix timestamps without confusing ordinary durations with epochs.
|
|
33
|
+
if (resetSeconds >= EPOCH_SECONDS_THRESHOLD) {
|
|
34
|
+
return Math.max(0, resetSeconds * 1000 - Date.now())
|
|
35
|
+
}
|
|
36
|
+
return resetSeconds * 1000
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { getRateLimitResetDelay }
|
|
@@ -13,12 +13,13 @@ const {
|
|
|
13
13
|
singleJitteredDelay,
|
|
14
14
|
} = require('../../exporters/common/retry')
|
|
15
15
|
const { parseUrl } = require('../../exporters/common/url')
|
|
16
|
+
const { getRateLimitResetDelay } = require('./rate-limit')
|
|
16
17
|
|
|
17
18
|
const legacyStorage = storage('legacy')
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Simplified HTTP request for test optimization (library config). Uses common HTTP agents.
|
|
21
|
-
* Retries: 429 (with X-
|
|
22
|
+
* Retries: 429 (with Retry-After or X-RateLimit-Reset, max 30s wait),
|
|
22
23
|
* >=500 and transient network errors (5–7.5s delay with jitter). Max one retry.
|
|
23
24
|
* Destroys connections on errors to prevent reuse of bad connections. Preserves
|
|
24
25
|
* original status code across retries for telemetry.
|
|
@@ -98,11 +99,7 @@ function request (data, options, callback) {
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
if (res.statusCode === 429 && !hasRetried) {
|
|
101
|
-
const
|
|
102
|
-
const resetTs = (resetHeader === null || resetHeader === undefined)
|
|
103
|
-
? NaN
|
|
104
|
-
: Number.parseInt(resetHeader, 10)
|
|
105
|
-
const waitMs = Number.isFinite(resetTs) ? Math.max(0, resetTs * 1000 - Date.now()) : NaN
|
|
102
|
+
const waitMs = getRateLimitResetDelay(res.headers)
|
|
106
103
|
|
|
107
104
|
if (Number.isFinite(waitMs) && waitMs <= RATE_LIMIT_MAX_WAIT_MS) {
|
|
108
105
|
hasRetried = true
|
|
@@ -4,8 +4,8 @@ const { readFileSync } = require('node:fs')
|
|
|
4
4
|
const { extname } = require('node:path')
|
|
5
5
|
|
|
6
6
|
const getConfig = require('../../config')
|
|
7
|
-
const request = require('../../exporters/common/request')
|
|
8
7
|
const log = require('../../log')
|
|
8
|
+
const request = require('../exporters/request')
|
|
9
9
|
|
|
10
10
|
const UPLOAD_TIMEOUT_MS = 30_000
|
|
11
11
|
const TEST_SCREENSHOT_ENDPOINT_PREFIX = '/api/v2/ci/test-runs/'
|
|
@@ -74,10 +74,12 @@ function toIdempotencyQueryValue (idempotencyKey) {
|
|
|
74
74
|
* @param {URL} options.url - The base URL for the screenshot upload
|
|
75
75
|
* @param {boolean} [options.isEvpProxy] - Whether to upload through the Agent's evp_proxy
|
|
76
76
|
* @param {string} [options.evpProxyPrefix] - The evp_proxy path prefix (e.g. '/evp_proxy/v4')
|
|
77
|
+
* @param {number} [options.deadline] - Absolute finalization deadline in epoch milliseconds
|
|
78
|
+
* @param {AbortSignal} [options.signal] - Signal used to cancel the upload
|
|
77
79
|
* @param {Function} callback - Callback function (err)
|
|
78
80
|
*/
|
|
79
81
|
function uploadTestScreenshot (
|
|
80
|
-
{ filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix },
|
|
82
|
+
{ filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix, deadline, signal },
|
|
81
83
|
callback
|
|
82
84
|
) {
|
|
83
85
|
const { DD_API_KEY } = getConfig()
|
|
@@ -125,6 +127,8 @@ function uploadTestScreenshot (
|
|
|
125
127
|
path: `${basePath}?${query}`,
|
|
126
128
|
timeout: UPLOAD_TIMEOUT_MS,
|
|
127
129
|
url,
|
|
130
|
+
deadline,
|
|
131
|
+
signal,
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
if (isEvpProxy) {
|
|
@@ -209,6 +209,7 @@ export interface GeneratedConfig {
|
|
|
209
209
|
DD_TRACE_BEAUTIFUL_LOGS: boolean;
|
|
210
210
|
DD_TRACE_BLUEBIRD_ENABLED: boolean;
|
|
211
211
|
DD_TRACE_BODY_PARSER_ENABLED: boolean;
|
|
212
|
+
DD_TRACE_BROWSER_BUNYAN_ENABLED: boolean;
|
|
212
213
|
DD_TRACE_BSON_ENABLED: boolean;
|
|
213
214
|
DD_TRACE_BULLMQ_ENABLED: boolean;
|
|
214
215
|
DD_TRACE_BUNYAN_ENABLED: boolean;
|
|
@@ -340,6 +341,7 @@ export interface GeneratedConfig {
|
|
|
340
341
|
DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
|
|
341
342
|
DD_TRACE_OPENTELEMETRY_SDK_TRACE_NODE_ENABLED: boolean;
|
|
342
343
|
DD_TRACE_ORACLEDB_ENABLED: boolean;
|
|
344
|
+
DD_TRACE_OTEL_CTX_ENABLED: boolean;
|
|
343
345
|
DD_TRACE_OTEL_ENABLED: boolean;
|
|
344
346
|
DD_TRACE_OTEL_SEMANTICS_ENABLED: boolean;
|
|
345
347
|
DD_TRACE_PASSPORT_ENABLED: boolean;
|
|
@@ -894,6 +896,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
894
896
|
DD_TRACE_BEAUTIFUL_LOGS: boolean;
|
|
895
897
|
DD_TRACE_BLUEBIRD_ENABLED: boolean;
|
|
896
898
|
DD_TRACE_BODY_PARSER_ENABLED: boolean;
|
|
899
|
+
DD_TRACE_BROWSER_BUNYAN_ENABLED: boolean;
|
|
897
900
|
DD_TRACE_BSON_ENABLED: boolean;
|
|
898
901
|
DD_TRACE_BULLMQ_ENABLED: boolean;
|
|
899
902
|
DD_TRACE_BUNYAN_ENABLED: boolean;
|
|
@@ -1040,6 +1043,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
1040
1043
|
DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
|
|
1041
1044
|
DD_TRACE_OPENTELEMETRY_SDK_TRACE_NODE_ENABLED: boolean;
|
|
1042
1045
|
DD_TRACE_ORACLEDB_ENABLED: boolean;
|
|
1046
|
+
DD_TRACE_OTEL_CTX_ENABLED: boolean;
|
|
1043
1047
|
DD_TRACE_OTEL_ENABLED: boolean;
|
|
1044
1048
|
DD_TRACE_OTEL_SEMANTICS_ENABLED: boolean;
|
|
1045
1049
|
DD_TRACE_PARTIAL_FLUSH_MIN_SPANS: number;
|
|
@@ -15,6 +15,7 @@ const { isTrue } = require('../util')
|
|
|
15
15
|
const telemetry = require('../telemetry')
|
|
16
16
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
17
17
|
const {
|
|
18
|
+
getServerlessPlatformTags,
|
|
18
19
|
IS_SERVERLESS,
|
|
19
20
|
getIsGCPFunction,
|
|
20
21
|
getIsAzureFunction,
|
|
@@ -477,9 +478,13 @@ class Config extends ConfigBase {
|
|
|
477
478
|
|
|
478
479
|
// Apply the OTel sampler when the user opted into OTel traces or explicitly set the sampler.
|
|
479
480
|
// OTEL_TRACES_SAMPLER has `default: parentbased_always_on` (per OTel spec), so opt-in users
|
|
480
|
-
// that don't set the sampler still get parent-based sampling.
|
|
481
|
+
// that don't set the sampler still get parent-based sampling. Electron exporter spans go over
|
|
482
|
+
// the Electron SDK's IPC bridge rather than OTLP (see opentracing/tracer.js), so an
|
|
483
|
+
// OTEL_TRACES_EXPORTER=otlp set for an unrelated telemetry pipeline shouldn't also override
|
|
484
|
+
// dd-trace's own sampling policy in that case.
|
|
481
485
|
if (!trackedConfigOrigins.has('sampleRate') &&
|
|
482
|
-
(trackedConfigOrigins.has('OTEL_TRACES_SAMPLER') ||
|
|
486
|
+
(trackedConfigOrigins.has('OTEL_TRACES_SAMPLER') ||
|
|
487
|
+
(this.OTEL_TRACES_EXPORTER === 'otlp' && this.experimental.exporter !== 'electron'))) {
|
|
483
488
|
setAndTrack(this, 'sampleRate',
|
|
484
489
|
getFromOtelSamplerMap(this.OTEL_TRACES_SAMPLER, this.OTEL_TRACES_SAMPLER_ARG))
|
|
485
490
|
}
|
|
@@ -597,6 +602,12 @@ class Config extends ConfigBase {
|
|
|
597
602
|
this.tags.version = this.version
|
|
598
603
|
}
|
|
599
604
|
this.tags['runtime-id'] = RUNTIME_ID
|
|
605
|
+
const platformTags = getServerlessPlatformTags()
|
|
606
|
+
if (platformTags) {
|
|
607
|
+
for (let i = 0; i < platformTags.length; i += 2) {
|
|
608
|
+
this.tags[platformTags[i]] ??= platformTags[i + 1]
|
|
609
|
+
}
|
|
610
|
+
}
|
|
600
611
|
|
|
601
612
|
if (IS_SERVERLESS) {
|
|
602
613
|
setAndTrack(this, 'telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED', false)
|
|
@@ -58,6 +58,13 @@ function applyMajorOverrides (supportedConfigurations, majorVersion) {
|
|
|
58
58
|
delete supportedConfigurations.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED
|
|
59
59
|
// eslint-disable-next-line eslint-rules/eslint-env-aliases
|
|
60
60
|
dropAlias(supportedConfigurations.DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED, 'DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED')
|
|
61
|
+
|
|
62
|
+
if (majorVersion >= 7) {
|
|
63
|
+
// The Electron plugin moved to the Electron SDK and is opt-in (disabled by default) from v7 on,
|
|
64
|
+
// while remaining enabled by default on earlier majors (see ElectronPlugin.optIn).
|
|
65
|
+
const electronEntry = supportedConfigurations.DD_TRACE_ELECTRON_ENABLED?.[0]
|
|
66
|
+
if (electronEntry) electronEntry.default = 'false'
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
/**
|
|
@@ -2452,6 +2452,13 @@
|
|
|
2452
2452
|
"default": "true"
|
|
2453
2453
|
}
|
|
2454
2454
|
],
|
|
2455
|
+
"DD_TRACE_BROWSER_BUNYAN_ENABLED": [
|
|
2456
|
+
{
|
|
2457
|
+
"implementation": "A",
|
|
2458
|
+
"type": "boolean",
|
|
2459
|
+
"default": "true"
|
|
2460
|
+
}
|
|
2461
|
+
],
|
|
2455
2462
|
"DD_TRACE_BSON_ENABLED": [
|
|
2456
2463
|
{
|
|
2457
2464
|
"implementation": "A",
|
|
@@ -3518,6 +3525,13 @@
|
|
|
3518
3525
|
"default": "false"
|
|
3519
3526
|
}
|
|
3520
3527
|
],
|
|
3528
|
+
"DD_TRACE_OTEL_CTX_ENABLED": [
|
|
3529
|
+
{
|
|
3530
|
+
"implementation": "A",
|
|
3531
|
+
"type": "boolean",
|
|
3532
|
+
"default": "false"
|
|
3533
|
+
}
|
|
3534
|
+
],
|
|
3521
3535
|
"DD_TRACE_PARTIAL_FLUSH_MIN_SPANS": [
|
|
3522
3536
|
{
|
|
3523
3537
|
"implementation": "B",
|
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
getHeadersSize,
|
|
6
6
|
getMessageSize,
|
|
7
7
|
getSizeOrZero,
|
|
8
|
+
PATHWAY_FIELD_BYTES,
|
|
8
9
|
} = require('./size')
|
|
9
10
|
|
|
10
11
|
// This is only needed because DSM code is spread across existing tracing
|
|
@@ -121,6 +122,7 @@ module.exports = {
|
|
|
121
122
|
DataStreamsManager,
|
|
122
123
|
DataStreamsProcessor,
|
|
123
124
|
SchemaBuilder,
|
|
125
|
+
PATHWAY_FIELD_BYTES,
|
|
124
126
|
|
|
125
127
|
// These are small functions so they are exposed directly and not lazy loaded.
|
|
126
128
|
getAmqpMessageSize,
|
|
@@ -8,9 +8,16 @@ class DataStreamsManager {
|
|
|
8
8
|
this._dataStreamsProcessor = processor
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @param {string[]} edgeTags
|
|
13
|
+
* @param {import('../opentracing/span')|null} span
|
|
14
|
+
* @param {number} [payloadSize]
|
|
15
|
+
* @param {number} [pathwayContextSize] See `DataStreamsProcessor#setCheckpoint`.
|
|
16
|
+
* @returns {object|undefined}
|
|
17
|
+
*/
|
|
18
|
+
setCheckpoint (edgeTags, span, payloadSize = 0, pathwayContextSize) {
|
|
12
19
|
const ctx = this._dataStreamsProcessor.setCheckpoint(
|
|
13
|
-
edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize
|
|
20
|
+
edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize, pathwayContextSize
|
|
14
21
|
)
|
|
15
22
|
DataStreamsContext.setDataStreamsContext(ctx)
|
|
16
23
|
return ctx
|
|
@@ -5,25 +5,17 @@ const pkg = require('../../../../package.json')
|
|
|
5
5
|
|
|
6
6
|
const { LogCollapsingLowestDenseDDSketch } = require('../../../../vendor/dist/@datadog/sketches-js')
|
|
7
7
|
const { PATHWAY_HASH, DSM_TRANSACTION_ID, DSM_TRANSACTION_CHECKPOINT } = require('../../../../ext/tags')
|
|
8
|
-
const { dsmBase64NameLength } = require('../carrier')
|
|
9
8
|
const log = require('../log')
|
|
10
9
|
const processTags = require('../process-tags')
|
|
11
10
|
const propagationHash = require('../propagation-hash')
|
|
12
11
|
const { computePathwayHash } = require('./pathway')
|
|
13
12
|
const { DataStreamsWriter } = require('./writer')
|
|
14
|
-
const { getAmqpMessageSize, getHeadersSize, getMessageSize, getSizeOrZero } = require('./size')
|
|
13
|
+
const { getAmqpMessageSize, getHeadersSize, getMessageSize, getSizeOrZero, PATHWAY_FIELD_BYTES } = require('./size')
|
|
15
14
|
const { SchemaBuilder } = require('./schemas/schema_builder')
|
|
16
15
|
const { SchemaSampler } = require('./schemas/schema_sampler')
|
|
17
16
|
|
|
18
17
|
const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
|
|
19
18
|
|
|
20
|
-
// A direction:out checkpoint estimates the size cost of the header the
|
|
21
|
-
// producer plugin will inject. The pathway context is always 20 binary
|
|
22
|
-
// bytes, encoded as 28 base64 chars; together with the header key and
|
|
23
|
-
// JSON framing (matching the prior `JSON.stringify({key: value})` byte
|
|
24
|
-
// count minus 1), this is a fixed value.
|
|
25
|
-
const PATHWAY_HEADER_BYTES = dsmBase64NameLength + 28 + 6
|
|
26
|
-
|
|
27
19
|
class StatsPoint {
|
|
28
20
|
constructor (hash, parentHash, edgeTags) {
|
|
29
21
|
this.hash = hash.readBigUInt64LE()
|
|
@@ -290,10 +282,19 @@ class DataStreamsProcessor {
|
|
|
290
282
|
if (this.flushInterval === 0) this.onInterval()
|
|
291
283
|
}
|
|
292
284
|
|
|
293
|
-
|
|
285
|
+
/**
|
|
286
|
+
* @param {string[]} edgeTags Direction tag first.
|
|
287
|
+
* @param {import('../opentracing/span')|null} span
|
|
288
|
+
* @param {object|null|undefined} ctx Parent pathway context.
|
|
289
|
+
* @param {number} [payloadSize] Bytes the caller built, before any propagation context.
|
|
290
|
+
* @param {number} [pathwayContextSize] Bytes the pathway context adds to an outbound payload.
|
|
291
|
+
* Most producers size their payload before this call hands them the context to inject, so they
|
|
292
|
+
* cannot measure it and take the default estimate. Pass `0` to report the payload alone.
|
|
293
|
+
* @returns {object|undefined}
|
|
294
|
+
*/
|
|
295
|
+
setCheckpoint (edgeTags, span, ctx, payloadSize = 0, pathwayContextSize = PATHWAY_FIELD_BYTES) {
|
|
294
296
|
if (!this.enabled) return
|
|
295
297
|
const nowNs = Date.now() * 1e6
|
|
296
|
-
// Callers must place the direction tag at index 0.
|
|
297
298
|
const direction = edgeTags[0]
|
|
298
299
|
let pathwayStartNs = nowNs
|
|
299
300
|
let edgeStartNs = nowNs
|
|
@@ -338,14 +339,15 @@ class DataStreamsProcessor {
|
|
|
338
339
|
const pathwayLatencyNs = nowNs - pathwayStartNs
|
|
339
340
|
const dataStreamsContext = {
|
|
340
341
|
hash,
|
|
341
|
-
|
|
342
|
+
// start the next hop's edge clock here so edge latency stays per-hop
|
|
343
|
+
edgeStartNs: nowNs,
|
|
342
344
|
pathwayStartNs,
|
|
343
345
|
previousDirection: direction,
|
|
344
346
|
closestOppositeDirectionHash,
|
|
345
347
|
closestOppositeDirectionEdgeStart,
|
|
346
348
|
}
|
|
347
349
|
if (direction === 'direction:out') {
|
|
348
|
-
payloadSize +=
|
|
350
|
+
payloadSize += pathwayContextSize
|
|
349
351
|
}
|
|
350
352
|
const checkpoint = {
|
|
351
353
|
currentTimestamp: nowNs,
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const { types } = require('util')
|
|
4
4
|
|
|
5
|
+
// Byte cost of the `,"dd-pathway-ctx-base64":"<28 chars>"` field a producer adds to a JSON carrier.
|
|
6
|
+
// A literal so eager `size` consumers don't pull in `./pathway`; `pathway.spec.js` pins it there.
|
|
7
|
+
const PATHWAY_FIELD_BYTES = 55
|
|
8
|
+
|
|
5
9
|
function getSizeOrZero (obj) {
|
|
6
10
|
if (typeof obj === 'string') {
|
|
7
11
|
return Buffer.byteLength(obj, 'utf8')
|
|
@@ -54,4 +58,5 @@ module.exports = {
|
|
|
54
58
|
getHeadersSize,
|
|
55
59
|
getSizeOrZero,
|
|
56
60
|
getAmqpMessageSize,
|
|
61
|
+
PATHWAY_FIELD_BYTES,
|
|
57
62
|
}
|
|
@@ -23,7 +23,6 @@ require('./remote_config')
|
|
|
23
23
|
|
|
24
24
|
/** @typedef {import('node:inspector').Debugger.EvaluateOnCallFrameReturnType} EvaluateOnCallFrameResult */
|
|
25
25
|
|
|
26
|
-
// Expression to run on a call frame of the paused thread to get its active trace and span id.
|
|
27
26
|
const templateExpressionSetupCode = `
|
|
28
27
|
const $dd_inspect = global.require('node:util').inspect;
|
|
29
28
|
const $dd_segmentInspectOptions = {
|
|
@@ -34,8 +33,10 @@ const templateExpressionSetupCode = `
|
|
|
34
33
|
breakLength: Infinity
|
|
35
34
|
};
|
|
36
35
|
`
|
|
36
|
+
|
|
37
|
+
// Expression to run on a call frame of the paused thread to get its active trace and span id.
|
|
37
38
|
const getDDTagsExpression = `(() => {
|
|
38
|
-
const context =
|
|
39
|
+
const context = globalThis._ddtrace.scope().active()?.context();
|
|
39
40
|
return { trace_id: context?.toTraceId(), span_id: context?.toSpanId() }
|
|
40
41
|
})()`
|
|
41
42
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { inspect } = require('node:util')
|
|
4
4
|
const { channel } = require('dc-polyfill')
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const commonRequest = require('../common/request')
|
|
7
7
|
const { logIntegrations, logAgentError } = require('../../startup-log')
|
|
8
8
|
const runtimeMetrics = require('../../runtime_metrics')
|
|
9
9
|
const log = require('../../log')
|
|
@@ -15,12 +15,15 @@ const METRIC_PREFIX = 'datadog.tracer.node.exporter.agent'
|
|
|
15
15
|
const firstFlushChannel = channel('dd-trace:exporter:first-flush')
|
|
16
16
|
|
|
17
17
|
class AgentWriter extends BaseWriter {
|
|
18
|
+
#request = commonRequest
|
|
19
|
+
#requestTracker
|
|
20
|
+
|
|
18
21
|
constructor (...args) {
|
|
19
22
|
super({
|
|
20
23
|
...args[0],
|
|
21
24
|
beforeFirstFlush: () => firstFlushChannel.publish(),
|
|
22
25
|
})
|
|
23
|
-
const { prioritySampler, lookup, protocolVersion, headers } = args[0]
|
|
26
|
+
const { prioritySampler, lookup, protocolVersion, headers, isTestOptimization } = args[0]
|
|
24
27
|
const AgentEncoder = getEncoder(protocolVersion)
|
|
25
28
|
|
|
26
29
|
this._prioritySampler = prioritySampler
|
|
@@ -28,13 +31,33 @@ class AgentWriter extends BaseWriter {
|
|
|
28
31
|
this._protocolVersion = protocolVersion
|
|
29
32
|
this._headers = headers
|
|
30
33
|
this._encoder = new AgentEncoder(this)
|
|
34
|
+
if (isTestOptimization) {
|
|
35
|
+
this.#request = require('../../ci-visibility/exporters/request')
|
|
36
|
+
const TestOptimizationRequestTracker = require('../../ci-visibility/exporters/agentless/request-tracker')
|
|
37
|
+
this.#requestTracker = new TestOptimizationRequestTracker(this)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Flushes payloads, including requests already in flight during Test Optimization finalization.
|
|
43
|
+
*
|
|
44
|
+
* @param {(error?: Error) => void} [done]
|
|
45
|
+
* @param {{ deadline?: number }} [options]
|
|
46
|
+
* @returns {void}
|
|
47
|
+
*/
|
|
48
|
+
flush (done, options) {
|
|
49
|
+
if (this.#requestTracker) {
|
|
50
|
+
this.#requestTracker.flush(done, options)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
super.flush(done, options)
|
|
31
54
|
}
|
|
32
55
|
|
|
33
|
-
_sendPayload (data, count, done) {
|
|
56
|
+
_sendPayload (data, count, done, flushOptions) {
|
|
34
57
|
runtimeMetrics.increment(`${METRIC_PREFIX}.requests`, true)
|
|
35
58
|
|
|
36
59
|
const { _headers, _lookup, _protocolVersion, _url } = this
|
|
37
|
-
|
|
60
|
+
const onResponse = (err, res, status, headers) => {
|
|
38
61
|
if (status) {
|
|
39
62
|
runtimeMetrics.increment(`${METRIC_PREFIX}.responses`, true)
|
|
40
63
|
runtimeMetrics.increment(`${METRIC_PREFIX}.responses.by.status`, `status:${status}`, true)
|
|
@@ -49,7 +72,7 @@ class AgentWriter extends BaseWriter {
|
|
|
49
72
|
|
|
50
73
|
if (err) {
|
|
51
74
|
log.errorWithoutTelemetry('Error sending payload to the agent (status code: %s)', err.status, err)
|
|
52
|
-
done()
|
|
75
|
+
done(flushOptions?.deadline === undefined ? undefined : err)
|
|
53
76
|
return
|
|
54
77
|
}
|
|
55
78
|
|
|
@@ -74,7 +97,19 @@ class AgentWriter extends BaseWriter {
|
|
|
74
97
|
runtimeMetrics.increment(`${METRIC_PREFIX}.errors.by.name`, `name:${e.name}`, true)
|
|
75
98
|
}
|
|
76
99
|
done()
|
|
77
|
-
}
|
|
100
|
+
}
|
|
101
|
+
makeRequest(
|
|
102
|
+
_protocolVersion,
|
|
103
|
+
data,
|
|
104
|
+
count,
|
|
105
|
+
_url,
|
|
106
|
+
_headers,
|
|
107
|
+
_lookup,
|
|
108
|
+
flushOptions,
|
|
109
|
+
this.#request,
|
|
110
|
+
this.#requestTracker,
|
|
111
|
+
onResponse
|
|
112
|
+
)
|
|
78
113
|
}
|
|
79
114
|
}
|
|
80
115
|
|
|
@@ -84,7 +119,7 @@ function getEncoder (protocolVersion) {
|
|
|
84
119
|
: require('../../encode/0.4').AgentEncoder
|
|
85
120
|
}
|
|
86
121
|
|
|
87
|
-
function makeRequest (version, data, count, url, headers, lookup, cb) {
|
|
122
|
+
function makeRequest (version, data, count, url, headers, lookup, flushOptions, request, requestTracker, cb) {
|
|
88
123
|
const options = {
|
|
89
124
|
path: `/v${version}/traces`,
|
|
90
125
|
method: 'PUT',
|
|
@@ -100,16 +135,21 @@ function makeRequest (version, data, count, url, headers, lookup, cb) {
|
|
|
100
135
|
lookup,
|
|
101
136
|
url,
|
|
102
137
|
}
|
|
138
|
+
if (flushOptions?.deadline !== undefined) {
|
|
139
|
+
options.deadline = flushOptions.deadline
|
|
140
|
+
}
|
|
103
141
|
|
|
104
142
|
log.debug('Request to the agent: %j', options)
|
|
105
143
|
|
|
106
|
-
|
|
144
|
+
const onResponse = (err, res, status, headers) => {
|
|
107
145
|
logIntegrations()
|
|
108
146
|
if (status !== 404 && status !== 200 && err) {
|
|
109
147
|
logAgentError({ status, message: err.message ?? inspect(err) })
|
|
110
148
|
}
|
|
111
149
|
cb(err, res, status, headers)
|
|
112
|
-
}
|
|
150
|
+
}
|
|
151
|
+
if (requestTracker) requestTracker.send(request, data, options, onResponse)
|
|
152
|
+
else request(data, options, onResponse)
|
|
113
153
|
}
|
|
114
154
|
|
|
115
155
|
module.exports = AgentWriter
|
|
@@ -24,23 +24,25 @@ class BufferingExporter {
|
|
|
24
24
|
this._export(trace)
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
_export (payload, writer = this._writer, timerKey = '_timer') {
|
|
27
|
+
_export (payload, writer = this._writer, timerKey = '_timer', deferImmediateFlush = false) {
|
|
28
28
|
if (this._config.isCiVisibility) {
|
|
29
29
|
incrementCountMetric(TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION, {}, payload.length)
|
|
30
30
|
}
|
|
31
|
-
writer.append(payload)
|
|
31
|
+
const appended = writer.append(payload)
|
|
32
32
|
|
|
33
33
|
const { flushInterval } = this._config
|
|
34
34
|
|
|
35
|
-
if (flushInterval === 0) {
|
|
35
|
+
if (flushInterval === 0 && !deferImmediateFlush) {
|
|
36
36
|
writer.flush()
|
|
37
|
-
} else if (this[timerKey] === undefined) {
|
|
37
|
+
} else if (flushInterval !== 0 && this[timerKey] === undefined) {
|
|
38
38
|
this[timerKey] = setTimeout(() => {
|
|
39
39
|
writer.flush()
|
|
40
40
|
this[timerKey] = undefined
|
|
41
41
|
}, flushInterval)
|
|
42
42
|
this[timerKey].unref?.()
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
return appended
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
getUncodedTraces () {
|
|
@@ -17,10 +17,10 @@ class Writer {
|
|
|
17
17
|
|
|
18
18
|
#isFirstFlush = true
|
|
19
19
|
|
|
20
|
-
flush (done = () => {}) {
|
|
20
|
+
flush (done = () => {}, options) {
|
|
21
21
|
const count = this._encoder.count()
|
|
22
22
|
|
|
23
|
-
if (!request.writable) {
|
|
23
|
+
if (!request.writable && options?.deadline === undefined) {
|
|
24
24
|
this._encoder.reset()
|
|
25
25
|
done()
|
|
26
26
|
} else if (count > 0) {
|
|
@@ -44,23 +44,28 @@ class Writer {
|
|
|
44
44
|
done()
|
|
45
45
|
return
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
if (options === undefined) {
|
|
48
|
+
this._sendPayload(payload, count, done)
|
|
49
|
+
} else {
|
|
50
|
+
this._sendPayload(payload, count, done, options)
|
|
51
|
+
}
|
|
48
52
|
} else {
|
|
49
53
|
done()
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
append (payload) {
|
|
54
|
-
if (!request.writable) {
|
|
57
|
+
append (payload, options) {
|
|
58
|
+
if (!request.writable && options?.deadline === undefined) {
|
|
55
59
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
56
60
|
log.debug(() => `Maximum number of active requests reached. Payload discarded: ${safeJSONStringify(payload)}`)
|
|
57
|
-
return
|
|
61
|
+
return false
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
61
65
|
log.debug(() => `Encoding payload: ${safeJSONStringify(payload)}`)
|
|
62
66
|
|
|
63
67
|
this._encode(payload)
|
|
68
|
+
return true
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
_encode (payload) {
|