dd-trace 4.40.0 → 4.42.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 -0
- package/ext/exporters.d.ts +1 -1
- package/index.d.ts +54 -1
- package/init.js +40 -1
- package/initialize.mjs +8 -5
- package/package.json +24 -20
- package/packages/datadog-core/src/storage/index.js +1 -10
- package/packages/datadog-esbuild/index.js +5 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +76 -34
- package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
- package/packages/datadog-instrumentations/src/http/server.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/undici.js +18 -0
- package/packages/datadog-instrumentations/src/vitest.js +303 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
- package/packages/datadog-plugin-child_process/src/index.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-mocha/src/index.js +25 -4
- package/packages/datadog-plugin-openai/src/index.js +52 -30
- package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
- package/packages/datadog-plugin-undici/src/index.js +12 -0
- package/packages/datadog-plugin-vitest/src/index.js +156 -0
- package/packages/dd-trace/src/appsec/blocking.js +4 -0
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
- package/packages/dd-trace/src/appsec/index.js +45 -11
- package/packages/dd-trace/src/appsec/rasp.js +32 -5
- package/packages/dd-trace/src/appsec/recommended.json +208 -3
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
- package/packages/dd-trace/src/appsec/reporter.js +64 -20
- package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
- package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
- package/packages/dd-trace/src/appsec/standalone.js +130 -0
- package/packages/dd-trace/src/appsec/telemetry.js +33 -1
- package/packages/dd-trace/src/appsec/waf/index.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/config.js +110 -40
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/datastreams/processor.js +2 -1
- package/packages/dd-trace/src/exporters/agent/index.js +2 -2
- package/packages/dd-trace/src/format.js +22 -2
- package/packages/dd-trace/src/opentelemetry/span.js +33 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
- package/packages/dd-trace/src/opentracing/span.js +42 -1
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +5 -1
- package/packages/dd-trace/src/priority_sampler.js +2 -5
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/proxy.js +3 -1
- package/packages/dd-trace/src/rate_limiter.js +2 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_stats.js +4 -3
- package/packages/dd-trace/src/tagger.js +10 -1
- package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
- package/packages/dd-trace/src/tracer.js +2 -2
- package/packages/dd-trace/src/util.js +6 -1
- package/packages/datadog-core/src/storage/async_hooks.js +0 -49
|
@@ -31,7 +31,10 @@ function newStore () {
|
|
|
31
31
|
return {
|
|
32
32
|
[DD_TELEMETRY_REQUEST_METRICS]: {
|
|
33
33
|
duration: 0,
|
|
34
|
-
durationExt: 0
|
|
34
|
+
durationExt: 0,
|
|
35
|
+
raspDuration: 0,
|
|
36
|
+
raspDurationExt: 0,
|
|
37
|
+
raspEvalCount: 0
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -76,6 +79,28 @@ function getOrCreateMetricTags (store, versionsTags) {
|
|
|
76
79
|
return metricTags
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {
|
|
83
|
+
if (!req) return
|
|
84
|
+
|
|
85
|
+
const store = getStore(req)
|
|
86
|
+
|
|
87
|
+
// it does not depend on whether telemetry is enabled or not
|
|
88
|
+
addRaspRequestMetrics(store, metrics)
|
|
89
|
+
|
|
90
|
+
if (!enabled) return
|
|
91
|
+
|
|
92
|
+
const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion }
|
|
93
|
+
appsecMetrics.count('appsec.rasp.rule.eval', tags).inc(1)
|
|
94
|
+
|
|
95
|
+
if (metrics.wafTimeout) {
|
|
96
|
+
appsecMetrics.count('appsec.rasp.timeout', tags).inc(1)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (metrics.ruleTriggered) {
|
|
100
|
+
appsecMetrics.count('appsec.rasp.rule.match', tags).inc(1)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
function updateWafRequestsMetricTags (metrics, req) {
|
|
80
105
|
if (!req) return
|
|
81
106
|
|
|
@@ -141,6 +166,12 @@ function addRequestMetrics (store, { duration, durationExt }) {
|
|
|
141
166
|
store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
|
|
142
167
|
}
|
|
143
168
|
|
|
169
|
+
function addRaspRequestMetrics (store, { duration, durationExt }) {
|
|
170
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspDuration += duration || 0
|
|
171
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspDurationExt += durationExt || 0
|
|
172
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspEvalCount++
|
|
173
|
+
}
|
|
174
|
+
|
|
144
175
|
function getRequestMetrics (req) {
|
|
145
176
|
if (req) {
|
|
146
177
|
const store = getStore(req)
|
|
@@ -153,6 +184,7 @@ module.exports = {
|
|
|
153
184
|
disable,
|
|
154
185
|
|
|
155
186
|
updateWafRequestsMetricTags,
|
|
187
|
+
updateRaspRequestsMetricTags,
|
|
156
188
|
incrementWafInitMetric,
|
|
157
189
|
incrementWafUpdatesMetric,
|
|
158
190
|
incrementWafRequestsMetric,
|
|
@@ -46,7 +46,7 @@ function update (newRules) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function run (data, req) {
|
|
49
|
+
function run (data, req, raspRuleType) {
|
|
50
50
|
if (!req) {
|
|
51
51
|
const store = storage.getStore()
|
|
52
52
|
if (!store || !store.req) {
|
|
@@ -59,7 +59,7 @@ function run (data, req) {
|
|
|
59
59
|
|
|
60
60
|
const wafContext = waf.wafManager.getWAFContext(req)
|
|
61
61
|
|
|
62
|
-
return wafContext.run(data)
|
|
62
|
+
return wafContext.run(data, raspRuleType)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function disposeContext (req) {
|
|
@@ -19,7 +19,7 @@ class WAFContextWrapper {
|
|
|
19
19
|
this.addressesToSkip = new Set()
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
run ({ persistent, ephemeral }) {
|
|
22
|
+
run ({ persistent, ephemeral }, raspRuleType) {
|
|
23
23
|
const payload = {}
|
|
24
24
|
let payloadHasData = false
|
|
25
25
|
const inputs = {}
|
|
@@ -72,7 +72,7 @@ class WAFContextWrapper {
|
|
|
72
72
|
blockTriggered,
|
|
73
73
|
wafVersion: this.wafVersion,
|
|
74
74
|
wafTimeout: result.timeout
|
|
75
|
-
})
|
|
75
|
+
}, raspRuleType)
|
|
76
76
|
|
|
77
77
|
if (ruleTriggered) {
|
|
78
78
|
Reporter.reportAttack(JSON.stringify(result.events))
|
|
@@ -190,7 +190,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
190
190
|
requireGit,
|
|
191
191
|
isEarlyFlakeDetectionEnabled,
|
|
192
192
|
earlyFlakeDetectionNumRetries,
|
|
193
|
-
earlyFlakeDetectionFaultyThreshold
|
|
193
|
+
earlyFlakeDetectionFaultyThreshold,
|
|
194
|
+
isFlakyTestRetriesEnabled
|
|
194
195
|
} = remoteConfiguration
|
|
195
196
|
return {
|
|
196
197
|
isCodeCoverageEnabled,
|
|
@@ -199,7 +200,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
199
200
|
requireGit,
|
|
200
201
|
isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
|
|
201
202
|
earlyFlakeDetectionNumRetries,
|
|
202
|
-
earlyFlakeDetectionFaultyThreshold
|
|
203
|
+
earlyFlakeDetectionFaultyThreshold,
|
|
204
|
+
isFlakyTestRetriesEnabled
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
|
|
@@ -93,7 +93,8 @@ function getLibraryConfiguration ({
|
|
|
93
93
|
tests_skipping: isSuitesSkippingEnabled,
|
|
94
94
|
itr_enabled: isItrEnabled,
|
|
95
95
|
require_git: requireGit,
|
|
96
|
-
early_flake_detection: earlyFlakeDetectionConfig
|
|
96
|
+
early_flake_detection: earlyFlakeDetectionConfig,
|
|
97
|
+
flaky_test_retries_enabled: isFlakyTestRetriesEnabled
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
} = JSON.parse(res)
|
|
@@ -107,7 +108,8 @@ function getLibraryConfiguration ({
|
|
|
107
108
|
earlyFlakeDetectionNumRetries:
|
|
108
109
|
earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
|
|
109
110
|
earlyFlakeDetectionFaultyThreshold:
|
|
110
|
-
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD
|
|
111
|
+
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
|
|
112
|
+
isFlakyTestRetriesEnabled
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
|
|
@@ -26,50 +26,104 @@ const telemetryCounters = {
|
|
|
26
26
|
'otel.env.invalid': {}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function getCounter (event, ddVar, otelVar
|
|
29
|
+
function getCounter (event, ddVar, otelVar) {
|
|
30
30
|
const counters = telemetryCounters[event]
|
|
31
31
|
const tags = []
|
|
32
|
+
const ddVarPrefix = 'config.datadog:'
|
|
33
|
+
const otelVarPrefix = 'config.opentelemetry:'
|
|
34
|
+
if (ddVar) {
|
|
35
|
+
ddVar = ddVarPrefix + ddVar
|
|
36
|
+
tags.push(ddVar)
|
|
37
|
+
}
|
|
38
|
+
if (otelVar) {
|
|
39
|
+
otelVar = otelVarPrefix + otelVar
|
|
40
|
+
tags.push(otelVar)
|
|
41
|
+
}
|
|
32
42
|
|
|
33
|
-
if (
|
|
34
|
-
if (otelVar) tags.push(otelVar)
|
|
35
|
-
if (otelTracesSamplerArg) tags.push(otelTracesSamplerArg)
|
|
36
|
-
|
|
37
|
-
if (!(ddVar in counters)) counters[ddVar] = {}
|
|
43
|
+
if (!(otelVar in counters)) counters[otelVar] = {}
|
|
38
44
|
|
|
39
45
|
const counter = tracerMetrics.count(event, tags)
|
|
40
|
-
counters[
|
|
46
|
+
counters[otelVar][ddVar] = counter
|
|
41
47
|
return counter
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
const otelDdEnvMapping = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
OTEL_LOG_LEVEL: 'DD_TRACE_LOG_LEVEL',
|
|
52
|
+
OTEL_PROPAGATORS: 'DD_TRACE_PROPAGATION_STYLE',
|
|
53
|
+
OTEL_SERVICE_NAME: 'DD_SERVICE',
|
|
54
|
+
OTEL_TRACES_SAMPLER: 'DD_TRACE_SAMPLE_RATE',
|
|
55
|
+
OTEL_TRACES_SAMPLER_ARG: 'DD_TRACE_SAMPLE_RATE',
|
|
56
|
+
OTEL_TRACES_EXPORTER: 'DD_TRACE_ENABLED',
|
|
57
|
+
OTEL_METRICS_EXPORTER: 'DD_RUNTIME_METRICS_ENABLED',
|
|
58
|
+
OTEL_RESOURCE_ATTRIBUTES: 'DD_TAGS',
|
|
59
|
+
OTEL_SDK_DISABLED: 'DD_TRACE_OTEL_ENABLED',
|
|
60
|
+
OTEL_LOGS_EXPORTER: undefined
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
const
|
|
63
|
+
const VALID_PROPAGATION_STYLES = new Set(['datadog', 'tracecontext', 'b3', 'b3 single header', 'none'])
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error'])
|
|
66
|
+
|
|
67
|
+
function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) {
|
|
68
|
+
const OTEL_TRACES_SAMPLER_MAPPING = {
|
|
69
|
+
always_on: '1.0',
|
|
70
|
+
always_off: '0.0',
|
|
71
|
+
traceidratio: otelTracesSamplerArg,
|
|
72
|
+
parentbased_always_on: '1.0',
|
|
73
|
+
parentbased_always_off: '0.0',
|
|
74
|
+
parentbased_traceidratio: otelTracesSamplerArg
|
|
75
|
+
}
|
|
76
|
+
return OTEL_TRACES_SAMPLER_MAPPING[otelTracesSampler]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateOtelPropagators (propagators) {
|
|
80
|
+
if (!process.env.PROPAGATION_STYLE_EXTRACT &&
|
|
81
|
+
!process.env.PROPAGATION_STYLE_INJECT &&
|
|
82
|
+
!process.env.DD_TRACE_PROPAGATION_STYLE &&
|
|
83
|
+
process.env.OTEL_PROPAGATORS) {
|
|
84
|
+
for (const style in propagators) {
|
|
85
|
+
if (!VALID_PROPAGATION_STYLES.has(style)) {
|
|
86
|
+
log.warn('unexpected value for OTEL_PROPAGATORS environment variable')
|
|
87
|
+
getCounter('otel.env.invalid', 'DD_TRACE_PROPAGATION_STYLE', 'OTEL_PROPAGATORS').inc()
|
|
88
|
+
}
|
|
66
89
|
}
|
|
67
90
|
}
|
|
91
|
+
}
|
|
68
92
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
93
|
+
function validateEnvVarType (envVar) {
|
|
94
|
+
const value = process.env[envVar]
|
|
95
|
+
switch (envVar) {
|
|
96
|
+
case 'OTEL_LOG_LEVEL':
|
|
97
|
+
return VALID_LOG_LEVELS.has(value)
|
|
98
|
+
case 'OTEL_PROPAGATORS':
|
|
99
|
+
case 'OTEL_RESOURCE_ATTRIBUTES':
|
|
100
|
+
case 'OTEL_SERVICE_NAME':
|
|
101
|
+
return typeof value === 'string'
|
|
102
|
+
case 'OTEL_TRACES_SAMPLER':
|
|
103
|
+
return getFromOtelSamplerMap(value, process.env.OTEL_TRACES_SAMPLER_ARG) !== undefined
|
|
104
|
+
case 'OTEL_TRACES_SAMPLER_ARG':
|
|
105
|
+
return !isNaN(parseFloat(value))
|
|
106
|
+
case 'OTEL_SDK_DISABLED':
|
|
107
|
+
return value.toLowerCase() === 'true' || value.toLowerCase() === 'false'
|
|
108
|
+
case 'OTEL_TRACES_EXPORTER':
|
|
109
|
+
case 'OTEL_METRICS_EXPORTER':
|
|
110
|
+
case 'OTEL_LOGS_EXPORTER':
|
|
111
|
+
return value.toLowerCase() === 'none'
|
|
112
|
+
default:
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function checkIfBothOtelAndDdEnvVarSet () {
|
|
118
|
+
for (const [otelEnvVar, ddEnvVar] of Object.entries(otelDdEnvMapping)) {
|
|
119
|
+
if (ddEnvVar && process.env[ddEnvVar] && process.env[otelEnvVar]) {
|
|
120
|
+
log.warn(`both ${ddEnvVar} and ${otelEnvVar} environment variables are set`)
|
|
121
|
+
getCounter('otel.env.hiding', ddEnvVar, otelEnvVar).inc()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (process.env[otelEnvVar] && !validateEnvVarType(otelEnvVar)) {
|
|
125
|
+
log.warn(`unexpected value for ${otelEnvVar} environment variable`)
|
|
126
|
+
getCounter('otel.env.invalid', ddEnvVar, otelEnvVar).inc()
|
|
73
127
|
}
|
|
74
128
|
}
|
|
75
129
|
}
|
|
@@ -80,9 +134,9 @@ const fromEntries = Object.fromEntries || (entries =>
|
|
|
80
134
|
// eslint-disable-next-line max-len
|
|
81
135
|
const qsRegex = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\\s|%20)*(?:=|%3D)[^&]+|(?:"|%22)(?:\\s|%20)*(?::|%3A)(?:\\s|%20)*(?:"|%22)(?:%2[^2]|%[^2]|[^"%])+(?:"|%22))|bearer(?:\\s|%20)+[a-z0-9\\._\\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\\w=-]|%3D)+\\.ey[I-L](?:[\\w=-]|%3D)+(?:\\.(?:[\\w.+\\/=-]|%3D|%2F|%2B)+)?|[\\-]{5}BEGIN(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY[\\-]{5}[^\\-]+[\\-]{5}END(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY|ssh-rsa(?:\\s|%20)*(?:[a-z0-9\\/\\.+]|%2F|%5C|%2B){100,}'
|
|
82
136
|
// eslint-disable-next-line max-len
|
|
83
|
-
const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret
|
|
137
|
+
const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key)|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt'
|
|
84
138
|
// eslint-disable-next-line max-len
|
|
85
|
-
const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret
|
|
139
|
+
const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\\.net(?:[_-]|-)sessionid|sid|jwt)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}'
|
|
86
140
|
const runtimeId = uuid()
|
|
87
141
|
|
|
88
142
|
function maybeFile (filepath) {
|
|
@@ -235,6 +289,9 @@ class Config {
|
|
|
235
289
|
options.tracePropagationStyle,
|
|
236
290
|
defaultPropagationStyle
|
|
237
291
|
)
|
|
292
|
+
|
|
293
|
+
validateOtelPropagators(PROPAGATION_STYLE_INJECT)
|
|
294
|
+
|
|
238
295
|
const DD_TRACE_PROPAGATION_EXTRACT_FIRST = coalesce(
|
|
239
296
|
process.env.DD_TRACE_PROPAGATION_EXTRACT_FIRST,
|
|
240
297
|
false
|
|
@@ -440,6 +497,10 @@ class Config {
|
|
|
440
497
|
this._setValue(defaults, 'appsec.rateLimit', 100)
|
|
441
498
|
this._setValue(defaults, 'appsec.rules', undefined)
|
|
442
499
|
this._setValue(defaults, 'appsec.sca.enabled', null)
|
|
500
|
+
this._setValue(defaults, 'appsec.standalone.enabled', undefined)
|
|
501
|
+
this._setValue(defaults, 'appsec.stackTrace.enabled', true)
|
|
502
|
+
this._setValue(defaults, 'appsec.stackTrace.maxDepth', 32)
|
|
503
|
+
this._setValue(defaults, 'appsec.stackTrace.maxStackTraces', 2)
|
|
443
504
|
this._setValue(defaults, 'appsec.wafTimeout', 5e3) // µs
|
|
444
505
|
this._setValue(defaults, 'clientIpEnabled', false)
|
|
445
506
|
this._setValue(defaults, 'clientIpHeader', null)
|
|
@@ -524,10 +585,13 @@ class Config {
|
|
|
524
585
|
DD_APPSEC_ENABLED,
|
|
525
586
|
DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
|
|
526
587
|
DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
|
|
588
|
+
DD_APPSEC_MAX_STACK_TRACES,
|
|
589
|
+
DD_APPSEC_MAX_STACK_TRACE_DEPTH,
|
|
527
590
|
DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
|
|
528
591
|
DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
|
|
529
592
|
DD_APPSEC_RULES,
|
|
530
593
|
DD_APPSEC_SCA_ENABLED,
|
|
594
|
+
DD_APPSEC_STACK_TRACE_ENABLED,
|
|
531
595
|
DD_APPSEC_RASP_ENABLED,
|
|
532
596
|
DD_APPSEC_TRACE_RATE_LIMIT,
|
|
533
597
|
DD_APPSEC_WAF_TIMEOUT,
|
|
@@ -536,6 +600,7 @@ class Config {
|
|
|
536
600
|
DD_DOGSTATSD_HOSTNAME,
|
|
537
601
|
DD_DOGSTATSD_PORT,
|
|
538
602
|
DD_ENV,
|
|
603
|
+
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
|
|
539
604
|
DD_EXPERIMENTAL_PROFILING_ENABLED,
|
|
540
605
|
JEST_WORKER_ID,
|
|
541
606
|
DD_IAST_DEDUPLICATION_ENABLED,
|
|
@@ -627,6 +692,12 @@ class Config {
|
|
|
627
692
|
this._setString(env, 'appsec.rules', DD_APPSEC_RULES)
|
|
628
693
|
// DD_APPSEC_SCA_ENABLED is never used locally, but only sent to the backend
|
|
629
694
|
this._setBoolean(env, 'appsec.sca.enabled', DD_APPSEC_SCA_ENABLED)
|
|
695
|
+
this._setBoolean(env, 'appsec.standalone.enabled', DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED)
|
|
696
|
+
this._setBoolean(env, 'appsec.stackTrace.enabled', DD_APPSEC_STACK_TRACE_ENABLED)
|
|
697
|
+
this._setValue(env, 'appsec.stackTrace.maxDepth', maybeInt(DD_APPSEC_MAX_STACK_TRACE_DEPTH))
|
|
698
|
+
this._envUnprocessed['appsec.stackTrace.maxDepth'] = DD_APPSEC_MAX_STACK_TRACE_DEPTH
|
|
699
|
+
this._setValue(env, 'appsec.stackTrace.maxStackTraces', maybeInt(DD_APPSEC_MAX_STACK_TRACES))
|
|
700
|
+
this._envUnprocessed['appsec.stackTrace.maxStackTraces'] = DD_APPSEC_MAX_STACK_TRACES
|
|
630
701
|
this._setValue(env, 'appsec.wafTimeout', maybeInt(DD_APPSEC_WAF_TIMEOUT))
|
|
631
702
|
this._envUnprocessed['appsec.wafTimeout'] = DD_APPSEC_WAF_TIMEOUT
|
|
632
703
|
this._setBoolean(env, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED)
|
|
@@ -701,15 +772,8 @@ class Config {
|
|
|
701
772
|
: undefined
|
|
702
773
|
this._setBoolean(env, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED ||
|
|
703
774
|
otelSetRuntimeMetrics)
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
always_off: '0.0',
|
|
707
|
-
traceidratio: OTEL_TRACES_SAMPLER_ARG,
|
|
708
|
-
parentbased_always_on: '1.0',
|
|
709
|
-
parentbased_always_off: '0.0',
|
|
710
|
-
parentbased_traceidratio: OTEL_TRACES_SAMPLER_ARG
|
|
711
|
-
}
|
|
712
|
-
this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE || OTEL_TRACES_SAMPLER_MAPPING[OTEL_TRACES_SAMPLER])
|
|
775
|
+
this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE ||
|
|
776
|
+
getFromOtelSamplerMap(OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG))
|
|
713
777
|
this._setValue(env, 'sampler.rateLimit', DD_TRACE_RATE_LIMIT)
|
|
714
778
|
this._setSamplingRule(env, 'sampler.rules', safeJsonParse(DD_TRACE_SAMPLING_RULES))
|
|
715
779
|
this._envUnprocessed['sampler.rules'] = DD_TRACE_SAMPLING_RULES
|
|
@@ -767,6 +831,12 @@ class Config {
|
|
|
767
831
|
this._setValue(opts, 'appsec.rateLimit', maybeInt(options.appsec.rateLimit))
|
|
768
832
|
this._optsUnprocessed['appsec.rateLimit'] = options.appsec.rateLimit
|
|
769
833
|
this._setString(opts, 'appsec.rules', options.appsec.rules)
|
|
834
|
+
this._setBoolean(opts, 'appsec.standalone.enabled', options.experimental?.appsec?.standalone?.enabled)
|
|
835
|
+
this._setBoolean(opts, 'appsec.stackTrace.enabled', options.appsec.stackTrace?.enabled)
|
|
836
|
+
this._setValue(opts, 'appsec.stackTrace.maxDepth', maybeInt(options.appsec.stackTrace?.maxDepth))
|
|
837
|
+
this._optsUnprocessed['appsec.stackTrace.maxDepth'] = options.appsec.stackTrace?.maxDepth
|
|
838
|
+
this._setValue(opts, 'appsec.stackTrace.maxStackTraces', maybeInt(options.appsec.stackTrace?.maxStackTraces))
|
|
839
|
+
this._optsUnprocessed['appsec.stackTrace.maxStackTraces'] = options.appsec.stackTrace?.maxStackTraces
|
|
770
840
|
this._setValue(opts, 'appsec.wafTimeout', maybeInt(options.appsec.wafTimeout))
|
|
771
841
|
this._optsUnprocessed['appsec.wafTimeout'] = options.appsec.wafTimeout
|
|
772
842
|
this._setBoolean(opts, 'clientIpEnabled', options.clientIpEnabled)
|
|
@@ -32,5 +32,7 @@ module.exports = {
|
|
|
32
32
|
PEER_SERVICE_SOURCE_KEY: '_dd.peer.service.source',
|
|
33
33
|
PEER_SERVICE_REMAP_KEY: '_dd.peer.service.remapped_from',
|
|
34
34
|
SCI_REPOSITORY_URL: '_dd.git.repository_url',
|
|
35
|
-
SCI_COMMIT_SHA: '_dd.git.commit.sha'
|
|
35
|
+
SCI_COMMIT_SHA: '_dd.git.commit.sha',
|
|
36
|
+
APM_TRACING_ENABLED_KEY: '_dd.apm.enabled',
|
|
37
|
+
APPSEC_PROPAGATION_KEY: '_dd.p.appsec'
|
|
36
38
|
}
|
|
@@ -211,7 +211,8 @@ class DataStreamsProcessor {
|
|
|
211
211
|
Stats,
|
|
212
212
|
TracerVersion: pkg.version,
|
|
213
213
|
Version: this.version,
|
|
214
|
-
Lang: 'javascript'
|
|
214
|
+
Lang: 'javascript',
|
|
215
|
+
Tags: Object.entries(this.tags).map(([key, value]) => `${key}:${value}`)
|
|
215
216
|
}
|
|
216
217
|
this.writer.flush(payload)
|
|
217
218
|
}
|
|
@@ -7,7 +7,7 @@ const Writer = require('./writer')
|
|
|
7
7
|
class AgentExporter {
|
|
8
8
|
constructor (config, prioritySampler) {
|
|
9
9
|
this._config = config
|
|
10
|
-
const { url, hostname, port, lookup, protocolVersion, stats = {} } = config
|
|
10
|
+
const { url, hostname, port, lookup, protocolVersion, stats = {}, appsec } = config
|
|
11
11
|
this._url = url || new URL(format({
|
|
12
12
|
protocol: 'http:',
|
|
13
13
|
hostname: hostname || 'localhost',
|
|
@@ -15,7 +15,7 @@ class AgentExporter {
|
|
|
15
15
|
}))
|
|
16
16
|
|
|
17
17
|
const headers = {}
|
|
18
|
-
if (stats.enabled) {
|
|
18
|
+
if (stats.enabled || appsec?.standalone?.enabled) {
|
|
19
19
|
headers['Datadog-Client-Computed-Stats'] = 'yes'
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -34,6 +34,7 @@ function format (span) {
|
|
|
34
34
|
const formatted = formatSpan(span)
|
|
35
35
|
|
|
36
36
|
extractSpanLinks(formatted, span)
|
|
37
|
+
extractSpanEvents(formatted, span)
|
|
37
38
|
extractRootTags(formatted, span)
|
|
38
39
|
extractChunkTags(formatted, span)
|
|
39
40
|
extractTags(formatted, span)
|
|
@@ -52,6 +53,7 @@ function formatSpan (span) {
|
|
|
52
53
|
resource: String(spanContext._name),
|
|
53
54
|
error: 0,
|
|
54
55
|
meta: {},
|
|
56
|
+
meta_struct: span.meta_struct,
|
|
55
57
|
metrics: {},
|
|
56
58
|
start: Math.round(span._startTime * 1e6),
|
|
57
59
|
duration: Math.round(span._duration * 1e6),
|
|
@@ -88,6 +90,22 @@ function extractSpanLinks (trace, span) {
|
|
|
88
90
|
if (links.length > 0) { trace.meta['_dd.span_links'] = JSON.stringify(links) }
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
function extractSpanEvents (trace, span) {
|
|
94
|
+
const events = []
|
|
95
|
+
if (span._events) {
|
|
96
|
+
for (const event of span._events) {
|
|
97
|
+
const formattedEvent = {
|
|
98
|
+
name: event.name,
|
|
99
|
+
time_unix_nano: Math.round(event.startTime * 1e6),
|
|
100
|
+
attributes: event.attributes && Object.keys(event.attributes).length > 0 ? event.attributes : undefined
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
events.push(formattedEvent)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (events.length > 0) { trace.meta.events = JSON.stringify(events) }
|
|
107
|
+
}
|
|
108
|
+
|
|
91
109
|
function extractTags (trace, span) {
|
|
92
110
|
const context = span.context()
|
|
93
111
|
const origin = context._trace.origin
|
|
@@ -134,7 +152,10 @@ function extractTags (trace, span) {
|
|
|
134
152
|
case ERROR_STACK:
|
|
135
153
|
// HACK: remove when implemented in the backend
|
|
136
154
|
if (context._name !== 'fs.operation') {
|
|
137
|
-
trace.error
|
|
155
|
+
// HACK: to ensure otel.recordException does not influence trace.error
|
|
156
|
+
if (tags.setTraceError) {
|
|
157
|
+
trace.error = 1
|
|
158
|
+
}
|
|
138
159
|
} else {
|
|
139
160
|
break
|
|
140
161
|
}
|
|
@@ -142,7 +163,6 @@ function extractTags (trace, span) {
|
|
|
142
163
|
addTag(trace.meta, trace.metrics, tag, tags[tag])
|
|
143
164
|
}
|
|
144
165
|
}
|
|
145
|
-
|
|
146
166
|
setSingleSpanIngestionTags(trace, context._spanSampling)
|
|
147
167
|
|
|
148
168
|
addTag(trace.meta, trace.metrics, 'language', 'javascript')
|
|
@@ -20,6 +20,20 @@ function hrTimeToMilliseconds (time) {
|
|
|
20
20
|
return time[0] * 1e3 + time[1] / 1e6
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function isTimeInput (startTime) {
|
|
24
|
+
if (typeof startTime === 'number') {
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
if (startTime instanceof Date) {
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(startTime) && startTime.length === 2 &&
|
|
31
|
+
typeof startTime[0] === 'number' && typeof startTime[1] === 'number') {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
const spanKindNames = {
|
|
24
38
|
[api.SpanKind.INTERNAL]: kinds.INTERNAL,
|
|
25
39
|
[api.SpanKind.SERVER]: kinds.SERVER,
|
|
@@ -196,11 +210,6 @@ class Span {
|
|
|
196
210
|
return this
|
|
197
211
|
}
|
|
198
212
|
|
|
199
|
-
addEvent (name, attributesOrStartTime, startTime) {
|
|
200
|
-
api.diag.warn('Events not supported')
|
|
201
|
-
return this
|
|
202
|
-
}
|
|
203
|
-
|
|
204
213
|
addLink (context, attributes) {
|
|
205
214
|
// extract dd context
|
|
206
215
|
const ddSpanContext = context._ddContext
|
|
@@ -244,12 +253,29 @@ class Span {
|
|
|
244
253
|
return this.ended === false
|
|
245
254
|
}
|
|
246
255
|
|
|
247
|
-
|
|
256
|
+
addEvent (name, attributesOrStartTime, startTime) {
|
|
257
|
+
startTime = attributesOrStartTime && isTimeInput(attributesOrStartTime) ? attributesOrStartTime : startTime
|
|
258
|
+
const hrStartTime = timeInputToHrTime(startTime || (performance.now() + timeOrigin))
|
|
259
|
+
startTime = hrTimeToMilliseconds(hrStartTime)
|
|
260
|
+
|
|
261
|
+
this._ddSpan.addEvent(name, attributesOrStartTime, startTime)
|
|
262
|
+
return this
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
recordException (exception, timeInput) {
|
|
266
|
+
// HACK: identifier is added so that trace.error remains unchanged after a call to otel.recordException
|
|
248
267
|
this._ddSpan.addTags({
|
|
249
268
|
[ERROR_TYPE]: exception.name,
|
|
250
269
|
[ERROR_MESSAGE]: exception.message,
|
|
251
|
-
[ERROR_STACK]: exception.stack
|
|
270
|
+
[ERROR_STACK]: exception.stack,
|
|
271
|
+
doNotSetTraceError: true
|
|
252
272
|
})
|
|
273
|
+
const attributes = {}
|
|
274
|
+
if (exception.message) attributes['exception.message'] = exception.message
|
|
275
|
+
if (exception.type) attributes['exception.type'] = exception.type
|
|
276
|
+
if (exception.escaped) attributes['exception.escaped'] = exception.escaped
|
|
277
|
+
if (exception.stack) attributes['exception.stacktrace'] = exception.stack
|
|
278
|
+
this.addEvent(exception.name, attributes, timeInput)
|
|
253
279
|
}
|
|
254
280
|
|
|
255
281
|
get duration () {
|
|
@@ -6,9 +6,13 @@ const DatadogSpanContext = require('../span_context')
|
|
|
6
6
|
const log = require('../../log')
|
|
7
7
|
const TraceState = require('./tracestate')
|
|
8
8
|
const tags = require('../../../../../ext/tags')
|
|
9
|
+
const { channel } = require('dc-polyfill')
|
|
9
10
|
|
|
10
11
|
const { AUTO_KEEP, AUTO_REJECT, USER_KEEP } = require('../../../../../ext/priority')
|
|
11
12
|
|
|
13
|
+
const injectCh = channel('dd-trace:span:inject')
|
|
14
|
+
const extractCh = channel('dd-trace:span:extract')
|
|
15
|
+
|
|
12
16
|
const traceKey = 'x-datadog-trace-id'
|
|
13
17
|
const spanKey = 'x-datadog-parent-id'
|
|
14
18
|
const originKey = 'x-datadog-origin'
|
|
@@ -54,6 +58,10 @@ class TextMapPropagator {
|
|
|
54
58
|
this._injectB3SingleHeader(spanContext, carrier)
|
|
55
59
|
this._injectTraceparent(spanContext, carrier)
|
|
56
60
|
|
|
61
|
+
if (injectCh.hasSubscribers) {
|
|
62
|
+
injectCh.publish({ spanContext, carrier })
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
58
66
|
}
|
|
59
67
|
|
|
@@ -62,6 +70,10 @@ class TextMapPropagator {
|
|
|
62
70
|
|
|
63
71
|
if (!spanContext) return spanContext
|
|
64
72
|
|
|
73
|
+
if (extractCh.hasSubscribers) {
|
|
74
|
+
extractCh.publish({ spanContext, carrier })
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
log.debug(() => `Extract from carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
66
78
|
|
|
67
79
|
return spanContext
|
|
@@ -67,6 +67,8 @@ class DatadogSpan {
|
|
|
67
67
|
this._store = storage.getStore()
|
|
68
68
|
this._duration = undefined
|
|
69
69
|
|
|
70
|
+
this._events = []
|
|
71
|
+
|
|
70
72
|
// For internal use only. You probably want `context()._name`.
|
|
71
73
|
// This name property is not updated when the span name changes.
|
|
72
74
|
// This is necessary for span count metrics.
|
|
@@ -97,7 +99,10 @@ class DatadogSpan {
|
|
|
97
99
|
unfinishedRegistry.register(this, operationName, this)
|
|
98
100
|
}
|
|
99
101
|
spanleak.addSpan(this, operationName)
|
|
100
|
-
|
|
102
|
+
|
|
103
|
+
if (startCh.hasSubscribers) {
|
|
104
|
+
startCh.publish({ span: this, fields })
|
|
105
|
+
}
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
toString () {
|
|
@@ -163,6 +168,19 @@ class DatadogSpan {
|
|
|
163
168
|
})
|
|
164
169
|
}
|
|
165
170
|
|
|
171
|
+
addEvent (name, attributesOrStartTime, startTime) {
|
|
172
|
+
const event = { name }
|
|
173
|
+
if (attributesOrStartTime) {
|
|
174
|
+
if (typeof attributesOrStartTime === 'object') {
|
|
175
|
+
event.attributes = this._sanitizeEventAttributes(attributesOrStartTime)
|
|
176
|
+
} else {
|
|
177
|
+
startTime = attributesOrStartTime
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
event.startTime = startTime || this._getTime()
|
|
181
|
+
this._events.push(event)
|
|
182
|
+
}
|
|
183
|
+
|
|
166
184
|
finish (finishTime) {
|
|
167
185
|
if (this._duration !== undefined) {
|
|
168
186
|
return
|
|
@@ -221,7 +239,30 @@ class DatadogSpan {
|
|
|
221
239
|
const [key, value] = entry
|
|
222
240
|
addArrayOrScalarAttributes(key, value)
|
|
223
241
|
})
|
|
242
|
+
return sanitizedAttributes
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
_sanitizeEventAttributes (attributes = {}) {
|
|
246
|
+
const sanitizedAttributes = {}
|
|
224
247
|
|
|
248
|
+
for (const key in attributes) {
|
|
249
|
+
const value = attributes[key]
|
|
250
|
+
if (Array.isArray(value)) {
|
|
251
|
+
const newArray = []
|
|
252
|
+
for (const subkey in value) {
|
|
253
|
+
if (ALLOWED.includes(typeof value[subkey])) {
|
|
254
|
+
newArray.push(value[subkey])
|
|
255
|
+
} else {
|
|
256
|
+
log.warn('Dropping span event attribute. It is not of an allowed type')
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
sanitizedAttributes[key] = newArray
|
|
260
|
+
} else if (ALLOWED.includes(typeof value)) {
|
|
261
|
+
sanitizedAttributes[key] = value
|
|
262
|
+
} else {
|
|
263
|
+
log.warn('Dropping span event attribute. It is not of an allowed type')
|
|
264
|
+
}
|
|
265
|
+
}
|
|
225
266
|
return sanitizedAttributes
|
|
226
267
|
}
|
|
227
268
|
|
|
@@ -19,7 +19,7 @@ const REFERENCE_CHILD_OF = 'child_of'
|
|
|
19
19
|
const REFERENCE_FOLLOWS_FROM = 'follows_from'
|
|
20
20
|
|
|
21
21
|
class DatadogTracer {
|
|
22
|
-
constructor (config) {
|
|
22
|
+
constructor (config, prioritySampler) {
|
|
23
23
|
const Exporter = getExporter(config.experimental.exporter)
|
|
24
24
|
|
|
25
25
|
this._config = config
|
|
@@ -28,7 +28,7 @@ class DatadogTracer {
|
|
|
28
28
|
this._env = config.env
|
|
29
29
|
this._logInjection = config.logInjection
|
|
30
30
|
this._debug = config.debug
|
|
31
|
-
this._prioritySampler = new PrioritySampler(config.env, config.sampler)
|
|
31
|
+
this._prioritySampler = prioritySampler ?? new PrioritySampler(config.env, config.sampler)
|
|
32
32
|
this._exporter = new Exporter(config, this._prioritySampler)
|
|
33
33
|
this._processor = new SpanProcessor(this._exporter, this._prioritySampler, config)
|
|
34
34
|
this._url = this._exporter._url
|
|
@@ -91,6 +91,13 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
91
91
|
...testModuleSpanMetadata
|
|
92
92
|
}
|
|
93
93
|
})
|
|
94
|
+
// only for vitest
|
|
95
|
+
// These are added for the worker threads to use
|
|
96
|
+
if (this.constructor.id === 'vitest') {
|
|
97
|
+
process.env.DD_CIVISIBILITY_TEST_SESSION_ID = this.testSessionSpan.context().toTraceId()
|
|
98
|
+
process.env.DD_CIVISIBILITY_TEST_MODULE_ID = this.testModuleSpan.context().toSpanId()
|
|
99
|
+
}
|
|
100
|
+
|
|
94
101
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
|
|
95
102
|
})
|
|
96
103
|
|