dd-trace 6.14.0 → 6.16.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 +15 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -2
- package/ci/test-optimization-validation/package-check.js +4 -1
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -1
- package/index.d.ts +12 -0
- package/next.d.ts +27 -0
- package/next.js +3 -0
- package/package.json +17 -13
- package/packages/datadog-esbuild/index.js +14 -1
- package/packages/datadog-esbuild/src/resolver.js +284 -0
- package/packages/datadog-esbuild/src/utils.js +163 -119
- package/packages/datadog-instrumentations/src/ai.js +39 -97
- package/packages/datadog-instrumentations/src/anthropic.js +76 -134
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -1
- package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
- package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
- package/packages/datadog-instrumentations/src/bullmq.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
- package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
- package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +72 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -64
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +46 -14
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +1 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -70
- package/packages/datadog-instrumentations/src/jest.js +141 -25
- package/packages/datadog-instrumentations/src/langchain.js +1 -1
- package/packages/datadog-instrumentations/src/langgraph.js +1 -1
- package/packages/datadog-instrumentations/src/mercurius.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +41 -5
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +54 -76
- package/packages/datadog-instrumentations/src/playwright.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +7 -10
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +96 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
- package/packages/datadog-plugin-graphql/src/resolve-error.js +33 -11
- package/packages/datadog-turbopack/index.js +212 -0
- package/packages/datadog-turbopack/src/constants.js +5 -0
- package/packages/datadog-turbopack/src/loader.js +500 -0
- package/packages/dd-trace/src/aiguard/index.js +2 -1
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
- package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
- package/packages/dd-trace/src/aiguard/integrations/index.js +3 -2
- package/packages/dd-trace/src/aiguard/integrations/openai.js +59 -30
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
- package/packages/dd-trace/src/aiguard/messages/utils.js +29 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -70
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
- package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
- package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +4 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +10 -2
- package/packages/dd-trace/src/config/index.js +20 -5
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -7
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +1 -6
- package/packages/dd-trace/src/evp_proxy/direct.js +2 -28
- package/packages/dd-trace/src/evp_proxy/discovery.js +3 -4
- package/packages/dd-trace/src/evp_proxy/path.js +12 -3
- package/packages/dd-trace/src/exporters/agentless/writer.js +3 -1
- package/packages/dd-trace/src/exporters/common/docker.js +4 -1
- package/packages/dd-trace/src/exporters/common/proxy.js +55 -0
- package/packages/dd-trace/src/exporters/common/request.js +11 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +106 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +8 -2
- package/packages/dd-trace/src/plugin_manager.js +9 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -1
- package/packages/dd-trace/src/priority_sampler.js +4 -0
- package/packages/dd-trace/src/sampling_rule.js +3 -1
- package/packages/dd-trace/src/span_processor.js +18 -3
- package/packages/dd-trace/src/telemetry/send-data.js +5 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
|
@@ -5,14 +5,13 @@ const {
|
|
|
5
5
|
incrementApiSecRequestNoSchema,
|
|
6
6
|
incrementApiSecRequestSchema,
|
|
7
7
|
} = require('./api_security')
|
|
8
|
-
const { DD_TELEMETRY_REQUEST_METRICS } = require('./common')
|
|
8
|
+
const { appsecMetrics, DD_TELEMETRY_REQUEST_METRICS, getVersionsTags } = require('./common')
|
|
9
9
|
const { incrementMissingUserId, incrementMissingUserLogin, incrementSdkEvent } = require('./user')
|
|
10
10
|
const {
|
|
11
11
|
addRaspRequestMetrics,
|
|
12
12
|
trackRaspMetrics,
|
|
13
13
|
trackRaspRuleMatch,
|
|
14
14
|
trackRaspRuleSkipped,
|
|
15
|
-
incrementRaspDurationMetrics,
|
|
16
15
|
} = require('./rasp')
|
|
17
16
|
const {
|
|
18
17
|
addWafRequestMetrics,
|
|
@@ -21,7 +20,6 @@ const {
|
|
|
21
20
|
incrementWafUpdates,
|
|
22
21
|
incrementWafConfigErrors,
|
|
23
22
|
incrementWafRequests,
|
|
24
|
-
incrementWafDurationMetrics,
|
|
25
23
|
} = require('./waf')
|
|
26
24
|
|
|
27
25
|
const metricsStoreMap = new WeakMap()
|
|
@@ -150,8 +148,28 @@ function incrementRequestDurationMetrics (req) {
|
|
|
150
148
|
const store = getStore(req)
|
|
151
149
|
const requestMetrics = store[DD_TELEMETRY_REQUEST_METRICS]
|
|
152
150
|
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
if (!requestMetrics.duration && !requestMetrics.durationExt &&
|
|
152
|
+
!requestMetrics.raspDuration && !requestMetrics.raspDurationExt) return
|
|
153
|
+
|
|
154
|
+
const { duration, durationExt, raspDuration, raspDurationExt, wafVersion, rulesVersion } = requestMetrics
|
|
155
|
+
|
|
156
|
+
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
157
|
+
|
|
158
|
+
if (duration) {
|
|
159
|
+
appsecMetrics.distribution('waf.duration', versionsTags).track(duration)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (durationExt) {
|
|
163
|
+
appsecMetrics.distribution('waf.duration_ext', versionsTags).track(durationExt)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (raspDuration) {
|
|
167
|
+
appsecMetrics.distribution('rasp.duration', versionsTags).track(raspDuration)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (raspDurationExt) {
|
|
171
|
+
appsecMetrics.distribution('rasp.duration_ext', versionsTags).track(raspDurationExt)
|
|
172
|
+
}
|
|
155
173
|
}
|
|
156
174
|
|
|
157
175
|
function incrementMissingUserLoginMetric (framework, eventType) {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { DD_TELEMETRY_REQUEST_METRICS, getVersionsTags } = require('./common')
|
|
5
|
-
|
|
6
|
-
const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
3
|
+
const { appsecMetrics, DD_TELEMETRY_REQUEST_METRICS, getVersionsTags } = require('./common')
|
|
7
4
|
|
|
8
5
|
const BLOCKING_STATUS = {
|
|
9
6
|
FAILURE: 'failure',
|
|
@@ -77,21 +74,6 @@ function trackRaspRuleMatch (store, raspRule, blockTriggered, blocked) {
|
|
|
77
74
|
appsecMetrics.count('rasp.rule.match', tags).inc(1)
|
|
78
75
|
}
|
|
79
76
|
|
|
80
|
-
function incrementRaspDurationMetrics (requestMetrics) {
|
|
81
|
-
const { raspDuration, raspDurationExt, wafVersion, rulesVersion } = requestMetrics
|
|
82
|
-
if (!raspDuration && !raspDurationExt) return
|
|
83
|
-
|
|
84
|
-
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
85
|
-
|
|
86
|
-
if (raspDuration) {
|
|
87
|
-
appsecMetrics.distribution('rasp.duration', versionsTags).track(raspDuration)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (raspDurationExt) {
|
|
91
|
-
appsecMetrics.distribution('rasp.duration_ext', versionsTags).track(raspDurationExt)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
77
|
function trackRaspRuleSkipped (raspRule, reason) {
|
|
96
78
|
const tags = { reason, rule_type: raspRule.type }
|
|
97
79
|
|
|
@@ -115,5 +97,4 @@ module.exports = {
|
|
|
115
97
|
trackRaspMetrics,
|
|
116
98
|
trackRaspRuleMatch,
|
|
117
99
|
trackRaspRuleSkipped,
|
|
118
|
-
incrementRaspDurationMetrics,
|
|
119
100
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
3
|
+
const { appsecMetrics } = require('./common')
|
|
6
4
|
|
|
7
5
|
function incrementMissingUserLogin (framework, eventType) {
|
|
8
6
|
appsecMetrics.count('instrum.user_auth.missing_user_login', {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { tags, getVersionsTags, DD_TELEMETRY_REQUEST_METRICS } = require('./common')
|
|
5
|
-
|
|
6
|
-
const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
3
|
+
const { appsecMetrics, tags, getVersionsTags, DD_TELEMETRY_REQUEST_METRICS } = require('./common')
|
|
7
4
|
|
|
8
5
|
const DD_TELEMETRY_WAF_RESULT_TAGS = Symbol('_dd.appsec.telemetry.waf.result.tags')
|
|
9
6
|
|
|
@@ -126,21 +123,6 @@ function incrementWafRequests (store) {
|
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
125
|
|
|
129
|
-
function incrementWafDurationMetrics (requestMetrics) {
|
|
130
|
-
const { duration, durationExt, wafVersion, rulesVersion } = requestMetrics
|
|
131
|
-
if (!duration && !durationExt) return
|
|
132
|
-
|
|
133
|
-
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
134
|
-
|
|
135
|
-
if (duration) {
|
|
136
|
-
appsecMetrics.distribution('waf.duration', versionsTags).track(duration)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (durationExt) {
|
|
140
|
-
appsecMetrics.distribution('waf.duration_ext', versionsTags).track(durationExt)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
126
|
function incrementTruncatedMetrics (metrics, truncationReason) {
|
|
145
127
|
const truncationTags = { truncation_reason: truncationReason }
|
|
146
128
|
appsecMetrics.count('waf.input_truncated', truncationTags).inc(1)
|
|
@@ -163,5 +145,4 @@ module.exports = {
|
|
|
163
145
|
incrementWafUpdates,
|
|
164
146
|
incrementWafConfigErrors,
|
|
165
147
|
incrementWafRequests,
|
|
166
|
-
incrementWafDurationMetrics,
|
|
167
148
|
}
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
singleJitteredDelay,
|
|
14
14
|
} = require('../../exporters/common/retry')
|
|
15
15
|
const { parseUrl } = require('../../exporters/common/url')
|
|
16
|
+
const { getHttpsProxyAgent } = require('../../exporters/common/proxy')
|
|
16
17
|
const { getRateLimitResetDelay } = require('./rate-limit')
|
|
17
18
|
|
|
18
19
|
const legacyStorage = storage('legacy')
|
|
@@ -58,6 +59,16 @@ function request (data, options, callback) {
|
|
|
58
59
|
opts.agent = isSecure ? httpsAgent : httpAgent
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
const hasApiKey = headers['dd-api-key'] !== undefined || headers['DD-API-KEY'] !== undefined
|
|
63
|
+
if (hasApiKey && isSecure) {
|
|
64
|
+
try {
|
|
65
|
+
opts.agent = getHttpsProxyAgent(opts, opts.agent)
|
|
66
|
+
} catch (error) {
|
|
67
|
+
callback(error)
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
let hasRetried = false
|
|
62
73
|
let firstStatusCode = null
|
|
63
74
|
|
|
@@ -6,6 +6,7 @@ const https = require('node:https')
|
|
|
6
6
|
const { storage } = require('../../../../datadog-core')
|
|
7
7
|
const log = require('../../log')
|
|
8
8
|
const { markEndpointReached } = require('../../exporters/common/retry')
|
|
9
|
+
const { getHttpsProxyAgent } = require('../../exporters/common/proxy')
|
|
9
10
|
const { isLoopbackHost, parseUrl } = require('../../exporters/common/url')
|
|
10
11
|
|
|
11
12
|
const legacyStorage = storage('legacy')
|
|
@@ -129,6 +130,9 @@ function getRequestOptions (options) {
|
|
|
129
130
|
delete requestOptions.headers['dd-api-key']
|
|
130
131
|
delete requestOptions.headers['DD-API-KEY']
|
|
131
132
|
}
|
|
133
|
+
if (hasApiKey && requestOptions.protocol === 'https:') {
|
|
134
|
+
requestOptions.agent = getHttpsProxyAgent(requestOptions, requestOptions.agent)
|
|
135
|
+
}
|
|
132
136
|
|
|
133
137
|
delete requestOptions.deadline
|
|
134
138
|
delete requestOptions.retry
|
|
@@ -68,6 +68,7 @@ export interface GeneratedConfig {
|
|
|
68
68
|
DD_AGENTLESS_ENABLED: boolean;
|
|
69
69
|
DD_AGENTLESS_LOG_SUBMISSION_ENABLED: boolean;
|
|
70
70
|
DD_AGENTLESS_LOG_SUBMISSION_URL: string | undefined;
|
|
71
|
+
DD_AI_GUARD_ANALYZE_STREAM_RESPONSES_ENABLED: boolean;
|
|
71
72
|
DD_API_KEY: string | undefined;
|
|
72
73
|
DD_APM_FLUSH_DEADLINE_MILLISECONDS: number;
|
|
73
74
|
DD_APP_KEY: string | undefined;
|
|
@@ -615,6 +616,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
615
616
|
DD_AGENTLESS_ENABLED: boolean;
|
|
616
617
|
DD_AGENTLESS_LOG_SUBMISSION_ENABLED: boolean;
|
|
617
618
|
DD_AGENTLESS_LOG_SUBMISSION_URL: string | undefined;
|
|
619
|
+
DD_AI_GUARD_ANALYZE_STREAM_RESPONSES_ENABLED: boolean;
|
|
618
620
|
DD_AI_GUARD_BLOCK: boolean;
|
|
619
621
|
DD_AI_GUARD_ENABLED: boolean;
|
|
620
622
|
DD_AI_GUARD_ENDPOINT: string | undefined;
|
|
@@ -143,10 +143,17 @@ function getEnvNameFromSource (name, source) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* @param {string} name
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
150
|
+
function isSupportedConfiguration (name) {
|
|
151
|
+
return supportedConfigurations[name] !== undefined || aliasToCanonical[name] !== undefined
|
|
152
|
+
}
|
|
153
|
+
|
|
146
154
|
function validateAccess (name) {
|
|
147
155
|
if ((name.startsWith('DD_') || name.startsWith('OTEL_')) &&
|
|
148
|
-
!
|
|
149
|
-
!aliasToCanonical[name]) {
|
|
156
|
+
!isSupportedConfiguration(name)) {
|
|
150
157
|
throw new Error(`Missing ${name} env/configuration in "supported-configurations.json" file.`)
|
|
151
158
|
}
|
|
152
159
|
}
|
|
@@ -284,6 +291,7 @@ function getValueFromEnvSources (name, skipDefault) {
|
|
|
284
291
|
|
|
285
292
|
module.exports = {
|
|
286
293
|
getValueFromEnvSources,
|
|
294
|
+
isSupportedConfiguration,
|
|
287
295
|
|
|
288
296
|
/**
|
|
289
297
|
* Expose raw stable config maps and warnings for consumers that need
|
|
@@ -24,6 +24,7 @@ const {
|
|
|
24
24
|
} = require('../serverless')
|
|
25
25
|
const { ORIGIN_KEY, DATADOG_MINI_AGENT_PATH } = require('../constants')
|
|
26
26
|
const { appendRules } = require('../payload-tagging/config')
|
|
27
|
+
const { createSiteUrl } = require('../exporters/common/url')
|
|
27
28
|
const ConfigBase = require('./config-base')
|
|
28
29
|
const {
|
|
29
30
|
getEnvironmentVariable,
|
|
@@ -495,9 +496,6 @@ class Config extends ConfigBase {
|
|
|
495
496
|
|
|
496
497
|
const agentlessTracingEnabled = this.DD_AGENTLESS_ENABLED ||
|
|
497
498
|
isTrue(getEnvironmentVariable('_DD_APM_TRACING_AGENTLESS_ENABLED'))
|
|
498
|
-
if (agentlessTracingEnabled && !this.isCiVisibility) {
|
|
499
|
-
setAndTrack(this, 'OTEL_TRACES_EXPORTER', 'none')
|
|
500
|
-
}
|
|
501
499
|
|
|
502
500
|
// Apply the OTel sampler when the user opted into OTel traces or explicitly set the sampler.
|
|
503
501
|
// OTEL_TRACES_SAMPLER has `default: parentbased_always_on` (per OTel spec), so opt-in users
|
|
@@ -699,16 +697,33 @@ class Config extends ConfigBase {
|
|
|
699
697
|
|
|
700
698
|
// TODO: This could likely be moved to the base class and allow easier GRPC handling
|
|
701
699
|
// Default OTLP endpoints follow the configured agent host so users who point DD at a custom
|
|
702
|
-
// agent (DD_AGENT_HOST / DD_TRACE_AGENT_URL) also reach OTLP on that host.
|
|
703
|
-
|
|
700
|
+
// agent (DD_AGENT_HOST / DD_TRACE_AGENT_URL) also reach OTLP on that host. In agentless mode
|
|
701
|
+
// there is no agent to relay through, so OTLP goes straight to the per-site intake instead.
|
|
702
|
+
const otlpAgentlessOrigin = agentlessTracingEnabled && !this.OTEL_EXPORTER_OTLP_ENDPOINT
|
|
703
|
+
? createSiteUrl(this.site, 'otlp')?.origin
|
|
704
|
+
: undefined
|
|
705
|
+
const defaultOtlpBase = otlpAgentlessOrigin ??
|
|
706
|
+
this.OTEL_EXPORTER_OTLP_ENDPOINT?.replace(/\/$/, '') ?? `http://${agentHostname}:4318`
|
|
707
|
+
|
|
708
|
+
const assignOtlpHeaderApiKey = (configName) => {
|
|
709
|
+
if (otlpAgentlessOrigin) {
|
|
710
|
+
this[configName] ??= { ...this.OTEL_EXPORTER_OTLP_HEADERS }
|
|
711
|
+
this[configName]['dd-api-key'] = this.DD_API_KEY
|
|
712
|
+
setAndTrack(this, configName, this[configName])
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
704
716
|
if (!this.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
|
|
705
717
|
setAndTrack(this, 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', `${defaultOtlpBase}/v1/logs`)
|
|
718
|
+
assignOtlpHeaderApiKey('OTEL_EXPORTER_OTLP_LOGS_HEADERS')
|
|
706
719
|
}
|
|
707
720
|
if (!this.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) {
|
|
708
721
|
setAndTrack(this, 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', `${defaultOtlpBase}/v1/metrics`)
|
|
722
|
+
assignOtlpHeaderApiKey('OTEL_EXPORTER_OTLP_METRICS_HEADERS')
|
|
709
723
|
}
|
|
710
724
|
if (!this.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) {
|
|
711
725
|
setAndTrack(this, 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', `${defaultOtlpBase}/v1/traces`)
|
|
726
|
+
assignOtlpHeaderApiKey('OTEL_EXPORTER_OTLP_TRACES_HEADERS')
|
|
712
727
|
}
|
|
713
728
|
|
|
714
729
|
const autoTraceMetrics = this.OTEL_TRACES_EXPORTER === 'otlp' && this.DD_METRICS_OTEL_ENABLED === true
|
|
@@ -53,6 +53,14 @@
|
|
|
53
53
|
]
|
|
54
54
|
}
|
|
55
55
|
],
|
|
56
|
+
"DD_AI_GUARD_ANALYZE_STREAM_RESPONSES_ENABLED": [
|
|
57
|
+
{
|
|
58
|
+
"implementation": "A",
|
|
59
|
+
"type": "boolean",
|
|
60
|
+
"default": "false",
|
|
61
|
+
"description": "Buffer Vercel AI streamed responses for After Model evaluation before replaying them."
|
|
62
|
+
}
|
|
63
|
+
],
|
|
56
64
|
"DD_AI_GUARD_BLOCK": [
|
|
57
65
|
{
|
|
58
66
|
"implementation": "C",
|
|
@@ -744,6 +752,7 @@
|
|
|
744
752
|
"configurationNames": [
|
|
745
753
|
"dogstatsd.port"
|
|
746
754
|
],
|
|
755
|
+
"allowed": "[1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]",
|
|
747
756
|
"default": "8125"
|
|
748
757
|
}
|
|
749
758
|
],
|
|
@@ -2028,6 +2037,7 @@
|
|
|
2028
2037
|
"configurationNames": [
|
|
2029
2038
|
"port"
|
|
2030
2039
|
],
|
|
2040
|
+
"allowed": "[1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]",
|
|
2031
2041
|
"default": "8126"
|
|
2032
2042
|
}
|
|
2033
2043
|
],
|
|
@@ -6,7 +6,11 @@ const { getGeneratedPosition } = require('./source-maps')
|
|
|
6
6
|
const session = require('./session')
|
|
7
7
|
const { compile, compileSegments, templateRequiresEvaluation } = require('./condition')
|
|
8
8
|
const { MAX_SNAPSHOTS_PER_SECOND_PER_PROBE, MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE } = require('./defaults')
|
|
9
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
compileBreakpointCondition,
|
|
11
|
+
getRemoveProbeExpression,
|
|
12
|
+
isSnapshotProducingProbe,
|
|
13
|
+
} = require('./probe_sampler')
|
|
10
14
|
const {
|
|
11
15
|
DEFAULT_MAX_REFERENCE_DEPTH,
|
|
12
16
|
DEFAULT_MAX_COLLECTION_SIZE,
|
|
@@ -76,12 +80,6 @@ async function addBreakpoint (probe) {
|
|
|
76
80
|
}
|
|
77
81
|
delete probe.segments
|
|
78
82
|
|
|
79
|
-
// Optimize for fast calculations when probe is hit
|
|
80
|
-
const snapshotsPerSecond = probe.sampling?.snapshotsPerSecond ?? (probe.captureSnapshot
|
|
81
|
-
? MAX_SNAPSHOTS_PER_SECOND_PER_PROBE
|
|
82
|
-
: MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE)
|
|
83
|
-
probe.nsBetweenSampling = BigInt(Math.trunc(1 / snapshotsPerSecond * 1e9))
|
|
84
|
-
|
|
85
83
|
// Warning: The code below relies on undocumented behavior of the inspector!
|
|
86
84
|
// It expects that `await session.post('Debugger.enable')` will wait for all loaded scripts to be emitted as
|
|
87
85
|
// `Debugger.scriptParsed` events. If this ever changes, we will have a race condition!
|
|
@@ -156,6 +154,15 @@ async function addBreakpoint (probe) {
|
|
|
156
154
|
}
|
|
157
155
|
}
|
|
158
156
|
|
|
157
|
+
// Must be calculated after `compiledCaptureExpressions` has been resolved, since capture-expression probes produce
|
|
158
|
+
// snapshots and therefore default to the snapshot rate.
|
|
159
|
+
//
|
|
160
|
+
// Optimize for fast calculations when probe is hit
|
|
161
|
+
const snapshotsPerSecond = probe.sampling?.snapshotsPerSecond ?? (isSnapshotProducingProbe(probe)
|
|
162
|
+
? MAX_SNAPSHOTS_PER_SECOND_PER_PROBE
|
|
163
|
+
: MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE)
|
|
164
|
+
probe.nsBetweenSampling = BigInt(Math.trunc(1 / snapshotsPerSecond * 1e9))
|
|
165
|
+
|
|
159
166
|
const locationKey = generateLocationKey(scriptId, lineNumber, columnNumber)
|
|
160
167
|
const breakpoint = locationToBreakpoint.get(locationKey)
|
|
161
168
|
|
|
@@ -8,6 +8,19 @@ const SAMPLER_EXPRESSION = `globalThis[Symbol.for(${JSON.stringify(DD_TRACE_SYMB
|
|
|
8
8
|
module.exports = {
|
|
9
9
|
compileBreakpointCondition,
|
|
10
10
|
getRemoveProbeExpression,
|
|
11
|
+
isSnapshotProducingProbe,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Determine whether a probe produces snapshots. Probes that capture the full local state and probes that only capture
|
|
16
|
+
* specific expressions both emit snapshot payloads, so they share the snapshot sampling defaults and count towards the
|
|
17
|
+
* global snapshot rate limit.
|
|
18
|
+
*
|
|
19
|
+
* @param {{ captureSnapshot?: boolean, compiledCaptureExpressions?: object[] }} probe - The probe to inspect.
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
function isSnapshotProducingProbe (probe) {
|
|
23
|
+
return probe.captureSnapshot === true || probe.compiledCaptureExpressions !== undefined
|
|
11
24
|
}
|
|
12
25
|
|
|
13
26
|
/**
|
|
@@ -66,7 +79,7 @@ function compileBreakpointCondition (probes) {
|
|
|
66
79
|
*/
|
|
67
80
|
function compileProbeCondition (probe) {
|
|
68
81
|
const sample = `$dd_sampler.makeSampleDecision(${probe.samplingIndex}, ${JSON.stringify(probe.id)}, ` +
|
|
69
|
-
`${probe.nsBetweenSampling}n, ${probe
|
|
82
|
+
`${probe.nsBetweenSampling}n, ${isSnapshotProducingProbe(probe)})`
|
|
70
83
|
|
|
71
84
|
if (probe.condition === undefined) {
|
|
72
85
|
return `$dd_sampled = ${sample} || $dd_sampled`
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { getHttpsProxyAgent } = require('../../evp_proxy/direct')
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* @param {ReturnType<import('../config')>} config - Debugger configuration
|
|
7
5
|
* @param {string} path - Request path
|
|
@@ -10,8 +8,7 @@ const { getHttpsProxyAgent } = require('../../evp_proxy/direct')
|
|
|
10
8
|
* method: 'POST',
|
|
11
9
|
* url: string | URL,
|
|
12
10
|
* path: string,
|
|
13
|
-
* headers: Record<string, string
|
|
14
|
-
* agent?: import('node:https').Agent
|
|
11
|
+
* headers: Record<string, string>
|
|
15
12
|
* }}
|
|
16
13
|
*/
|
|
17
14
|
module.exports = function getRequestOptions (config, path, headers) {
|
|
@@ -25,8 +22,6 @@ module.exports = function getRequestOptions (config, path, headers) {
|
|
|
25
22
|
if (config.agentless) {
|
|
26
23
|
if (config.apiKey !== undefined) headers['DD-API-KEY'] = config.apiKey
|
|
27
24
|
headers['DD-EVP-ORIGIN'] = 'agent-debugger'
|
|
28
|
-
const agent = getHttpsProxyAgent(config.url.href)
|
|
29
|
-
if (agent !== undefined) options.agent = agent
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
return options
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { HttpsProxyAgent } = require('../../../../vendor/dist/https-proxy-agent')
|
|
4
|
-
const { getProxyForUrl } = require('../../../../vendor/dist/proxy-from-env')
|
|
5
3
|
const { createSiteUrl } = require('../exporters/common/url')
|
|
6
4
|
const log = require('../log')
|
|
7
5
|
|
|
8
|
-
/** @type {Map<string, import('node:https').Agent>} */
|
|
9
|
-
const proxyAgents = new Map()
|
|
10
|
-
|
|
11
6
|
/**
|
|
12
7
|
* @typedef {object} DirectEVPRoute
|
|
13
8
|
* @property {URL} url - Direct intake URL
|
|
14
9
|
* @property {string} basePath - Direct intake base path
|
|
15
10
|
* @property {object} headers - Direct intake authentication headers
|
|
16
|
-
* @property {import('node:https').Agent} [agent] - Optional HTTPS proxy agent
|
|
17
11
|
*/
|
|
18
12
|
|
|
19
13
|
/**
|
|
@@ -33,36 +27,16 @@ function createDirectEVPRoute (config, intake) {
|
|
|
33
27
|
const url = createSiteUrl(config.site, intake)
|
|
34
28
|
if (url === undefined) throw new Error('Invalid direct EVP intake URL')
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const route = {
|
|
30
|
+
return {
|
|
39
31
|
url,
|
|
40
32
|
basePath: '',
|
|
41
33
|
headers: {
|
|
42
34
|
'DD-API-KEY': apiKey,
|
|
43
35
|
},
|
|
44
36
|
}
|
|
45
|
-
if (agent) route.agent = agent
|
|
46
|
-
return route
|
|
47
37
|
} catch (error) {
|
|
48
38
|
log.debug('Unable to configure direct EVP intake: %s', error.message)
|
|
49
39
|
}
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
* @param {string} url
|
|
54
|
-
* @returns {import('node:https').Agent|undefined}
|
|
55
|
-
*/
|
|
56
|
-
function getHttpsProxyAgent (url) {
|
|
57
|
-
const proxyUrl = getProxyForUrl(url)
|
|
58
|
-
if (!proxyUrl) return
|
|
59
|
-
|
|
60
|
-
let agent = proxyAgents.get(proxyUrl)
|
|
61
|
-
if (agent === undefined) {
|
|
62
|
-
agent = new HttpsProxyAgent(proxyUrl)
|
|
63
|
-
proxyAgents.set(proxyUrl, agent)
|
|
64
|
-
}
|
|
65
|
-
return agent
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
module.exports = { createDirectEVPRoute, getHttpsProxyAgent }
|
|
42
|
+
module.exports = { createDirectEVPRoute }
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { fetchAgentInfo } = require('../agent/info')
|
|
4
4
|
const log = require('../log')
|
|
5
|
-
|
|
6
|
-
const TRAILING_SLASHES = /\/+$/
|
|
5
|
+
const { stripTrailingSlashes } = require('./path')
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Receiver discovery contract
|
|
@@ -81,14 +80,14 @@ function selectEVPProxyPath (agentInfo, { supportedPaths, requiredHeaders = [] }
|
|
|
81
80
|
const advertisedPaths = new Set()
|
|
82
81
|
for (const endpoint of agentInfo.endpoints) {
|
|
83
82
|
if (typeof endpoint === 'string') {
|
|
84
|
-
advertisedPaths.add(endpoint
|
|
83
|
+
advertisedPaths.add(stripTrailingSlashes(endpoint))
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
for (const supportedPath of supportedPaths) {
|
|
89
88
|
if (typeof supportedPath !== 'string') continue
|
|
90
89
|
|
|
91
|
-
const normalizedPath = supportedPath
|
|
90
|
+
const normalizedPath = stripTrailingSlashes(supportedPath)
|
|
92
91
|
if (advertisedPaths.has(normalizedPath)) {
|
|
93
92
|
return normalizedPath
|
|
94
93
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const LEADING_SLASHES = /^\/+/
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} value
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
function stripTrailingSlashes (value) {
|
|
10
|
+
let end = value.length
|
|
11
|
+
while (end > 0 && value[end - 1] === '/') end--
|
|
12
|
+
return end === value.length ? value : value.slice(0, end)
|
|
13
|
+
}
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
16
|
* Joins a caller-supplied EVP proxy path and product endpoint.
|
|
@@ -13,10 +22,10 @@ const TRAILING_SLASHES = /\/+$/
|
|
|
13
22
|
* @returns {string} Joined request path
|
|
14
23
|
*/
|
|
15
24
|
function joinEVPProxyPath (basePath, endpoint) {
|
|
16
|
-
const normalizedBasePath = basePath
|
|
25
|
+
const normalizedBasePath = stripTrailingSlashes(basePath)
|
|
17
26
|
const normalizedEndpoint = endpoint.replace(LEADING_SLASHES, '')
|
|
18
27
|
|
|
19
28
|
return `${normalizedBasePath}/${normalizedEndpoint}`
|
|
20
29
|
}
|
|
21
30
|
|
|
22
|
-
module.exports = { joinEVPProxyPath }
|
|
31
|
+
module.exports = { joinEVPProxyPath, stripTrailingSlashes }
|
|
@@ -8,6 +8,7 @@ const log = require('../../log')
|
|
|
8
8
|
const tracerVersion = require('../../../../../package.json').version
|
|
9
9
|
|
|
10
10
|
const { canSendApiKey } = require('../common/url')
|
|
11
|
+
const { getHttpsProxyAgent } = require('../common/proxy')
|
|
11
12
|
const BaseWriter = require('../common/writer')
|
|
12
13
|
const { AgentEncoder } = require('../../encode/0.4')
|
|
13
14
|
const { computeIntakeUrl, INTAKE_PATH } = require('./intake')
|
|
@@ -153,6 +154,7 @@ class AgentlessWriter extends BaseWriter {
|
|
|
153
154
|
|
|
154
155
|
this.#closeExporter()
|
|
155
156
|
const config = getConfig()
|
|
157
|
+
const agent = this._url.protocol === 'https:' ? getHttpsProxyAgent(this._url) : undefined
|
|
156
158
|
this.#exporter = createAgentlessExporter({
|
|
157
159
|
endpoint: this.#endpoint(),
|
|
158
160
|
apiKey,
|
|
@@ -165,7 +167,7 @@ class AgentlessWriter extends BaseWriter {
|
|
|
165
167
|
tracerVersion,
|
|
166
168
|
languageVersion: process.version,
|
|
167
169
|
languageInterpreter: process.versions.bun ? 'JavaScriptCore' : 'v8',
|
|
168
|
-
})
|
|
170
|
+
}, { agent })
|
|
169
171
|
this.#exporterApiKey = apiKey
|
|
170
172
|
this.#exporterEnv = env
|
|
171
173
|
this.#exporterRuntimeId = runtimeID
|
|
@@ -12,7 +12,10 @@ const uuidSource =
|
|
|
12
12
|
const containerSource = '[0-9a-f]{64}'
|
|
13
13
|
const taskSource = String.raw`[0-9a-f]{32}-\d+`
|
|
14
14
|
const lineReg = /^(\d+):([^:]*):(.+)$/m
|
|
15
|
-
const entityReg = new RegExp(
|
|
15
|
+
const entityReg = new RegExp(
|
|
16
|
+
String.raw`(?:^|/)[^/\r\n\u2028\u2029]*?(${uuidSource}|${containerSource}|${taskSource})(?:\.scope)?$`,
|
|
17
|
+
'm'
|
|
18
|
+
)
|
|
16
19
|
|
|
17
20
|
let inode = 0
|
|
18
21
|
let cgroup = ''
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { isIPv6 } = require('node:net')
|
|
4
|
+
|
|
5
|
+
let defaultAgentKey
|
|
6
|
+
let getProxyForUrl
|
|
7
|
+
let proxyAgents
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Selects a proxy agent for an HTTPS target while preserving the direct agent's pool boundaries.
|
|
11
|
+
*
|
|
12
|
+
* @param {string|URL|object} url
|
|
13
|
+
* @param {import('node:http').Agent|false} [directAgent]
|
|
14
|
+
* @returns {import('node:http').Agent|false|undefined}
|
|
15
|
+
*/
|
|
16
|
+
function getHttpsProxyAgent (url, directAgent) {
|
|
17
|
+
getProxyForUrl ??= require('../../../../../vendor/dist/proxy-from-env').getProxyForUrl
|
|
18
|
+
|
|
19
|
+
const host = typeof url === 'string' ? undefined : url.host ?? url.hostname
|
|
20
|
+
const isUnbracketedIPv6 = typeof host === 'string' && host.includes(':') && isIPv6(host)
|
|
21
|
+
const target = typeof url === 'string'
|
|
22
|
+
? url
|
|
23
|
+
: { protocol: url.protocol, host: isUnbracketedIPv6 ? `[${host}]` : host, port: url.port }
|
|
24
|
+
const proxyUrl = getProxyForUrl(target)
|
|
25
|
+
if (!proxyUrl) return directAgent
|
|
26
|
+
|
|
27
|
+
directAgent ??= require('node:https').globalAgent
|
|
28
|
+
|
|
29
|
+
if (proxyAgents === undefined) {
|
|
30
|
+
defaultAgentKey = {}
|
|
31
|
+
proxyAgents = new WeakMap()
|
|
32
|
+
}
|
|
33
|
+
const cacheKey = directAgent || defaultAgentKey
|
|
34
|
+
let agents = proxyAgents.get(cacheKey)
|
|
35
|
+
if (agents === undefined) {
|
|
36
|
+
agents = new Map()
|
|
37
|
+
proxyAgents.set(cacheKey, agents)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let agent = agents.get(proxyUrl)
|
|
41
|
+
if (agent === undefined) {
|
|
42
|
+
const { HttpsProxyAgent } = require('../../../../../vendor/dist/https-proxy-agent')
|
|
43
|
+
const options = directAgent
|
|
44
|
+
? { keepAlive: directAgent.keepAlive, maxSockets: directAgent.maxSockets }
|
|
45
|
+
: undefined
|
|
46
|
+
agent = new HttpsProxyAgent(proxyUrl, options)
|
|
47
|
+
// AgentBase normally infers this from the node:https call stack. Interceptors such as MSW
|
|
48
|
+
// construct http.ClientRequest directly, so identify the target protocol explicitly.
|
|
49
|
+
agent.protocol = 'https:'
|
|
50
|
+
agents.set(proxyUrl, agent)
|
|
51
|
+
}
|
|
52
|
+
return agent
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { getHttpsProxyAgent }
|
|
@@ -13,6 +13,7 @@ const log = require('../../log')
|
|
|
13
13
|
const { canSendApiKey, parseUrl } = require('./url')
|
|
14
14
|
const docker = require('./docker')
|
|
15
15
|
const { httpAgent, httpsAgent } = require('./agents')
|
|
16
|
+
const { getHttpsProxyAgent } = require('./proxy')
|
|
16
17
|
const {
|
|
17
18
|
getMaxAttempts,
|
|
18
19
|
getRetryDelay,
|
|
@@ -94,11 +95,18 @@ function request (data, options, callback) {
|
|
|
94
95
|
|
|
95
96
|
docker.inject(options.headers)
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
let agent = options.agent ?? (isSecure ? httpsAgent : httpAgent)
|
|
99
|
+
if (hasApiKey && isSecure) {
|
|
100
|
+
try {
|
|
101
|
+
agent = getHttpsProxyAgent(options, agent)
|
|
102
|
+
} catch (error) {
|
|
103
|
+
callback(error)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
|
|
108
|
+
const connectionOptions = { ...options, agent }
|
|
109
|
+
|
|
102
110
|
/**
|
|
103
111
|
* @param {import('node:http').IncomingMessage} res
|
|
104
112
|
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
@@ -66,7 +66,7 @@ function initializeOpenTelemetryMetrics (config) {
|
|
|
66
66
|
metrics.setGlobalMeterProvider(meterProvider)
|
|
67
67
|
|
|
68
68
|
// Include the final metric collection and export in lifecycle retention.
|
|
69
|
-
registerTelemetryFlusher(done =>
|
|
69
|
+
registerTelemetryFlusher(done => reader.forceFlush(done))
|
|
70
70
|
|
|
71
71
|
registerResourceAttributeRefresh(exporter, () => buildGeneralResourceAttributes(config))
|
|
72
72
|
}
|