dd-trace 6.0.0 → 6.2.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/LICENSE-3rdparty.csv +1 -2
- package/index.d.ts +28 -1
- package/initialize.mjs +4 -2
- package/package.json +34 -30
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +5 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +31 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +19 -5
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mercurius.js +11 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +4 -5
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +15 -0
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +26 -1
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +103 -34
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +14 -23
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +351 -11
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-graphql/src/execute.js +25 -25
- package/packages/datadog-plugin-graphql/src/index.js +23 -1
- package/packages/datadog-plugin-graphql/src/request.js +104 -0
- package/packages/datadog-plugin-graphql/src/utils.js +186 -2
- package/packages/datadog-plugin-graphql/src/validate.js +25 -2
- package/packages/datadog-plugin-jest/src/index.js +30 -14
- package/packages/datadog-plugin-mocha/src/index.js +39 -14
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-playwright/src/index.js +3 -2
- package/packages/datadog-plugin-vitest/src/index.js +43 -9
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/appsec/graphql.js +9 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +3 -10
- package/packages/dd-trace/src/appsec/lambda.js +8 -8
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +75 -7
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +37 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +14 -2
- package/packages/dd-trace/src/encode/0.4.js +23 -2
- package/packages/dd-trace/src/encode/0.5.js +12 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +11 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +20 -2
- package/packages/dd-trace/src/exporters/common/writer.js +17 -1
- package/packages/dd-trace/src/guardrails/index.js +3 -1
- package/packages/dd-trace/src/guardrails/telemetry.js +40 -3
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/msgpack/chunk.js +33 -1
- package/packages/dd-trace/src/msgpack/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugin_manager.js +16 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +147 -7
- package/packages/dd-trace/src/plugins/index.js +5 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +6 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +8 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +5 -6
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { readFileSync } = require('node:fs')
|
|
4
|
+
const { extname } = require('node:path')
|
|
5
|
+
|
|
6
|
+
const getConfig = require('../../config')
|
|
7
|
+
const request = require('../../exporters/common/request')
|
|
8
|
+
const log = require('../../log')
|
|
9
|
+
|
|
10
|
+
const UPLOAD_TIMEOUT_MS = 30_000
|
|
11
|
+
const TEST_SCREENSHOT_ENDPOINT_PREFIX = '/api/v2/ci/test-runs/'
|
|
12
|
+
const TEST_SCREENSHOT_ENDPOINT_SUFFIX = '/media'
|
|
13
|
+
const UINT64_MAX = 18_446_744_073_709_551_615n
|
|
14
|
+
|
|
15
|
+
function getContentType (filePath) {
|
|
16
|
+
const extension = extname(filePath).toLowerCase()
|
|
17
|
+
if (extension === '.gif') {
|
|
18
|
+
return 'image/gif'
|
|
19
|
+
}
|
|
20
|
+
if (extension === '.jpg' || extension === '.jpeg') {
|
|
21
|
+
return 'image/jpeg'
|
|
22
|
+
}
|
|
23
|
+
if (extension === '.webp') {
|
|
24
|
+
return 'image/webp'
|
|
25
|
+
}
|
|
26
|
+
return 'image/png'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isValidTraceId (traceId) {
|
|
30
|
+
if (!/^[1-9]\d*$/.test(traceId)) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
return BigInt(traceId) <= UINT64_MAX
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Renders the idempotency key (`${traceId}:${basename(filePath)}`) into a value safe to carry in
|
|
38
|
+
* the upload's query string. The Agent's evp_proxy validates the forwarded query against a
|
|
39
|
+
* restrictive charset and rejects a raw Cypress filename (spaces, parens, non-ASCII), so the
|
|
40
|
+
* filename part is hex-encoded to [0-9a-f]; the decimal trace id and ':' separator are already in
|
|
41
|
+
* the allowed set and stay readable. Deterministic, so a retried upload reproduces the same key
|
|
42
|
+
* and the backend's UUIDv5 overwrite-on-retry holds.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} idempotencyKey - The raw idempotency key (`<traceId>:<filename>`)
|
|
45
|
+
* @returns {string} A query-safe, deterministic representation of the key
|
|
46
|
+
*/
|
|
47
|
+
function toIdempotencyQueryValue (idempotencyKey) {
|
|
48
|
+
const separatorIndex = idempotencyKey.indexOf(':')
|
|
49
|
+
if (separatorIndex === -1) {
|
|
50
|
+
return Buffer.from(idempotencyKey, 'utf8').toString('hex')
|
|
51
|
+
}
|
|
52
|
+
const traceIdPart = idempotencyKey.slice(0, separatorIndex)
|
|
53
|
+
const filenamePart = idempotencyKey.slice(separatorIndex + 1)
|
|
54
|
+
return `${traceIdPart}:${Buffer.from(filenamePart, 'utf8').toString('hex')}`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Uploads a single test screenshot to the Test Optimization media intake.
|
|
59
|
+
* The trace id is included in the request path and the body is the raw image bytes.
|
|
60
|
+
*
|
|
61
|
+
* The media service requires two values from the tracer, sent as query params so they
|
|
62
|
+
* survive the Agent's evp_proxy (which forwards only an allow-listed header set and would
|
|
63
|
+
* otherwise strip the metadata, leaving the backend with an empty idempotency key):
|
|
64
|
+
* - `idempotencyKey`: stable per artifact and reused on retry, so a retried
|
|
65
|
+
* upload overwrites the same stored object instead of creating a duplicate.
|
|
66
|
+
* - `capturedAtMs`: the capture time in epoch milliseconds, stamped once at
|
|
67
|
+
* capture and resent unchanged on retry (it is part of the stored object key).
|
|
68
|
+
*
|
|
69
|
+
* @param {object} options - Upload options
|
|
70
|
+
* @param {string} options.filePath - Path to the screenshot file
|
|
71
|
+
* @param {string} options.traceId - Test trace id used as the screenshot key
|
|
72
|
+
* @param {string} options.idempotencyKey - Stable per-artifact key, reused on retry
|
|
73
|
+
* @param {number} options.capturedAtMs - Capture time in epoch milliseconds
|
|
74
|
+
* @param {URL} options.url - The base URL for the screenshot upload
|
|
75
|
+
* @param {boolean} [options.isEvpProxy] - Whether to upload through the Agent's evp_proxy
|
|
76
|
+
* @param {string} [options.evpProxyPrefix] - The evp_proxy path prefix (e.g. '/evp_proxy/v4')
|
|
77
|
+
* @param {Function} callback - Callback function (err)
|
|
78
|
+
*/
|
|
79
|
+
function uploadTestScreenshot (
|
|
80
|
+
{ filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix },
|
|
81
|
+
callback
|
|
82
|
+
) {
|
|
83
|
+
const { DD_API_KEY } = getConfig()
|
|
84
|
+
|
|
85
|
+
if (!isValidTraceId(traceId)) {
|
|
86
|
+
return callback(new Error('A non-zero decimal uint64 trace_id is required for test screenshot upload'))
|
|
87
|
+
}
|
|
88
|
+
if (!DD_API_KEY && !isEvpProxy) {
|
|
89
|
+
return callback(new Error('DD_API_KEY is required for test screenshot upload'))
|
|
90
|
+
}
|
|
91
|
+
if (!idempotencyKey) {
|
|
92
|
+
return callback(new Error('An idempotency key is required for test screenshot upload'))
|
|
93
|
+
}
|
|
94
|
+
if (!Number.isInteger(capturedAtMs) || capturedAtMs <= 0) {
|
|
95
|
+
return callback(new Error('A positive captured-at timestamp (epoch ms) is required for test screenshot upload'))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let screenshotContent
|
|
99
|
+
try {
|
|
100
|
+
screenshotContent = readFileSync(filePath)
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return callback(new Error(`Failed to read screenshot at ${filePath}: ${err.message}`))
|
|
103
|
+
}
|
|
104
|
+
if (screenshotContent.length === 0) {
|
|
105
|
+
return callback(new Error(`Screenshot at ${filePath} is empty`))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Metadata rides the query string, not X-Dd-* headers: the Agent's evp_proxy strips
|
|
109
|
+
// non-allow-listed headers, so header-borne metadata reached the backend empty. The key is
|
|
110
|
+
// rendered proxy-safe (see toIdempotencyQueryValue) because evp_proxy also validates the
|
|
111
|
+
// forwarded query against a restrictive charset. capturedAtMs is a plain integer and part of the
|
|
112
|
+
// stored object key, so it (and the key) must stay stable across retries.
|
|
113
|
+
const query = new URLSearchParams({
|
|
114
|
+
idempotency_key: toIdempotencyQueryValue(idempotencyKey),
|
|
115
|
+
captured_at_ms: String(capturedAtMs),
|
|
116
|
+
}).toString()
|
|
117
|
+
const basePath = `${TEST_SCREENSHOT_ENDPOINT_PREFIX}${traceId}${TEST_SCREENSHOT_ENDPOINT_SUFFIX}`
|
|
118
|
+
|
|
119
|
+
const contentType = getContentType(filePath)
|
|
120
|
+
const options = {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': contentType,
|
|
124
|
+
},
|
|
125
|
+
path: `${basePath}?${query}`,
|
|
126
|
+
timeout: UPLOAD_TIMEOUT_MS,
|
|
127
|
+
url,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (isEvpProxy) {
|
|
131
|
+
// Agent mode: prefix the evp_proxy path, tell the proxy which subdomain to forward to, and
|
|
132
|
+
// drop the API key — the Agent injects it. The query params survive the proxy.
|
|
133
|
+
options.path = `${evpProxyPrefix}${basePath}?${query}`
|
|
134
|
+
options.headers['X-Datadog-EVP-Subdomain'] = 'api'
|
|
135
|
+
} else {
|
|
136
|
+
options.headers['DD-API-KEY'] = DD_API_KEY
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
log.debug('Uploading test screenshot %s to %s', filePath, new URL(options.path, url).href)
|
|
140
|
+
|
|
141
|
+
request(screenshotContent, options, (err, res, statusCode) => {
|
|
142
|
+
if (err) {
|
|
143
|
+
log.error('Error uploading test screenshot: %s', err.message)
|
|
144
|
+
return callback(err)
|
|
145
|
+
}
|
|
146
|
+
if (statusCode === undefined) {
|
|
147
|
+
const uploadError = new Error('Test screenshot upload request was dropped before it was sent')
|
|
148
|
+
log.error('Error uploading test screenshot: %s', uploadError.message)
|
|
149
|
+
return callback(uploadError)
|
|
150
|
+
}
|
|
151
|
+
log.debug('Test screenshot uploaded successfully (status: %d)', statusCode)
|
|
152
|
+
callback(null)
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = { TEST_SCREENSHOT_ENDPOINT_PREFIX, TEST_SCREENSHOT_ENDPOINT_SUFFIX, uploadTestScreenshot }
|
|
@@ -213,6 +213,7 @@ export interface GeneratedConfig {
|
|
|
213
213
|
DD_TRACE_BUNYAN_ENABLED: boolean;
|
|
214
214
|
DD_TRACE_CASSANDRA_DRIVER_ENABLED: boolean;
|
|
215
215
|
DD_TRACE_CHILD_PROCESS_ENABLED: boolean;
|
|
216
|
+
DD_TRACE_CLAUDE_AGENT_SDK_ENABLED: boolean;
|
|
216
217
|
DD_TRACE_COLLECTIONS_ENABLED: boolean;
|
|
217
218
|
DD_TRACE_COMMONPLUGIN_ENABLED: boolean;
|
|
218
219
|
DD_TRACE_CONFLUENTINC_KAFKA_JAVASCRIPT_ENABLED: boolean;
|
|
@@ -252,11 +253,14 @@ export interface GeneratedConfig {
|
|
|
252
253
|
DD_TRACE_GOOGLE_CLOUD_VERTEXAI_ENABLED: boolean;
|
|
253
254
|
DD_TRACE_GOOGLE_GAX_ENABLED: boolean;
|
|
254
255
|
DD_TRACE_GOOGLE_GENAI_ENABLED: boolean;
|
|
256
|
+
DD_TRACE_GRAPHQL_COLLAPSE: boolean;
|
|
257
|
+
DD_TRACE_GRAPHQL_DEPTH: number;
|
|
255
258
|
DD_TRACE_GRAPHQL_ENABLED: boolean;
|
|
256
259
|
DD_TRACE_GRAPHQL_ERROR_EXTENSIONS: string[];
|
|
257
260
|
DD_TRACE_GRAPHQL_TAG_ENABLED: boolean;
|
|
258
261
|
DD_TRACE_GRAPHQL_TOOLS_ENABLED: boolean;
|
|
259
262
|
DD_TRACE_GRAPHQL_TOOLS_EXECUTOR_ENABLED: boolean;
|
|
263
|
+
DD_TRACE_GRAPHQL_VARIABLES: string[];
|
|
260
264
|
DD_TRACE_GRAPHQL_YOGA_ENABLED: boolean;
|
|
261
265
|
DD_TRACE_GRPC_ENABLED: boolean;
|
|
262
266
|
DD_TRACE_GRPC_GRPC_JS_ENABLED: boolean;
|
|
@@ -558,6 +562,7 @@ export interface GeneratedConfig {
|
|
|
558
562
|
DD_CIVISIBILITY_TEST_MODULE_ID: string | undefined;
|
|
559
563
|
DD_CIVISIBILITY_TEST_SESSION_ID: string | undefined;
|
|
560
564
|
DD_TEST_FAILED_TEST_REPLAY_ENABLED: boolean;
|
|
565
|
+
DD_TEST_FAILURE_SCREENSHOTS_ENABLED: boolean | undefined;
|
|
561
566
|
DD_TEST_FLEET_CONFIG_PATH: string | undefined;
|
|
562
567
|
DD_TEST_LOCAL_CONFIG_PATH: string | undefined;
|
|
563
568
|
DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES: number;
|
|
@@ -790,6 +795,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
790
795
|
DD_TELEMETRY_LOG_COLLECTION_ENABLED: boolean;
|
|
791
796
|
DD_TELEMETRY_METRICS_ENABLED: boolean;
|
|
792
797
|
DD_TEST_FAILED_TEST_REPLAY_ENABLED: boolean;
|
|
798
|
+
DD_TEST_FAILURE_SCREENSHOTS_ENABLED: boolean | undefined;
|
|
793
799
|
DD_TEST_FLEET_CONFIG_PATH: string | undefined;
|
|
794
800
|
DD_TEST_LOCAL_CONFIG_PATH: string | undefined;
|
|
795
801
|
DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES: number;
|
|
@@ -868,6 +874,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
868
874
|
DD_TRACE_BUNYAN_ENABLED: boolean;
|
|
869
875
|
DD_TRACE_CASSANDRA_DRIVER_ENABLED: boolean;
|
|
870
876
|
DD_TRACE_CHILD_PROCESS_ENABLED: boolean;
|
|
877
|
+
DD_TRACE_CLAUDE_AGENT_SDK_ENABLED: boolean;
|
|
871
878
|
DD_TRACE_CLIENT_IP_ENABLED: boolean;
|
|
872
879
|
DD_TRACE_CLIENT_IP_HEADER: string | undefined;
|
|
873
880
|
DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH: number;
|
|
@@ -917,11 +924,14 @@ export interface GeneratedEnvVarConfig {
|
|
|
917
924
|
DD_TRACE_GOOGLE_CLOUD_VERTEXAI_ENABLED: boolean;
|
|
918
925
|
DD_TRACE_GOOGLE_GAX_ENABLED: boolean;
|
|
919
926
|
DD_TRACE_GOOGLE_GENAI_ENABLED: boolean;
|
|
927
|
+
DD_TRACE_GRAPHQL_COLLAPSE: boolean;
|
|
928
|
+
DD_TRACE_GRAPHQL_DEPTH: number;
|
|
920
929
|
DD_TRACE_GRAPHQL_ENABLED: boolean;
|
|
921
930
|
DD_TRACE_GRAPHQL_ERROR_EXTENSIONS: string[];
|
|
922
931
|
DD_TRACE_GRAPHQL_TAG_ENABLED: boolean;
|
|
923
932
|
DD_TRACE_GRAPHQL_TOOLS_ENABLED: boolean;
|
|
924
933
|
DD_TRACE_GRAPHQL_TOOLS_EXECUTOR_ENABLED: boolean;
|
|
934
|
+
DD_TRACE_GRAPHQL_VARIABLES: string[];
|
|
925
935
|
DD_TRACE_GRAPHQL_YOGA_ENABLED: boolean;
|
|
926
936
|
DD_TRACE_GRPC_ENABLED: boolean;
|
|
927
937
|
DD_TRACE_GRPC_GRPC_JS_ENABLED: boolean;
|
|
@@ -1819,6 +1819,14 @@
|
|
|
1819
1819
|
"namespace": "testOptimization"
|
|
1820
1820
|
}
|
|
1821
1821
|
],
|
|
1822
|
+
"DD_TEST_FAILURE_SCREENSHOTS_ENABLED": [
|
|
1823
|
+
{
|
|
1824
|
+
"implementation": "A",
|
|
1825
|
+
"type": "boolean",
|
|
1826
|
+
"default": null,
|
|
1827
|
+
"namespace": "testOptimization"
|
|
1828
|
+
}
|
|
1829
|
+
],
|
|
1822
1830
|
"DD_TEST_FLEET_CONFIG_PATH": [
|
|
1823
1831
|
{
|
|
1824
1832
|
"implementation": "A",
|
|
@@ -1946,6 +1954,13 @@
|
|
|
1946
1954
|
"default": "true"
|
|
1947
1955
|
}
|
|
1948
1956
|
],
|
|
1957
|
+
"DD_TRACE_CLAUDE_AGENT_SDK_ENABLED": [
|
|
1958
|
+
{
|
|
1959
|
+
"implementation": "A",
|
|
1960
|
+
"type": "boolean",
|
|
1961
|
+
"default": "true"
|
|
1962
|
+
}
|
|
1963
|
+
],
|
|
1949
1964
|
"DD_TRACE_APOLLO_ENABLED": [
|
|
1950
1965
|
{
|
|
1951
1966
|
"implementation": "A",
|
|
@@ -2742,6 +2757,21 @@
|
|
|
2742
2757
|
"default": "true"
|
|
2743
2758
|
}
|
|
2744
2759
|
],
|
|
2760
|
+
"DD_TRACE_GRAPHQL_COLLAPSE": [
|
|
2761
|
+
{
|
|
2762
|
+
"implementation": "A",
|
|
2763
|
+
"type": "boolean",
|
|
2764
|
+
"default": "true"
|
|
2765
|
+
}
|
|
2766
|
+
],
|
|
2767
|
+
"DD_TRACE_GRAPHQL_DEPTH": [
|
|
2768
|
+
{
|
|
2769
|
+
"implementation": "A",
|
|
2770
|
+
"type": "int",
|
|
2771
|
+
"default": "-1",
|
|
2772
|
+
"allowed": "-1|\\d+"
|
|
2773
|
+
}
|
|
2774
|
+
],
|
|
2745
2775
|
"DD_TRACE_GRAPHQL_ENABLED": [
|
|
2746
2776
|
{
|
|
2747
2777
|
"implementation": "A",
|
|
@@ -2777,6 +2807,13 @@
|
|
|
2777
2807
|
"default": "true"
|
|
2778
2808
|
}
|
|
2779
2809
|
],
|
|
2810
|
+
"DD_TRACE_GRAPHQL_VARIABLES": [
|
|
2811
|
+
{
|
|
2812
|
+
"implementation": "A",
|
|
2813
|
+
"type": "array",
|
|
2814
|
+
"default": ""
|
|
2815
|
+
}
|
|
2816
|
+
],
|
|
2780
2817
|
"DD_TRACE_GRAPHQL_YOGA_ENABLED": [
|
|
2781
2818
|
{
|
|
2782
2819
|
"implementation": "A",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { EOL } = require('node:os')
|
|
3
|
+
const { EOL, platform } = require('node:os')
|
|
4
4
|
|
|
5
5
|
// Load binding first to not import other modules if it throws
|
|
6
6
|
const libdatadog = require('@datadog/libdatadog')
|
|
@@ -71,7 +71,7 @@ class Crashtracker {
|
|
|
71
71
|
|
|
72
72
|
// Out-of-process symbolication currently works on
|
|
73
73
|
// Linux only, does not work on Mac.
|
|
74
|
-
const resolveMode =
|
|
74
|
+
const resolveMode = platform() === 'linux'
|
|
75
75
|
? 'EnabledWithSymbolsInReceiver'
|
|
76
76
|
: 'EnabledWithInprocessSymbols'
|
|
77
77
|
|
|
@@ -4,7 +4,7 @@ const zlib = require('zlib')
|
|
|
4
4
|
const pkg = require('../../../../package.json')
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const request = require('../exporters/common/request')
|
|
7
|
-
const { encode: encodeMsgpack } = require('../msgpack')
|
|
7
|
+
const { encode: encodeMsgpack, MAX_SIZE: MAX_CHUNK_SIZE } = require('../msgpack')
|
|
8
8
|
|
|
9
9
|
function makeRequest (data, url, cb) {
|
|
10
10
|
const options = {
|
|
@@ -36,7 +36,19 @@ class DataStreamsWriter {
|
|
|
36
36
|
log.debug('Maximum number of active requests reached. Payload discarded: %j', payload)
|
|
37
37
|
return
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
let encodedPayload
|
|
41
|
+
try {
|
|
42
|
+
encodedPayload = encodeMsgpack(payload)
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error.code !== 'ERR_MSGPACK_CHUNK_OVERFLOW') throw error
|
|
45
|
+
// The msgpack-encoded pipeline-stats payload exceeded the agent
|
|
46
|
+
// intake cap. Dropping it locally is safer than letting the
|
|
47
|
+
// RangeError crash the host process; the agent would reject the
|
|
48
|
+
// oversized payload at the network boundary anyway.
|
|
49
|
+
log.error('DataStreamsWriter dropped a payload that exceeded the %d byte chunk cap', MAX_CHUNK_SIZE)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
40
52
|
|
|
41
53
|
zlib.gzip(encodedPayload, { level: 1 }, (err, compressedData) => {
|
|
42
54
|
if (err) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const getConfig = require('../config')
|
|
4
|
-
const { MsgpackChunk } = require('../msgpack')
|
|
4
|
+
const { MsgpackChunk, MAX_SIZE: MAX_CHUNK_SIZE } = require('../msgpack')
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const { normalizeSpan, eventTimeNano } = require('./tags-processors')
|
|
7
7
|
|
|
@@ -265,7 +265,28 @@ class AgentEncoder {
|
|
|
265
265
|
|
|
266
266
|
this._traceCount++
|
|
267
267
|
|
|
268
|
-
|
|
268
|
+
try {
|
|
269
|
+
this._encode(bytes, trace)
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (error.code !== 'ERR_MSGPACK_CHUNK_OVERFLOW') throw error
|
|
272
|
+
// The trace, or the queued payload it joined, hit the chunk cap.
|
|
273
|
+
// Rolling back just the in-flight trace is unsafe: the string cache
|
|
274
|
+
// may already hold subarrays / indices pointing at bytes we'd
|
|
275
|
+
// discard, and the next encode would emit stale bytes against a
|
|
276
|
+
// smaller string table. Drop the whole queued payload so the next
|
|
277
|
+
// encode starts from a clean state. Emitting a partial buffer would
|
|
278
|
+
// corrupt the msgpack wire. The virtual `reset()` is called (not
|
|
279
|
+
// `_reset()`) so subclasses can clear their own per-payload state
|
|
280
|
+
// (e.g. `AgentlessCiVisibilityEncoder._eventCount`).
|
|
281
|
+
const dropped = this._traceCount
|
|
282
|
+
this.reset()
|
|
283
|
+
log.error(
|
|
284
|
+
'Trace encoder reset after exceeding the %d byte chunk cap; dropped %d trace(s)',
|
|
285
|
+
MAX_CHUNK_SIZE,
|
|
286
|
+
dropped
|
|
287
|
+
)
|
|
288
|
+
return
|
|
289
|
+
}
|
|
269
290
|
|
|
270
291
|
if (this.#debugEncoding) {
|
|
271
292
|
const end = bytes.length
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { MAX_SIZE, OverflowError } = require('../msgpack')
|
|
3
4
|
const { normalizeSpan } = require('./tags-processors')
|
|
4
5
|
const { AgentEncoder: BaseEncoder, stringifySpanEvents } = require('./0.4')
|
|
5
6
|
|
|
@@ -30,7 +31,17 @@ class AgentEncoder extends BaseEncoder {
|
|
|
30
31
|
const prefixSize = 1
|
|
31
32
|
const stringSize = this._stringBytes.length + 5
|
|
32
33
|
const traceSize = this._traceBytes.length + 5
|
|
33
|
-
const
|
|
34
|
+
const payloadSize = prefixSize + stringSize + traceSize
|
|
35
|
+
|
|
36
|
+
// The string table and the trace bytes are capped independently, so both
|
|
37
|
+
// can sit just under the cap while their concatenation crosses it. `encode`
|
|
38
|
+
// never sees this overflow — it only exists once the two are summed here —
|
|
39
|
+
// so the writer's flush-time catch drops the payload.
|
|
40
|
+
if (payloadSize > MAX_SIZE) {
|
|
41
|
+
throw new OverflowError(payloadSize)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const buffer = Buffer.allocUnsafe(payloadSize)
|
|
34
45
|
|
|
35
46
|
buffer[0] = ARRAY_OF_TWO
|
|
36
47
|
|
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
|
|
8
8
|
TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT,
|
|
9
9
|
} = require('../ci-visibility/telemetry')
|
|
10
|
-
const { MsgpackChunk } = require('../msgpack')
|
|
10
|
+
const { MsgpackChunk, MAX_SIZE, OverflowError } = require('../msgpack')
|
|
11
11
|
const { AgentEncoder } = require('./0.4')
|
|
12
12
|
const {
|
|
13
13
|
truncateSpanTestOpt,
|
|
@@ -371,6 +371,16 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
|
371
371
|
|
|
372
372
|
const eventsBytes = this._traceBytes
|
|
373
373
|
const totalSize = prefixBytes.length + eventsBytes.length
|
|
374
|
+
|
|
375
|
+
// The metadata prefix (built here, not during `encode`) and the events are
|
|
376
|
+
// capped independently, so both can stay under the cap while the assembled
|
|
377
|
+
// payload crosses it. An oversized metadata tag also overflows the prefix
|
|
378
|
+
// chunk itself inside `_encodePayloadStart` above; either way the tagged
|
|
379
|
+
// error propagates to the writer's flush-time catch to drop the payload.
|
|
380
|
+
if (totalSize > MAX_SIZE) {
|
|
381
|
+
throw new OverflowError(totalSize)
|
|
382
|
+
}
|
|
383
|
+
|
|
374
384
|
const buffer = Buffer.allocUnsafe(totalSize)
|
|
375
385
|
prefixBytes.buffer.copy(buffer, 0, 0, prefixBytes.length)
|
|
376
386
|
eventsBytes.buffer.copy(buffer, prefixBytes.length, 0, eventsBytes.length)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
const { MsgpackChunk } = require('../msgpack')
|
|
2
|
+
const { MsgpackChunk, MAX_SIZE: MAX_CHUNK_SIZE } = require('../msgpack')
|
|
3
3
|
|
|
4
4
|
const {
|
|
5
5
|
distributionMetric,
|
|
6
6
|
TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
|
|
7
7
|
TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT,
|
|
8
8
|
} = require('../ci-visibility/telemetry')
|
|
9
|
+
const log = require('../log')
|
|
9
10
|
const FormData = require('../exporters/common/form-data')
|
|
10
11
|
const { AgentEncoder } = require('./0.4')
|
|
11
12
|
|
|
@@ -40,7 +41,24 @@ class CoverageCIVisibilityEncoder extends AgentEncoder {
|
|
|
40
41
|
const startTime = Date.now()
|
|
41
42
|
|
|
42
43
|
this._coveragesCount++
|
|
43
|
-
|
|
44
|
+
try {
|
|
45
|
+
this.encodeCodeCoverage(this._coverageBytes, coverage)
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.code !== 'ERR_MSGPACK_CHUNK_OVERFLOW') throw error
|
|
48
|
+
// The coverage payload hit the chunk cap. `_coverageBytes` may
|
|
49
|
+
// hold a partial entry and a stale `coverages` array prefix. Drop
|
|
50
|
+
// the whole queued payload so the next encode starts from a clean
|
|
51
|
+
// state; the parent `AgentEncoder` does the equivalent for its own
|
|
52
|
+
// bytes / string cache via the inherited `_reset()`.
|
|
53
|
+
const dropped = this._coveragesCount
|
|
54
|
+
this.reset()
|
|
55
|
+
log.error(
|
|
56
|
+
'Coverage encoder reset after exceeding the %d byte chunk cap; dropped %d coverage(s)',
|
|
57
|
+
MAX_CHUNK_SIZE,
|
|
58
|
+
dropped
|
|
59
|
+
)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
44
62
|
|
|
45
63
|
distributionMetric(
|
|
46
64
|
TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
5
|
const log = require('../../log')
|
|
6
|
+
const { MAX_SIZE: MAX_CHUNK_SIZE } = require('../../msgpack')
|
|
6
7
|
const request = require('./request')
|
|
7
8
|
const { safeJSONStringify } = require('./util')
|
|
8
9
|
|
|
@@ -27,7 +28,22 @@ class Writer {
|
|
|
27
28
|
this.#isFirstFlush = false
|
|
28
29
|
this._beforeFirstFlush()
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
+
let payload
|
|
32
|
+
try {
|
|
33
|
+
payload = this._encoder.makePayload()
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error.code !== 'ERR_MSGPACK_CHUNK_OVERFLOW') throw error
|
|
36
|
+
// Multi-chunk encoders (v0.5, CI Visibility) only learn the assembled
|
|
37
|
+
// payload exceeds the cap when `makePayload` stitches the chunks
|
|
38
|
+
// together, after `encode` already returned — so the encode-time catch
|
|
39
|
+
// never sees it. Drop the queued payload here instead of letting the
|
|
40
|
+
// RangeError escape into the host application; the agent would reject
|
|
41
|
+
// the oversized payload at the network boundary anyway.
|
|
42
|
+
this._encoder.reset()
|
|
43
|
+
log.error('Writer dropped %d trace(s) that exceeded the %d byte chunk cap', count, MAX_CHUNK_SIZE)
|
|
44
|
+
done()
|
|
45
|
+
return
|
|
46
|
+
}
|
|
31
47
|
this._sendPayload(payload, count, done)
|
|
32
48
|
} else {
|
|
33
49
|
done()
|
|
@@ -48,6 +48,8 @@ function guard (fn) {
|
|
|
48
48
|
if (!clobberBailout && (NODE_MAJOR < minMajor || NODE_MAJOR >= nextMajor)) {
|
|
49
49
|
initBailout = true
|
|
50
50
|
var runtimeInfo = 'Incompatible runtime Node.js ' + version + ', supported runtimes: Node.js ' + supportedRange
|
|
51
|
+
// When not forced, the process bails out here and may call process.exit() right away;
|
|
52
|
+
// forward synchronously so the telemetry child can't outlive us and wedge the exit.
|
|
51
53
|
telemetry([
|
|
52
54
|
{ name: 'abort', tags: ['reason:incompatible_runtime'] },
|
|
53
55
|
{ name: 'abort.runtime', tags: [] }
|
|
@@ -55,7 +57,7 @@ function guard (fn) {
|
|
|
55
57
|
result: 'abort',
|
|
56
58
|
result_class: 'incompatible_runtime',
|
|
57
59
|
result_reason: runtimeInfo
|
|
58
|
-
})
|
|
60
|
+
}, !forced)
|
|
59
61
|
log.info('Aborting application instrumentation due to incompatible_runtime.')
|
|
60
62
|
log.info('Found incompatible runtime Node.js %s, Supported runtimes: Node.js %s.', version, supportedRange)
|
|
61
63
|
if (forced) {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var fs = require('fs')
|
|
4
|
-
|
|
4
|
+
// Capture the child_process functions at load time, before the tracer wraps the module.
|
|
5
|
+
// Reaching through require('child_process') at send time would route the forwarder through
|
|
6
|
+
// the tracer's own child_process instrumentation once the tracer is initialized.
|
|
7
|
+
var childProcess = require('child_process')
|
|
8
|
+
var spawn = childProcess.spawn
|
|
9
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
10
|
+
var spawnSync = childProcess.spawnSync
|
|
5
11
|
var tracerVersion = require('../../../../package.json').version
|
|
6
12
|
var log = require('./log')
|
|
7
13
|
|
|
@@ -47,7 +53,7 @@ function shouldSend (point) {
|
|
|
47
53
|
return true
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
function sendTelemetry (name, tags, resultMetadata) {
|
|
56
|
+
function sendTelemetry (name, tags, resultMetadata, synchronous) {
|
|
51
57
|
var points = name
|
|
52
58
|
if (typeof name === 'string') {
|
|
53
59
|
points = [{ name: name, tags: tags || [] }]
|
|
@@ -74,6 +80,37 @@ function sendTelemetry (name, tags, resultMetadata) {
|
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
|
|
83
|
+
var payload = JSON.stringify({ metadata: currentMetadata, points: points })
|
|
84
|
+
|
|
85
|
+
// A forwarder spawned asynchronously can still be tearing down its stdio pipes when the
|
|
86
|
+
// injected app calls process.exit(); on Node 24.0.0/24.1.x that deadlocks the exit
|
|
87
|
+
// (fixed upstream in 24.2), hanging short-lived single-step-install processes. On the
|
|
88
|
+
// bailout path the caller passes synchronous=true, so the child is fully reaped before we
|
|
89
|
+
// return and nothing survives to race the exit. spawnSync is only reached on that path,
|
|
90
|
+
// before any instrumentation is active, so it never traces the forwarder. It exists since
|
|
91
|
+
// Node 0.11.12; the guardrails still target >=0.8, which predates the exit bug anyway.
|
|
92
|
+
if (synchronous && spawnSync) {
|
|
93
|
+
// Bound the blocking send: this telemetry is best-effort and the whole point of the
|
|
94
|
+
// synchronous path is to avoid a hung exit, so a forwarder that wedges must not become a
|
|
95
|
+
// new hard hang. On timeout spawnSync kills the child and returns error.code ETIMEDOUT.
|
|
96
|
+
var result = spawnSync(telemetryForwarderPath, ['library_entrypoint'], {
|
|
97
|
+
input: payload,
|
|
98
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
99
|
+
timeout: 1000,
|
|
100
|
+
killSignal: 'SIGKILL'
|
|
101
|
+
})
|
|
102
|
+
if (result.error) {
|
|
103
|
+
if (result.error.code === 'ETIMEDOUT') {
|
|
104
|
+
log.error('Telemetry forwarder timed out')
|
|
105
|
+
} else {
|
|
106
|
+
log.error('Failed to spawn telemetry forwarder')
|
|
107
|
+
}
|
|
108
|
+
} else if (result.status) {
|
|
109
|
+
log.error('Telemetry forwarder exited with code', result.status)
|
|
110
|
+
}
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
77
114
|
var proc = spawn(telemetryForwarderPath, ['library_entrypoint'], {
|
|
78
115
|
stdio: 'pipe'
|
|
79
116
|
})
|
|
@@ -88,5 +125,5 @@ function sendTelemetry (name, tags, resultMetadata) {
|
|
|
88
125
|
proc.stdin.on('error', function () {
|
|
89
126
|
log.error('Failed to write telemetry data to telemetry forwarder')
|
|
90
127
|
})
|
|
91
|
-
proc.stdin.end(
|
|
128
|
+
proc.stdin.end(payload)
|
|
92
129
|
}
|