dd-trace 3.55.0 → 3.56.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/index.d.ts +15 -0
- package/package.json +2 -1
- package/packages/datadog-instrumentations/src/fetch.js +6 -45
- package/packages/datadog-instrumentations/src/helpers/fetch.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +77 -10
- package/packages/datadog-instrumentations/src/mongoose.js +2 -1
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +6 -1
- package/packages/datadog-instrumentations/src/selenium.js +69 -0
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-cypress/src/support.js +19 -3
- package/packages/datadog-plugin-fetch/src/index.js +17 -11
- package/packages/datadog-plugin-jest/src/index.js +7 -2
- package/packages/datadog-plugin-mocha/src/index.js +4 -5
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-playwright/src/index.js +2 -2
- package/packages/datadog-plugin-selenium/src/index.js +71 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +70 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +14 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +12 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-rule-type.js +6 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +5 -50
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +742 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +539 -66
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/reporter.js +11 -10
- package/packages/dd-trace/src/appsec/telemetry.js +36 -7
- 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 -1
- package/packages/dd-trace/src/config.js +94 -9
- package/packages/dd-trace/src/dogstatsd.js +13 -11
- package/packages/dd-trace/src/index.js +5 -1
- package/packages/dd-trace/src/noop/dogstatsd.js +11 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +10 -4
- package/packages/dd-trace/src/opentracing/span.js +2 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +34 -3
- package/packages/dd-trace/src/profiling/config.js +8 -4
- package/packages/dd-trace/src/profiling/profiler.js +4 -0
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +33 -0
- package/packages/dd-trace/src/profiling/ssi-telemetry.js +167 -0
- package/packages/dd-trace/src/proxy.js +7 -1
- package/packages/dd-trace/src/tagger.js +13 -3
- package/packages/dd-trace/src/telemetry/index.js +5 -4
- package/packages/dd-trace/src/telemetry/metrics.js +2 -2
|
@@ -8,7 +8,8 @@ const {
|
|
|
8
8
|
incrementWafInitMetric,
|
|
9
9
|
updateWafRequestsMetricTags,
|
|
10
10
|
incrementWafUpdatesMetric,
|
|
11
|
-
incrementWafRequestsMetric
|
|
11
|
+
incrementWafRequestsMetric,
|
|
12
|
+
getRequestMetrics
|
|
12
13
|
} = require('./telemetry')
|
|
13
14
|
const zlib = require('zlib')
|
|
14
15
|
|
|
@@ -92,19 +93,10 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
function reportMetrics (metrics) {
|
|
95
|
-
// TODO: metrics should be incremental, there already is an RFC to report metrics
|
|
96
96
|
const store = storage.getStore()
|
|
97
97
|
const rootSpan = store?.req && web.root(store.req)
|
|
98
98
|
if (!rootSpan) return
|
|
99
99
|
|
|
100
|
-
if (metrics.duration) {
|
|
101
|
-
rootSpan.setTag('_dd.appsec.waf.duration', metrics.duration)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (metrics.durationExt) {
|
|
105
|
-
rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
100
|
if (metrics.rulesVersion) {
|
|
109
101
|
rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
|
|
110
102
|
}
|
|
@@ -179,6 +171,15 @@ function finishRequest (req, res) {
|
|
|
179
171
|
metricsQueue.clear()
|
|
180
172
|
}
|
|
181
173
|
|
|
174
|
+
const metrics = getRequestMetrics(req)
|
|
175
|
+
if (metrics?.duration) {
|
|
176
|
+
rootSpan.setTag('_dd.appsec.waf.duration', metrics.duration)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (metrics?.durationExt) {
|
|
180
|
+
rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
|
|
181
|
+
}
|
|
182
|
+
|
|
182
183
|
incrementWafRequestsMetric(req)
|
|
183
184
|
|
|
184
185
|
// collect some headers even when no attack is detected
|
|
@@ -5,6 +5,7 @@ const telemetryMetrics = require('../telemetry/metrics')
|
|
|
5
5
|
const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
6
6
|
|
|
7
7
|
const DD_TELEMETRY_WAF_RESULT_TAGS = Symbol('_dd.appsec.telemetry.waf.result.tags')
|
|
8
|
+
const DD_TELEMETRY_REQUEST_METRICS = Symbol('_dd.appsec.telemetry.request.metrics')
|
|
8
9
|
|
|
9
10
|
const tags = {
|
|
10
11
|
REQUEST_BLOCKED: 'request_blocked',
|
|
@@ -26,10 +27,19 @@ function disable () {
|
|
|
26
27
|
enabled = false
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
function newStore () {
|
|
31
|
+
return {
|
|
32
|
+
[DD_TELEMETRY_REQUEST_METRICS]: {
|
|
33
|
+
duration: 0,
|
|
34
|
+
durationExt: 0
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
function getStore (req) {
|
|
30
40
|
let store = metricsStoreMap.get(req)
|
|
31
41
|
if (!store) {
|
|
32
|
-
store =
|
|
42
|
+
store = newStore()
|
|
33
43
|
metricsStoreMap.set(req, store)
|
|
34
44
|
}
|
|
35
45
|
return store
|
|
@@ -51,9 +61,7 @@ function trackWafDurations (metrics, versionsTags) {
|
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
|
|
54
|
-
function getOrCreateMetricTags (
|
|
55
|
-
const store = getStore(req)
|
|
56
|
-
|
|
64
|
+
function getOrCreateMetricTags (store, versionsTags) {
|
|
57
65
|
let metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
|
|
58
66
|
if (!metricTags) {
|
|
59
67
|
metricTags = {
|
|
@@ -69,13 +77,20 @@ function getOrCreateMetricTags (req, versionsTags) {
|
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
function updateWafRequestsMetricTags (metrics, req) {
|
|
72
|
-
if (!req
|
|
80
|
+
if (!req) return
|
|
81
|
+
|
|
82
|
+
const store = getStore(req)
|
|
83
|
+
|
|
84
|
+
// it does not depend on whether telemetry is enabled or not
|
|
85
|
+
addRequestMetrics(store, metrics)
|
|
86
|
+
|
|
87
|
+
if (!enabled) return
|
|
73
88
|
|
|
74
89
|
const versionsTags = getVersionsTags(metrics.wafVersion, metrics.rulesVersion)
|
|
75
90
|
|
|
76
91
|
trackWafDurations(metrics, versionsTags)
|
|
77
92
|
|
|
78
|
-
const metricTags = getOrCreateMetricTags(
|
|
93
|
+
const metricTags = getOrCreateMetricTags(store, versionsTags)
|
|
79
94
|
|
|
80
95
|
const { blockTriggered, ruleTriggered, wafTimeout } = metrics
|
|
81
96
|
|
|
@@ -121,6 +136,18 @@ function incrementWafRequestsMetric (req) {
|
|
|
121
136
|
metricsStoreMap.delete(req)
|
|
122
137
|
}
|
|
123
138
|
|
|
139
|
+
function addRequestMetrics (store, { duration, durationExt }) {
|
|
140
|
+
store[DD_TELEMETRY_REQUEST_METRICS].duration += duration || 0
|
|
141
|
+
store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getRequestMetrics (req) {
|
|
145
|
+
if (req) {
|
|
146
|
+
const store = getStore(req)
|
|
147
|
+
return store?.[DD_TELEMETRY_REQUEST_METRICS]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
124
151
|
module.exports = {
|
|
125
152
|
enable,
|
|
126
153
|
disable,
|
|
@@ -128,5 +155,7 @@ module.exports = {
|
|
|
128
155
|
updateWafRequestsMetricTags,
|
|
129
156
|
incrementWafInitMetric,
|
|
130
157
|
incrementWafUpdatesMetric,
|
|
131
|
-
incrementWafRequestsMetric
|
|
158
|
+
incrementWafRequestsMetric,
|
|
159
|
+
|
|
160
|
+
getRequestMetrics
|
|
132
161
|
}
|
|
@@ -189,7 +189,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
189
189
|
isItrEnabled,
|
|
190
190
|
requireGit,
|
|
191
191
|
isEarlyFlakeDetectionEnabled,
|
|
192
|
-
earlyFlakeDetectionNumRetries
|
|
192
|
+
earlyFlakeDetectionNumRetries,
|
|
193
|
+
earlyFlakeDetectionFaultyThreshold
|
|
193
194
|
} = remoteConfiguration
|
|
194
195
|
return {
|
|
195
196
|
isCodeCoverageEnabled,
|
|
@@ -197,7 +198,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
197
198
|
isItrEnabled,
|
|
198
199
|
requireGit,
|
|
199
200
|
isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
|
|
200
|
-
earlyFlakeDetectionNumRetries
|
|
201
|
+
earlyFlakeDetectionNumRetries,
|
|
202
|
+
earlyFlakeDetectionFaultyThreshold
|
|
201
203
|
}
|
|
202
204
|
}
|
|
203
205
|
|
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
} = require('../telemetry')
|
|
13
13
|
|
|
14
14
|
const DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES = 2
|
|
15
|
+
const DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD = 30
|
|
15
16
|
|
|
16
17
|
function getLibraryConfiguration ({
|
|
17
18
|
url,
|
|
@@ -104,7 +105,9 @@ function getLibraryConfiguration ({
|
|
|
104
105
|
requireGit,
|
|
105
106
|
isEarlyFlakeDetectionEnabled: earlyFlakeDetectionConfig?.enabled ?? false,
|
|
106
107
|
earlyFlakeDetectionNumRetries:
|
|
107
|
-
earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES
|
|
108
|
+
earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
|
|
109
|
+
earlyFlakeDetectionFaultyThreshold:
|
|
110
|
+
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
|
|
@@ -15,9 +15,65 @@ const { isTrue, isFalse } = require('./util')
|
|
|
15
15
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
|
|
16
16
|
const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } = require('./git_properties')
|
|
17
17
|
const { updateConfig } = require('./telemetry')
|
|
18
|
+
const telemetryMetrics = require('./telemetry/metrics')
|
|
18
19
|
const { getIsGCPFunction, getIsAzureFunctionConsumptionPlan } = require('./serverless')
|
|
19
20
|
const { ORIGIN_KEY } = require('./constants')
|
|
20
21
|
|
|
22
|
+
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
23
|
+
|
|
24
|
+
const telemetryCounters = {
|
|
25
|
+
'otel.env.hiding': {},
|
|
26
|
+
'otel.env.invalid': {}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getCounter (event, ddVar, otelVar, otelTracesSamplerArg) {
|
|
30
|
+
const counters = telemetryCounters[event]
|
|
31
|
+
const tags = []
|
|
32
|
+
|
|
33
|
+
if (ddVar) tags.push(ddVar)
|
|
34
|
+
if (otelVar) tags.push(otelVar)
|
|
35
|
+
if (otelTracesSamplerArg) tags.push(otelTracesSamplerArg)
|
|
36
|
+
|
|
37
|
+
if (!(ddVar in counters)) counters[ddVar] = {}
|
|
38
|
+
|
|
39
|
+
const counter = tracerMetrics.count(event, tags)
|
|
40
|
+
counters[ddVar][otelVar] = counter
|
|
41
|
+
return counter
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const otelDdEnvMapping = {
|
|
45
|
+
DD_TRACE_LOG_LEVEL: 'OTEL_LOG_LEVEL',
|
|
46
|
+
DD_TRACE_PROPAGATION_STYLE: 'OTEL_PROPAGATORS',
|
|
47
|
+
DD_SERVICE: 'OTEL_SERVICE_NAME',
|
|
48
|
+
DD_TRACE_SAMPLE_RATE: 'OTEL_TRACES_SAMPLER',
|
|
49
|
+
DD_TRACE_ENABLED: 'OTEL_TRACES_EXPORTER',
|
|
50
|
+
DD_RUNTIME_METRICS_ENABLED: 'OTEL_METRICS_EXPORTER',
|
|
51
|
+
DD_TAGS: 'OTEL_RESOURCE_ATTRIBUTES',
|
|
52
|
+
DD_TRACE_OTEL_ENABLED: 'OTEL_SDK_DISABLED'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const otelInvalidEnv = ['OTEL_LOGS_EXPORTER']
|
|
56
|
+
|
|
57
|
+
function checkIfBothOtelAndDdEnvVarSet () {
|
|
58
|
+
for (const [ddVar, otelVar] of Object.entries(otelDdEnvMapping)) {
|
|
59
|
+
if (process.env[ddVar] && process.env[otelVar]) {
|
|
60
|
+
log.warn(`both ${ddVar} and ${otelVar} environment variables are set`)
|
|
61
|
+
getCounter('otel.env.hiding', ddVar, otelVar,
|
|
62
|
+
otelVar === 'OTEL_TRACES_SAMPLER' &&
|
|
63
|
+
process.env.OTEL_TRACES_SAMPLER_ARG
|
|
64
|
+
? 'OTEL_TRACES_SAMPLER_ARG'
|
|
65
|
+
: undefined).inc()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const otelVar of otelInvalidEnv) {
|
|
70
|
+
if (process.env[otelVar]) {
|
|
71
|
+
log.warn(`${otelVar} is not supported by the Datadog SDK`)
|
|
72
|
+
getCounter('otel.env.invalid', otelVar).inc()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
21
77
|
const fromEntries = Object.fromEntries || (entries =>
|
|
22
78
|
entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
|
|
23
79
|
|
|
@@ -89,7 +145,8 @@ function propagationStyle (key, option, defaultValue) {
|
|
|
89
145
|
|
|
90
146
|
// Otherwise, fallback to env var parsing
|
|
91
147
|
const envKey = `DD_TRACE_PROPAGATION_STYLE_${key.toUpperCase()}`
|
|
92
|
-
|
|
148
|
+
|
|
149
|
+
const envVar = coalesce(process.env[envKey], process.env.DD_TRACE_PROPAGATION_STYLE, process.env.OTEL_PROPAGATORS)
|
|
93
150
|
if (typeof envVar !== 'undefined') {
|
|
94
151
|
return envVar.split(',')
|
|
95
152
|
.filter(v => v !== '')
|
|
@@ -108,15 +165,19 @@ class Config {
|
|
|
108
165
|
iastOptions: options.experimental?.iast
|
|
109
166
|
}
|
|
110
167
|
|
|
168
|
+
checkIfBothOtelAndDdEnvVarSet()
|
|
169
|
+
|
|
111
170
|
// Configure the logger first so it can be used to warn about other configs
|
|
112
171
|
this.debug = isTrue(coalesce(
|
|
113
172
|
process.env.DD_TRACE_DEBUG,
|
|
114
173
|
false
|
|
115
174
|
))
|
|
116
175
|
this.logger = options.logger
|
|
176
|
+
|
|
117
177
|
this.logLevel = coalesce(
|
|
118
178
|
options.logLevel,
|
|
119
179
|
process.env.DD_TRACE_LOG_LEVEL,
|
|
180
|
+
process.env.OTEL_LOG_LEVEL,
|
|
120
181
|
'debug'
|
|
121
182
|
)
|
|
122
183
|
|
|
@@ -163,12 +224,12 @@ class Config {
|
|
|
163
224
|
'environment variables'
|
|
164
225
|
)
|
|
165
226
|
}
|
|
166
|
-
const
|
|
227
|
+
const PROPAGATION_STYLE_INJECT = propagationStyle(
|
|
167
228
|
'inject',
|
|
168
229
|
options.tracePropagationStyle,
|
|
169
230
|
defaultPropagationStyle
|
|
170
231
|
)
|
|
171
|
-
const
|
|
232
|
+
const PROPAGATION_STYLE_EXTRACT = propagationStyle(
|
|
172
233
|
'extract',
|
|
173
234
|
options.tracePropagationStyle,
|
|
174
235
|
defaultPropagationStyle
|
|
@@ -253,8 +314,13 @@ class Config {
|
|
|
253
314
|
this.apiKey = DD_API_KEY
|
|
254
315
|
this.serviceMapping = DD_SERVICE_MAPPING
|
|
255
316
|
this.tracePropagationStyle = {
|
|
256
|
-
inject:
|
|
257
|
-
extract:
|
|
317
|
+
inject: PROPAGATION_STYLE_INJECT,
|
|
318
|
+
extract: PROPAGATION_STYLE_EXTRACT,
|
|
319
|
+
otelPropagators: process.env.DD_TRACE_PROPAGATION_STYLE ||
|
|
320
|
+
process.env.DD_TRACE_PROPAGATION_STYLE_INJECT ||
|
|
321
|
+
process.env.DD_TRACE_PROPAGATION_STYLE_EXTRACT
|
|
322
|
+
? false
|
|
323
|
+
: !!process.env.OTEL_PROPAGATORS
|
|
258
324
|
}
|
|
259
325
|
this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST)
|
|
260
326
|
this.sampler = sampler
|
|
@@ -526,12 +592,18 @@ class Config {
|
|
|
526
592
|
DD_TRACE_TELEMETRY_ENABLED,
|
|
527
593
|
DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
|
|
528
594
|
DD_TRACING_ENABLED,
|
|
529
|
-
DD_VERSION
|
|
595
|
+
DD_VERSION,
|
|
596
|
+
OTEL_SERVICE_NAME,
|
|
597
|
+
OTEL_RESOURCE_ATTRIBUTES,
|
|
598
|
+
OTEL_TRACES_SAMPLER,
|
|
599
|
+
OTEL_TRACES_SAMPLER_ARG,
|
|
600
|
+
OTEL_METRICS_EXPORTER
|
|
530
601
|
} = process.env
|
|
531
602
|
|
|
532
603
|
const tags = {}
|
|
533
604
|
const env = this._env = {}
|
|
534
605
|
|
|
606
|
+
tagger.add(tags, OTEL_RESOURCE_ATTRIBUTES, true)
|
|
535
607
|
tagger.add(tags, DD_TAGS)
|
|
536
608
|
tagger.add(tags, DD_TRACE_TAGS)
|
|
537
609
|
tagger.add(tags, DD_TRACE_GLOBAL_TAGS)
|
|
@@ -592,11 +664,24 @@ class Config {
|
|
|
592
664
|
))
|
|
593
665
|
this._setValue(env, 'remoteConfig.pollInterval', maybeFloat(DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS))
|
|
594
666
|
this._setBoolean(env, 'reportHostname', DD_TRACE_REPORT_HOSTNAME)
|
|
595
|
-
|
|
596
|
-
|
|
667
|
+
// only used to explicitly set runtimeMetrics to false
|
|
668
|
+
const otelSetRuntimeMetrics = String(OTEL_METRICS_EXPORTER).toLowerCase() === 'none'
|
|
669
|
+
? false
|
|
670
|
+
: undefined
|
|
671
|
+
this._setBoolean(env, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED ||
|
|
672
|
+
otelSetRuntimeMetrics)
|
|
673
|
+
const OTEL_TRACES_SAMPLER_MAPPING = {
|
|
674
|
+
always_on: '1.0',
|
|
675
|
+
always_off: '0.0',
|
|
676
|
+
traceidratio: OTEL_TRACES_SAMPLER_ARG,
|
|
677
|
+
parentbased_always_on: '1.0',
|
|
678
|
+
parentbased_always_off: '0.0',
|
|
679
|
+
parentbased_traceidratio: OTEL_TRACES_SAMPLER_ARG
|
|
680
|
+
}
|
|
681
|
+
this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE || OTEL_TRACES_SAMPLER_MAPPING[OTEL_TRACES_SAMPLER])
|
|
597
682
|
this._setValue(env, 'sampler.rateLimit', DD_TRACE_RATE_LIMIT)
|
|
598
683
|
this._setString(env, 'scope', DD_TRACE_SCOPE)
|
|
599
|
-
this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service)
|
|
684
|
+
this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service || OTEL_SERVICE_NAME)
|
|
600
685
|
this._setString(env, 'site', DD_SITE)
|
|
601
686
|
if (DD_TRACE_SPAN_ATTRIBUTE_SCHEMA) {
|
|
602
687
|
this._setString(env, 'spanAttributeSchema', validateNamingVersion(DD_TRACE_SPAN_ATTRIBUTE_SCHEMA))
|
|
@@ -12,6 +12,7 @@ const MAX_BUFFER_SIZE = 1024 // limit from the agent
|
|
|
12
12
|
const TYPE_COUNTER = 'c'
|
|
13
13
|
const TYPE_GAUGE = 'g'
|
|
14
14
|
const TYPE_DISTRIBUTION = 'd'
|
|
15
|
+
const TYPE_HISTOGRAM = 'h'
|
|
15
16
|
|
|
16
17
|
class DogStatsDClient {
|
|
17
18
|
constructor (options = {}) {
|
|
@@ -46,6 +47,10 @@ class DogStatsDClient {
|
|
|
46
47
|
this._add(stat, value, TYPE_DISTRIBUTION, tags)
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
histogram (stat, value, tags) {
|
|
51
|
+
this._add(stat, value, TYPE_HISTOGRAM, tags)
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
flush () {
|
|
50
55
|
const queue = this._enqueue()
|
|
51
56
|
|
|
@@ -180,16 +185,6 @@ class DogStatsDClient {
|
|
|
180
185
|
}
|
|
181
186
|
}
|
|
182
187
|
|
|
183
|
-
class NoopDogStatsDClient {
|
|
184
|
-
gauge () { }
|
|
185
|
-
|
|
186
|
-
increment () { }
|
|
187
|
-
|
|
188
|
-
distribution () { }
|
|
189
|
-
|
|
190
|
-
flush () { }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
188
|
// This is a simplified user-facing proxy to the underlying DogStatsDClient instance
|
|
194
189
|
class CustomMetrics {
|
|
195
190
|
constructor (config) {
|
|
@@ -229,6 +224,14 @@ class CustomMetrics {
|
|
|
229
224
|
)
|
|
230
225
|
}
|
|
231
226
|
|
|
227
|
+
histogram (stat, value, tags) {
|
|
228
|
+
return this.dogstatsd.histogram(
|
|
229
|
+
stat,
|
|
230
|
+
value,
|
|
231
|
+
CustomMetrics.tagTranslator(tags)
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
232
235
|
flush () {
|
|
233
236
|
return this.dogstatsd.flush()
|
|
234
237
|
}
|
|
@@ -252,6 +255,5 @@ class CustomMetrics {
|
|
|
252
255
|
|
|
253
256
|
module.exports = {
|
|
254
257
|
DogStatsDClient,
|
|
255
|
-
NoopDogStatsDClient,
|
|
256
258
|
CustomMetrics
|
|
257
259
|
}
|
|
@@ -5,6 +5,10 @@ const { isFalse } = require('./util')
|
|
|
5
5
|
// Global `jest` is only present in Jest workers.
|
|
6
6
|
const inJestWorker = typeof jest !== 'undefined'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const ddTraceDisabled = process.env.DD_TRACE_ENABLED
|
|
9
|
+
? isFalse(process.env.DD_TRACE_ENABLED)
|
|
10
|
+
: String(process.env.OTEL_TRACES_EXPORTER).toLowerCase() === 'none'
|
|
11
|
+
|
|
12
|
+
module.exports = ddTraceDisabled || inJestWorker
|
|
9
13
|
? require('./noop/proxy')
|
|
10
14
|
: require('./proxy')
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
const NoopTracer = require('./tracer')
|
|
4
4
|
const NoopAppsecSdk = require('../appsec/sdk/noop')
|
|
5
|
+
const NoopDogStatsDClient = require('./dogstatsd')
|
|
5
6
|
|
|
6
7
|
const noop = new NoopTracer()
|
|
7
8
|
const noopAppsec = new NoopAppsecSdk()
|
|
9
|
+
const noopDogStatsDClient = new NoopDogStatsDClient()
|
|
8
10
|
|
|
9
11
|
class Tracer {
|
|
10
12
|
constructor () {
|
|
11
13
|
this._tracer = noop
|
|
12
14
|
this.appsec = noopAppsec
|
|
15
|
+
this.dogstatsd = noopDogStatsDClient
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
init () {
|
|
@@ -224,13 +224,19 @@ class TextMapPropagator {
|
|
|
224
224
|
case 'tracecontext':
|
|
225
225
|
spanContext = this._extractTraceparentContext(carrier)
|
|
226
226
|
break
|
|
227
|
-
case 'b3'
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
case 'b3' && this
|
|
228
|
+
._config
|
|
229
|
+
.tracePropagationStyle
|
|
230
|
+
.otelPropagators: // TODO: should match "b3 single header" in next major
|
|
231
231
|
case 'b3 single header': // TODO: delete in major after singular "b3"
|
|
232
232
|
spanContext = this._extractB3SingleContext(carrier)
|
|
233
233
|
break
|
|
234
|
+
case 'b3':
|
|
235
|
+
case 'b3multi':
|
|
236
|
+
spanContext = this._extractB3MultiContext(carrier)
|
|
237
|
+
break
|
|
238
|
+
default:
|
|
239
|
+
log.warn(`Unknown propagation style: ${extractor}`)
|
|
234
240
|
}
|
|
235
241
|
|
|
236
242
|
if (spanContext !== null) {
|
|
@@ -33,6 +33,7 @@ const integrationCounters = {
|
|
|
33
33
|
spans_finished: {}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const startCh = channel('dd-trace:span:start')
|
|
36
37
|
const finishCh = channel('dd-trace:span:finish')
|
|
37
38
|
|
|
38
39
|
function getIntegrationCounter (event, integration) {
|
|
@@ -96,6 +97,7 @@ class DatadogSpan {
|
|
|
96
97
|
unfinishedRegistry.register(this, operationName, this)
|
|
97
98
|
}
|
|
98
99
|
spanleak.addSpan(this, operationName)
|
|
100
|
+
startCh.publish(this)
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
toString () {
|
|
@@ -45,6 +45,7 @@ module.exports = {
|
|
|
45
45
|
get 'jest-environment-node' () { return require('../../../datadog-plugin-jest/src') },
|
|
46
46
|
get 'jest-environment-jsdom' () { return require('../../../datadog-plugin-jest/src') },
|
|
47
47
|
get 'jest-jasmine2' () { return require('../../../datadog-plugin-jest/src') },
|
|
48
|
+
get 'jest-runtime' () { return require('../../../datadog-plugin-jest/src') },
|
|
48
49
|
get 'jest-worker' () { return require('../../../datadog-plugin-jest/src') },
|
|
49
50
|
get koa () { return require('../../../datadog-plugin-koa/src') },
|
|
50
51
|
get 'koa-router' () { return require('../../../datadog-plugin-koa/src') },
|
|
@@ -77,6 +78,7 @@ module.exports = {
|
|
|
77
78
|
get restify () { return require('../../../datadog-plugin-restify/src') },
|
|
78
79
|
get rhea () { return require('../../../datadog-plugin-rhea/src') },
|
|
79
80
|
get router () { return require('../../../datadog-plugin-router/src') },
|
|
81
|
+
get 'selenium-webdriver' () { return require('../../../datadog-plugin-selenium/src') },
|
|
80
82
|
get sharedb () { return require('../../../datadog-plugin-sharedb/src') },
|
|
81
83
|
get tedious () { return require('../../../datadog-plugin-tedious/src') },
|
|
82
84
|
get winston () { return require('../../../datadog-plugin-winston/src') }
|
|
@@ -53,7 +53,8 @@ const TEST_CONFIGURATION_BROWSER_NAME = 'test.configuration.browser_name'
|
|
|
53
53
|
// Early flake detection
|
|
54
54
|
const TEST_IS_NEW = 'test.is_new'
|
|
55
55
|
const TEST_IS_RETRY = 'test.is_retry'
|
|
56
|
-
const
|
|
56
|
+
const TEST_EARLY_FLAKE_ENABLED = 'test.early_flake.enabled'
|
|
57
|
+
const TEST_EARLY_FLAKE_ABORT_REASON = 'test.early_flake.abort_reason'
|
|
57
58
|
|
|
58
59
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
59
60
|
|
|
@@ -71,6 +72,12 @@ const ITR_CORRELATION_ID = 'itr_correlation_id'
|
|
|
71
72
|
|
|
72
73
|
const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
|
|
73
74
|
|
|
75
|
+
// selenium tags
|
|
76
|
+
const TEST_BROWSER_DRIVER = 'test.browser.driver'
|
|
77
|
+
const TEST_BROWSER_DRIVER_VERSION = 'test.browser.driver_version'
|
|
78
|
+
const TEST_BROWSER_NAME = 'test.browser.name'
|
|
79
|
+
const TEST_BROWSER_VERSION = 'test.browser.version'
|
|
80
|
+
|
|
74
81
|
// jest worker variables
|
|
75
82
|
const JEST_WORKER_TRACE_PAYLOAD_CODE = 60
|
|
76
83
|
const JEST_WORKER_COVERAGE_PAYLOAD_CODE = 61
|
|
@@ -102,7 +109,8 @@ module.exports = {
|
|
|
102
109
|
TEST_CONFIGURATION_BROWSER_NAME,
|
|
103
110
|
TEST_IS_NEW,
|
|
104
111
|
TEST_IS_RETRY,
|
|
105
|
-
|
|
112
|
+
TEST_EARLY_FLAKE_ENABLED,
|
|
113
|
+
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
106
114
|
getTestEnvironmentMetadata,
|
|
107
115
|
getTestParametersString,
|
|
108
116
|
finishAllTraceSpans,
|
|
@@ -141,7 +149,12 @@ module.exports = {
|
|
|
141
149
|
EFD_STRING,
|
|
142
150
|
EFD_TEST_NAME_REGEX,
|
|
143
151
|
removeEfdStringFromTestName,
|
|
144
|
-
addEfdStringToTestName
|
|
152
|
+
addEfdStringToTestName,
|
|
153
|
+
getIsFaultyEarlyFlakeDetection,
|
|
154
|
+
TEST_BROWSER_DRIVER,
|
|
155
|
+
TEST_BROWSER_DRIVER_VERSION,
|
|
156
|
+
TEST_BROWSER_NAME,
|
|
157
|
+
TEST_BROWSER_VERSION
|
|
145
158
|
}
|
|
146
159
|
|
|
147
160
|
// Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
|
|
@@ -571,3 +584,21 @@ function addEfdStringToTestName (testName, numAttempt) {
|
|
|
571
584
|
function removeEfdStringFromTestName (testName) {
|
|
572
585
|
return testName.replace(EFD_TEST_NAME_REGEX, '')
|
|
573
586
|
}
|
|
587
|
+
|
|
588
|
+
function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faultyThresholdPercentage) {
|
|
589
|
+
let newSuites = 0
|
|
590
|
+
for (const suite of projectSuites) {
|
|
591
|
+
if (!testsBySuiteName[suite]) {
|
|
592
|
+
newSuites++
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const newSuitesPercentage = (newSuites / projectSuites.length) * 100
|
|
596
|
+
|
|
597
|
+
// The faulty threshold represents a percentage, but we also want to consider
|
|
598
|
+
// smaller projects, where big variations in the % are more likely.
|
|
599
|
+
// This is why we also check the absolute number of new suites.
|
|
600
|
+
return (
|
|
601
|
+
newSuites > faultyThresholdPercentage &&
|
|
602
|
+
newSuitesPercentage > faultyThresholdPercentage
|
|
603
|
+
)
|
|
604
|
+
}
|
|
@@ -21,6 +21,7 @@ class Config {
|
|
|
21
21
|
DD_AGENT_HOST,
|
|
22
22
|
DD_ENV,
|
|
23
23
|
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
24
|
+
DD_PROFILING_CPU_ENABLED,
|
|
24
25
|
DD_PROFILING_DEBUG_SOURCE_MAPS,
|
|
25
26
|
DD_PROFILING_ENABLED,
|
|
26
27
|
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
@@ -165,7 +166,7 @@ class Config {
|
|
|
165
166
|
|
|
166
167
|
this.timelineEnabled = isTrue(coalesce(options.timelineEnabled,
|
|
167
168
|
DD_PROFILING_TIMELINE_ENABLED,
|
|
168
|
-
DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED,
|
|
169
|
+
DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED, samplingContextsAvailable))
|
|
169
170
|
logExperimentalVarDeprecation('TIMELINE_ENABLED')
|
|
170
171
|
checkOptionWithSamplingContextAllowed(this.timelineEnabled, 'Timeline view')
|
|
171
172
|
|
|
@@ -176,7 +177,9 @@ class Config {
|
|
|
176
177
|
checkOptionWithSamplingContextAllowed(this.codeHotspotsEnabled, 'Code hotspots')
|
|
177
178
|
|
|
178
179
|
this.cpuProfilingEnabled = isTrue(coalesce(options.cpuProfilingEnabled,
|
|
179
|
-
|
|
180
|
+
DD_PROFILING_CPU_ENABLED,
|
|
181
|
+
DD_PROFILING_EXPERIMENTAL_CPU_ENABLED, samplingContextsAvailable))
|
|
182
|
+
logExperimentalVarDeprecation('CPU_ENABLED')
|
|
180
183
|
checkOptionWithSamplingContextAllowed(this.cpuProfilingEnabled, 'CPU profiling')
|
|
181
184
|
|
|
182
185
|
this.profilers = ensureProfilers(profilers, this)
|
|
@@ -288,8 +291,9 @@ function ensureProfilers (profilers, options) {
|
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
293
|
|
|
291
|
-
// Events profiler is a profiler
|
|
292
|
-
if
|
|
294
|
+
// Events profiler is a profiler that produces timeline events. It is only
|
|
295
|
+
// added if timeline is enabled and there's a wall profiler.
|
|
296
|
+
if (options.timelineEnabled && profilers.some(p => p instanceof WallProfiler)) {
|
|
293
297
|
profilers.push(new EventsProfiler(options))
|
|
294
298
|
}
|
|
295
299
|
|
|
@@ -4,6 +4,9 @@ const { EventEmitter } = require('events')
|
|
|
4
4
|
const { Config } = require('./config')
|
|
5
5
|
const { snapshotKinds } = require('./constants')
|
|
6
6
|
const { threadNamePrefix } = require('./profilers/shared')
|
|
7
|
+
const dc = require('dc-polyfill')
|
|
8
|
+
|
|
9
|
+
const profileSubmittedChannel = dc.channel('datadog:profiling:profile-submitted')
|
|
7
10
|
|
|
8
11
|
function maybeSourceMap (sourceMap, SourceMapper, debug) {
|
|
9
12
|
if (!sourceMap) return
|
|
@@ -161,6 +164,7 @@ class Profiler extends EventEmitter {
|
|
|
161
164
|
this._capture(this._timeoutInterval, endDate)
|
|
162
165
|
}
|
|
163
166
|
await this._submit(encodedProfiles, startDate, endDate, snapshotKind)
|
|
167
|
+
profileSubmittedChannel.publish()
|
|
164
168
|
this._logger.debug('Submitted profiles')
|
|
165
169
|
} catch (err) {
|
|
166
170
|
this._logger.error(err)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const dc = require('dc-polyfill')
|
|
4
|
+
const coalesce = require('koalas')
|
|
5
|
+
const profileSubmittedChannel = dc.channel('datadog:profiling:mock-profile-submitted')
|
|
6
|
+
const { DD_PROFILING_UPLOAD_PERIOD } = process.env
|
|
7
|
+
|
|
8
|
+
let timerId
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
start: config => {
|
|
12
|
+
// Copied from packages/dd-trace/src/profiler.js
|
|
13
|
+
const flushInterval = coalesce(config.interval, Number(DD_PROFILING_UPLOAD_PERIOD) * 1000, 65 * 1000)
|
|
14
|
+
|
|
15
|
+
function scheduleProfileSubmit () {
|
|
16
|
+
timerId = setTimeout(emitProfileSubmit, flushInterval)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function emitProfileSubmit () {
|
|
20
|
+
profileSubmittedChannel.publish()
|
|
21
|
+
scheduleProfileSubmit()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
scheduleProfileSubmit()
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
stop: () => {
|
|
28
|
+
if (timerId !== undefined) {
|
|
29
|
+
clearTimeout(timerId)
|
|
30
|
+
timerId = undefined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|