dd-trace 5.110.0 → 5.111.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 +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +13 -0
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +9 -9
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +10 -4
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +69 -63
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +61 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +90 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +12 -3
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +11 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -112,6 +112,12 @@ const TEST_RETRY_REASON = 'test.retry_reason'
|
|
|
112
112
|
const TEST_HAS_FAILED_ALL_RETRIES = 'test.has_failed_all_retries'
|
|
113
113
|
const TEST_IS_MODIFIED = 'test.is_modified'
|
|
114
114
|
const TEST_HAS_DYNAMIC_NAME = '_dd.has_dynamic_name'
|
|
115
|
+
const EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS = [
|
|
116
|
+
{ limitMs: 5 * 1000, key: '5s' },
|
|
117
|
+
{ limitMs: 10 * 1000, key: '10s' },
|
|
118
|
+
{ limitMs: 30 * 1000, key: '30s' },
|
|
119
|
+
{ limitMs: 5 * 60 * 1000, key: '5m' },
|
|
120
|
+
]
|
|
115
121
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
116
122
|
|
|
117
123
|
// Matches patterns that are almost certainly runtime-generated values in test names:
|
|
@@ -493,6 +499,7 @@ module.exports = {
|
|
|
493
499
|
removeInvalidMetadata,
|
|
494
500
|
parseAnnotations,
|
|
495
501
|
getIsFaultyEarlyFlakeDetection,
|
|
502
|
+
EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
496
503
|
getEfdRetryCount,
|
|
497
504
|
getMaxEfdRetryCount,
|
|
498
505
|
TEST_BROWSER_DRIVER,
|
|
@@ -1447,13 +1454,7 @@ function parseAnnotations (annotations) {
|
|
|
1447
1454
|
* @returns {number}
|
|
1448
1455
|
*/
|
|
1449
1456
|
function getEfdRetryCount (durationMs, slowTestRetries) {
|
|
1450
|
-
const
|
|
1451
|
-
{ limitMs: 5 * 1000, key: '5s' },
|
|
1452
|
-
{ limitMs: 10 * 1000, key: '10s' },
|
|
1453
|
-
{ limitMs: 30 * 1000, key: '30s' },
|
|
1454
|
-
{ limitMs: 5 * 60 * 1000, key: '5m' },
|
|
1455
|
-
]
|
|
1456
|
-
for (const { limitMs, key } of thresholds) {
|
|
1457
|
+
for (const { limitMs, key } of EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS) {
|
|
1457
1458
|
if (durationMs < limitMs) {
|
|
1458
1459
|
return slowTestRetries[key] ?? 0
|
|
1459
1460
|
}
|
|
@@ -1616,7 +1617,7 @@ function isFailedTestReplaySupported (testFramework, frameworkVersion) {
|
|
|
1616
1617
|
: true
|
|
1617
1618
|
}
|
|
1618
1619
|
|
|
1619
|
-
function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
|
|
1620
|
+
function getLibraryCapabilitiesTags (testFramework, frameworkVersion, options = {}) {
|
|
1620
1621
|
return {
|
|
1621
1622
|
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: isTiaSupported(testFramework)
|
|
1622
1623
|
? '1'
|
|
@@ -1638,7 +1639,8 @@ function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
|
|
|
1638
1639
|
isAttemptToFixSupported(testFramework, frameworkVersion)
|
|
1639
1640
|
? '5'
|
|
1640
1641
|
: undefined,
|
|
1641
|
-
[DD_CAPABILITIES_FAILED_TEST_REPLAY]:
|
|
1642
|
+
[DD_CAPABILITIES_FAILED_TEST_REPLAY]: !options.omitFailedTestReplay &&
|
|
1643
|
+
isFailedTestReplaySupported(testFramework, frameworkVersion)
|
|
1642
1644
|
? '1'
|
|
1643
1645
|
: undefined,
|
|
1644
1646
|
}
|
|
@@ -18,7 +18,7 @@ function exporterFromURL (url) {
|
|
|
18
18
|
return new FileExporter({}, fileURLToPath(url))
|
|
19
19
|
}
|
|
20
20
|
// The subprocess has no tracer config, so mirror the canonical config property
|
|
21
|
-
// names the exporters read. Normalize the raw env to the parsed profiling.
|
|
21
|
+
// names the exporters read. Normalize the raw env to the parsed profiling.DD_PROFILING_ENABLED
|
|
22
22
|
// values ('true' | 'false' | 'auto') the main process would have produced.
|
|
23
23
|
const profilingEnabled = (getValueFromEnvSources('DD_PROFILING_ENABLED') ?? '').toLowerCase()
|
|
24
24
|
const enabled = profilingEnabled === 'auto'
|
|
@@ -28,7 +28,7 @@ function exporterFromURL (url) {
|
|
|
28
28
|
url,
|
|
29
29
|
DD_PROFILING_UPLOAD_TIMEOUT: timeoutMs,
|
|
30
30
|
DD_INJECTION_ENABLED: getValueFromEnvSources('DD_INJECTION_ENABLED'),
|
|
31
|
-
profiling: { enabled },
|
|
31
|
+
profiling: { DD_PROFILING_ENABLED: enabled },
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -11,10 +11,10 @@ const processTags = require('../../process-tags')
|
|
|
11
11
|
/** @typedef {import('../../config/config-base')} TracerConfig */
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Maps the canonical profiling.
|
|
14
|
+
* Maps the canonical profiling.DD_PROFILING_ENABLED value to the activation reported in the
|
|
15
15
|
* profile event.
|
|
16
16
|
*
|
|
17
|
-
* @param {string} [enabled] - config.profiling.
|
|
17
|
+
* @param {string} [enabled] - config.profiling.DD_PROFILING_ENABLED ('true' | 'false' | 'auto')
|
|
18
18
|
* @returns {string}
|
|
19
19
|
*/
|
|
20
20
|
function getActivation (enabled) {
|
|
@@ -38,7 +38,7 @@ class EventSerializer {
|
|
|
38
38
|
this.#service = config.service
|
|
39
39
|
this.#appVersion = config.version
|
|
40
40
|
this.#libraryInjected = !!config.DD_INJECTION_ENABLED
|
|
41
|
-
this.#activation = getActivation(config.profiling?.
|
|
41
|
+
this.#activation = getActivation(config.profiling?.DD_PROFILING_ENABLED)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -148,7 +148,7 @@ class NativeWallProfiler {
|
|
|
148
148
|
this.#endpointCollectionEnabled = config.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED
|
|
149
149
|
this.#flushIntervalMillis = flushInterval
|
|
150
150
|
this.#samplingIntervalMicros = SAMPLING_INTERVAL * 1000
|
|
151
|
-
this.#telemetryHeartbeatIntervalMillis = config.telemetry.
|
|
151
|
+
this.#telemetryHeartbeatIntervalMillis = config.telemetry.DD_TELEMETRY_HEARTBEAT_INTERVAL
|
|
152
152
|
this.#timelineEnabled = config.DD_PROFILING_TIMELINE_ENABLED
|
|
153
153
|
this.#v8ProfilerBugWorkaroundEnabled = config.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND
|
|
154
154
|
|
|
@@ -138,7 +138,7 @@ class Tracer extends NoopProxy {
|
|
|
138
138
|
spanleak.startScrubber()
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
if (config.remoteConfig.
|
|
141
|
+
if (config.remoteConfig.DD_REMOTE_CONFIGURATION_ENABLED && !config.isCiVisibility) {
|
|
142
142
|
const RemoteConfig = require('./remote_config')
|
|
143
143
|
const rc = new RemoteConfig(config)
|
|
144
144
|
|
|
@@ -180,11 +180,11 @@ class Tracer extends NoopProxy {
|
|
|
180
180
|
openfeatureRemoteConfig.enable(rc, config, () => this.openfeature)
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
if (config.profiling.
|
|
183
|
+
if (config.profiling.DD_PROFILING_ENABLED === 'true') {
|
|
184
184
|
this._profilerStarted = this._startProfiler(config)
|
|
185
185
|
} else {
|
|
186
186
|
this._profilerStarted = false
|
|
187
|
-
if (config.profiling.
|
|
187
|
+
if (config.profiling.DD_PROFILING_ENABLED === 'auto') {
|
|
188
188
|
const { SSIHeuristics } = require('./profiling/ssi-heuristics')
|
|
189
189
|
const ssiHeuristics = new SSIHeuristics(config)
|
|
190
190
|
ssiHeuristics.start()
|
|
@@ -216,7 +216,7 @@ class Tracer extends NoopProxy {
|
|
|
216
216
|
|
|
217
217
|
this._modules.rewriter.enable(config)
|
|
218
218
|
|
|
219
|
-
if (config.
|
|
219
|
+
if (config.DD_TRACE_ENABLED && config.testOptimization.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
|
|
220
220
|
const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
|
|
221
221
|
this._testApiManualPlugin = new TestApiManualPlugin(this)
|
|
222
222
|
// `shouldGetEnvironmentData` is passed as false so that we only lazily calculate it
|
|
@@ -225,7 +225,7 @@ class Tracer extends NoopProxy {
|
|
|
225
225
|
this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
|
|
226
226
|
}
|
|
227
227
|
if (config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
|
|
228
|
-
if (config.
|
|
228
|
+
if (config.DD_API_KEY) {
|
|
229
229
|
const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
|
|
230
230
|
const automaticLogPlugin = new LogSubmissionPlugin(this)
|
|
231
231
|
automaticLogPlugin.configure({ ...config, enabled: true })
|
|
@@ -237,7 +237,7 @@ class Tracer extends NoopProxy {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
if (config.
|
|
240
|
+
if (config.testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
|
|
241
241
|
const getDynamicInstrumentationClient = require('./ci-visibility/dynamic-instrumentation')
|
|
242
242
|
// We instantiate the client but do not start the Worker here. The worker is started lazily
|
|
243
243
|
getDynamicInstrumentationClient(config)
|
|
@@ -270,11 +270,11 @@ class Tracer extends NoopProxy {
|
|
|
270
270
|
* @param {import('./config/config-base')} config - Tracer configuration
|
|
271
271
|
*/
|
|
272
272
|
#updateTracing (config) {
|
|
273
|
-
if (config.
|
|
273
|
+
if (config.DD_TRACE_ENABLED !== false) {
|
|
274
274
|
if (config.appsec.enabled) {
|
|
275
275
|
this._modules.appsec.enable(config)
|
|
276
276
|
}
|
|
277
|
-
if (config.llmobs.
|
|
277
|
+
if (config.llmobs.DD_LLMOBS_ENABLED) {
|
|
278
278
|
this._modules.llmobs.enable(config)
|
|
279
279
|
}
|
|
280
280
|
if (!this._tracingInitialized) {
|
|
@@ -11,16 +11,15 @@ const startedSpans = new WeakSet()
|
|
|
11
11
|
const finishedSpans = new WeakSet()
|
|
12
12
|
|
|
13
13
|
class SpanProcessor {
|
|
14
|
-
constructor (exporter, prioritySampler, config) {
|
|
14
|
+
constructor (exporter, prioritySampler, config, otlpStatsExporter) {
|
|
15
15
|
this._exporter = exporter
|
|
16
16
|
this._prioritySampler = prioritySampler
|
|
17
17
|
this._config = config
|
|
18
18
|
this._killAll = false
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
if (config.stats?.enabled && !config.appsec?.standalone?.enabled) {
|
|
20
|
+
if (config.stats?.DD_TRACE_STATS_COMPUTATION_ENABLED && !config.appsec?.standalone?.enabled) {
|
|
22
21
|
const { SpanStatsProcessor } = require('./span_stats')
|
|
23
|
-
this._stats = new SpanStatsProcessor(config)
|
|
22
|
+
this._stats = new SpanStatsProcessor(config, otlpStatsExporter)
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
this._spanSampler = new SpanSampler(config.sampler)
|
|
@@ -42,11 +41,11 @@ class SpanProcessor {
|
|
|
42
41
|
const active = []
|
|
43
42
|
const formatted = []
|
|
44
43
|
const trace = spanContext._trace
|
|
45
|
-
const { flushMinSpans,
|
|
44
|
+
const { flushMinSpans, DD_TRACE_ENABLED } = this._config
|
|
46
45
|
const { started, finished } = trace
|
|
47
46
|
|
|
48
47
|
if (trace.record === false) return
|
|
49
|
-
if (
|
|
48
|
+
if (DD_TRACE_ENABLED === false) {
|
|
50
49
|
this._erase(trace, active)
|
|
51
50
|
return
|
|
52
51
|
}
|
|
@@ -10,8 +10,10 @@ const {
|
|
|
10
10
|
HTTP_ENDPOINT,
|
|
11
11
|
HTTP_ROUTE,
|
|
12
12
|
HTTP_METHOD,
|
|
13
|
+
SPAN_KIND,
|
|
14
|
+
GRPC_STATUS_CODE,
|
|
13
15
|
} = require('../../../ext/tags')
|
|
14
|
-
const { ORIGIN_KEY, TOP_LEVEL_KEY, SVC_SRC_KEY } = require('./constants')
|
|
16
|
+
const { ORIGIN_KEY, TOP_LEVEL_KEY, SVC_SRC_KEY, GRPC_STATUS_NAMES } = require('./constants')
|
|
15
17
|
const { version } = require('./pkg')
|
|
16
18
|
const processTags = require('./process-tags')
|
|
17
19
|
|
|
@@ -27,43 +29,32 @@ class SpanAggStats {
|
|
|
27
29
|
this.aggKey = aggKey
|
|
28
30
|
this.hits = 0
|
|
29
31
|
this.topLevelHits = 0
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
32
|
+
this.topLevelOkDistribution = new LogCollapsingLowestDenseDDSketch()
|
|
33
|
+
this.topLevelErrorDistribution = new LogCollapsingLowestDenseDDSketch()
|
|
34
|
+
this.nonTopLevelOkDistribution = new LogCollapsingLowestDenseDDSketch()
|
|
35
|
+
this.nonTopLevelErrorDistribution = new LogCollapsingLowestDenseDDSketch()
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
record (span) {
|
|
37
39
|
const durationNs = span.duration
|
|
38
40
|
this.hits++
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (span.metrics[TOP_LEVEL_KEY]) {
|
|
42
|
-
this.topLevelHits++
|
|
43
|
-
}
|
|
44
|
-
|
|
41
|
+
const isTopLevel = Boolean(span.metrics[TOP_LEVEL_KEY])
|
|
42
|
+
if (isTopLevel) this.topLevelHits++
|
|
45
43
|
if (span.error) {
|
|
46
|
-
this.
|
|
47
|
-
this.
|
|
44
|
+
if (isTopLevel) this.topLevelErrorDistribution.accept(durationNs)
|
|
45
|
+
else this.nonTopLevelErrorDistribution.accept(durationNs)
|
|
48
46
|
} else {
|
|
49
|
-
this.
|
|
47
|
+
if (isTopLevel) this.topLevelOkDistribution.accept(durationNs)
|
|
48
|
+
else this.nonTopLevelOkDistribution.accept(durationNs)
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
toJSON () {
|
|
54
53
|
const {
|
|
55
|
-
name,
|
|
56
|
-
|
|
57
|
-
resource,
|
|
58
|
-
type,
|
|
59
|
-
statusCode,
|
|
60
|
-
synthetics,
|
|
61
|
-
method,
|
|
62
|
-
endpoint,
|
|
63
|
-
srvSrc,
|
|
54
|
+
name, service, resource, type, statusCode, synthetics, method, endpoint, srvSrc,
|
|
55
|
+
spanKind, rpcStatusCode,
|
|
64
56
|
} = this.aggKey
|
|
65
|
-
|
|
66
|
-
return {
|
|
57
|
+
const base = {
|
|
67
58
|
Name: name,
|
|
68
59
|
Service: service,
|
|
69
60
|
Resource: resource,
|
|
@@ -73,13 +64,34 @@ class SpanAggStats {
|
|
|
73
64
|
HTTPMethod: method,
|
|
74
65
|
HTTPEndpoint: endpoint,
|
|
75
66
|
srv_src: srvSrc,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
SpanKind: spanKind,
|
|
68
|
+
GRPCStatusCode: rpcStatusCode,
|
|
69
|
+
}
|
|
70
|
+
const rows = []
|
|
71
|
+
if (this.topLevelHits > 0) {
|
|
72
|
+
rows.push({
|
|
73
|
+
...base,
|
|
74
|
+
Hits: this.topLevelHits,
|
|
75
|
+
TopLevelHits: this.topLevelHits,
|
|
76
|
+
Errors: this.topLevelErrorDistribution.count,
|
|
77
|
+
Duration: this.topLevelOkDistribution.sum + this.topLevelErrorDistribution.sum,
|
|
78
|
+
OkSummary: this.topLevelOkDistribution.toProto(),
|
|
79
|
+
ErrorSummary: this.topLevelErrorDistribution.toProto(),
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
const nonTopLevelHits = this.hits - this.topLevelHits
|
|
83
|
+
if (nonTopLevelHits > 0) {
|
|
84
|
+
rows.push({
|
|
85
|
+
...base,
|
|
86
|
+
Hits: nonTopLevelHits,
|
|
87
|
+
TopLevelHits: 0,
|
|
88
|
+
Errors: this.nonTopLevelErrorDistribution.count,
|
|
89
|
+
Duration: this.nonTopLevelOkDistribution.sum + this.nonTopLevelErrorDistribution.sum,
|
|
90
|
+
OkSummary: this.nonTopLevelOkDistribution.toProto(), // TODO: custom proto encoding
|
|
91
|
+
ErrorSummary: this.nonTopLevelErrorDistribution.toProto(), // TODO: custom proto encoding
|
|
92
|
+
})
|
|
82
93
|
}
|
|
94
|
+
return rows
|
|
83
95
|
}
|
|
84
96
|
}
|
|
85
97
|
|
|
@@ -94,6 +106,12 @@ class SpanAggKey {
|
|
|
94
106
|
this.endpoint = span.meta[HTTP_ROUTE] || span.meta[HTTP_ENDPOINT] || ''
|
|
95
107
|
this.method = span.meta[HTTP_METHOD] || ''
|
|
96
108
|
this.srvSrc = span.meta[SVC_SRC_KEY] || ''
|
|
109
|
+
this.spanKind = span.meta[SPAN_KIND] || ''
|
|
110
|
+
// dd gRPC plugin sets a numeric code via setTag; OTel/manual sets a string name via meta.
|
|
111
|
+
const grpcCode = span.meta[GRPC_STATUS_CODE] ?? span.metrics?.[GRPC_STATUS_CODE]
|
|
112
|
+
this.rpcStatusCode = typeof grpcCode === 'number'
|
|
113
|
+
? (GRPC_STATUS_NAMES[grpcCode] ?? String(grpcCode))
|
|
114
|
+
: (grpcCode ?? '')
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
toString () {
|
|
@@ -107,6 +125,8 @@ class SpanAggKey {
|
|
|
107
125
|
this.method,
|
|
108
126
|
this.endpoint,
|
|
109
127
|
this.srvSrc,
|
|
128
|
+
this.spanKind,
|
|
129
|
+
this.rpcStatusCode,
|
|
110
130
|
].join(',')
|
|
111
131
|
}
|
|
112
132
|
}
|
|
@@ -137,57 +157,60 @@ class TimeBuckets extends Map {
|
|
|
137
157
|
class SpanStatsProcessor {
|
|
138
158
|
constructor ({
|
|
139
159
|
stats: {
|
|
140
|
-
enabled = false,
|
|
160
|
+
DD_TRACE_STATS_COMPUTATION_ENABLED: enabled = false,
|
|
141
161
|
interval = 10,
|
|
142
|
-
},
|
|
162
|
+
} = {},
|
|
143
163
|
hostname,
|
|
144
164
|
port,
|
|
145
165
|
url,
|
|
146
166
|
env,
|
|
147
167
|
tags,
|
|
148
|
-
version,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
port,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
this.
|
|
157
|
-
this.bucketSizeNs = interval * 1e9
|
|
168
|
+
version: appVersion,
|
|
169
|
+
_DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL: flushIntervalMs,
|
|
170
|
+
} = {}, otlpExporter) {
|
|
171
|
+
if (!otlpExporter) {
|
|
172
|
+
this.exporter = new SpanStatsExporter({ hostname, port, tags, url })
|
|
173
|
+
}
|
|
174
|
+
const intervalMs = otlpExporter ? (flushIntervalMs ?? 10_000) : interval * 1e3
|
|
175
|
+
this.interval = intervalMs / 1e3
|
|
176
|
+
this.bucketSizeNs = intervalMs * 1e6
|
|
158
177
|
this.buckets = new TimeBuckets()
|
|
159
178
|
this.hostname = os.hostname()
|
|
160
179
|
this.enabled = enabled
|
|
180
|
+
this.otlpExporter = otlpExporter || null
|
|
161
181
|
this.env = env
|
|
162
182
|
this.tags = tags || {}
|
|
163
183
|
this.sequence = 0
|
|
164
|
-
this.version =
|
|
184
|
+
this.version = appVersion
|
|
165
185
|
|
|
166
|
-
if (this.enabled) {
|
|
167
|
-
this.timer = setInterval(this.onInterval.bind(this),
|
|
186
|
+
if (this.enabled || this.otlpExporter) {
|
|
187
|
+
this.timer = setInterval(this.onInterval.bind(this), intervalMs)
|
|
168
188
|
this.timer.unref?.()
|
|
169
189
|
}
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
onInterval () {
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
const drained = this.#drainBuckets()
|
|
194
|
+
|
|
195
|
+
if (this.enabled && !this.otlpExporter) {
|
|
196
|
+
this.exporter.export({
|
|
197
|
+
Hostname: this.hostname,
|
|
198
|
+
Env: this.env,
|
|
199
|
+
Version: this.version || version,
|
|
200
|
+
Stats: this.#toV06Payload(drained),
|
|
201
|
+
Lang: 'javascript',
|
|
202
|
+
TracerVersion: pkg.version,
|
|
203
|
+
RuntimeID: this.tags['runtime-id'],
|
|
204
|
+
Sequence: ++this.sequence,
|
|
205
|
+
ProcessTags: processTags.serialized,
|
|
206
|
+
})
|
|
207
|
+
} else if (this.otlpExporter && drained.length > 0) {
|
|
208
|
+
this.otlpExporter.export(drained, this.bucketSizeNs)
|
|
209
|
+
}
|
|
187
210
|
}
|
|
188
211
|
|
|
189
212
|
onSpanFinished (span) {
|
|
190
|
-
if (!this.enabled) return
|
|
213
|
+
if (!this.enabled && !this.otlpExporter) return
|
|
191
214
|
if (!span.metrics[TOP_LEVEL_KEY] && !span.metrics[MEASURED]) return
|
|
192
215
|
|
|
193
216
|
const spanEndNs = span.start + span.duration
|
|
@@ -198,27 +221,22 @@ class SpanStatsProcessor {
|
|
|
198
221
|
.record(span)
|
|
199
222
|
}
|
|
200
223
|
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const serializedBuckets = []
|
|
204
|
-
|
|
224
|
+
#drainBuckets () {
|
|
225
|
+
const drained = []
|
|
205
226
|
for (const [timeNs, bucket] of this.buckets.entries()) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
for (const stats of bucket.values()) {
|
|
209
|
-
bucketAggStats.push(stats.toJSON())
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
serializedBuckets.push({
|
|
213
|
-
Start: timeNs,
|
|
214
|
-
Duration: bucketSizeNs,
|
|
215
|
-
Stats: bucketAggStats,
|
|
216
|
-
})
|
|
227
|
+
drained.push({ timeNs, bucket })
|
|
217
228
|
}
|
|
218
|
-
|
|
219
229
|
this.buckets.clear()
|
|
230
|
+
return drained
|
|
231
|
+
}
|
|
220
232
|
|
|
221
|
-
|
|
233
|
+
#toV06Payload (drained) {
|
|
234
|
+
const { bucketSizeNs } = this
|
|
235
|
+
return drained.map(({ timeNs, bucket }) => ({
|
|
236
|
+
Start: timeNs,
|
|
237
|
+
Duration: bucketSizeNs,
|
|
238
|
+
Stats: [...bucket.values()].flatMap(stats => stats.toJSON()),
|
|
239
|
+
}))
|
|
222
240
|
}
|
|
223
241
|
}
|
|
224
242
|
|
|
@@ -76,6 +76,7 @@ function logGenericError (message) {
|
|
|
76
76
|
*/
|
|
77
77
|
function configInfo () {
|
|
78
78
|
const url = config.url
|
|
79
|
+
const profilingEnabled = config.profiling.DD_PROFILING_ENABLED
|
|
79
80
|
|
|
80
81
|
return {
|
|
81
82
|
[inspect.custom] () {
|
|
@@ -104,7 +105,7 @@ function configInfo () {
|
|
|
104
105
|
...(config.tags && config.tags.version && { dd_version: config.tags.version }),
|
|
105
106
|
log_injection_enabled: !!config.logInjection,
|
|
106
107
|
runtime_metrics_enabled: !!config.runtimeMetrics,
|
|
107
|
-
profiling_enabled:
|
|
108
|
+
profiling_enabled: profilingEnabled === 'true' || profilingEnabled === 'auto',
|
|
108
109
|
appsec_enabled: config.appsec.enabled,
|
|
109
110
|
data_streams_enabled: !!config.dsmEnabled,
|
|
110
111
|
}
|
|
@@ -32,7 +32,7 @@ function waitAndSend (config, application, host) {
|
|
|
32
32
|
const [name, version, initialLoadModule] = dependency.split(' ')
|
|
33
33
|
// If a dependency is from the initial load, *always* send the event
|
|
34
34
|
// Otherwise, only send if dependencyCollection is enabled
|
|
35
|
-
const sendModule = isTrue(initialLoadModule) || config.telemetry
|
|
35
|
+
const sendModule = isTrue(initialLoadModule) || config.telemetry.DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED
|
|
36
36
|
|
|
37
37
|
savedDependenciesToSend.delete(dependency)
|
|
38
38
|
|
|
@@ -11,7 +11,10 @@ const { sendData } = require('./send-data')
|
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
13
13
|
* @typedef {import('./send-data').TelemetryConfig & {
|
|
14
|
-
* appsec
|
|
14
|
+
* appsec: {
|
|
15
|
+
* DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED: boolean,
|
|
16
|
+
* DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT: number
|
|
17
|
+
* }
|
|
15
18
|
* }} TelemetryConfig
|
|
16
19
|
*/
|
|
17
20
|
|
|
@@ -142,8 +145,7 @@ function flushAndSend () {
|
|
|
142
145
|
for (const [key, endpoint] of pendingEndpoints) {
|
|
143
146
|
batchEndpoints.push(endpoint)
|
|
144
147
|
pendingEndpoints.delete(key)
|
|
145
|
-
|
|
146
|
-
if (batchEndpoints.length >= (config.appsec?.apiSecurity?.endpointCollectionMessageLimit ?? 0)) break
|
|
148
|
+
if (batchEndpoints.length >= config.appsec.DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT) break
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
const payloadObj = {
|
|
@@ -184,7 +186,7 @@ function flushAndSend () {
|
|
|
184
186
|
* @param {import('./send-data').SendDataCallback} updateRetryDataFunction
|
|
185
187
|
*/
|
|
186
188
|
function start (_config, _application, _host, getRetryDataFunction, updateRetryDataFunction) {
|
|
187
|
-
if (!_config.appsec
|
|
189
|
+
if (!_config.appsec.DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED) return
|
|
188
190
|
|
|
189
191
|
config = _config
|
|
190
192
|
application = _application
|
|
@@ -5,14 +5,14 @@ let telemetry
|
|
|
5
5
|
// Lazy load the telemetry module to avoid the performance impact of loading it unconditionally
|
|
6
6
|
module.exports = {
|
|
7
7
|
start (config, ...args) {
|
|
8
|
-
if (!config.telemetry.
|
|
8
|
+
if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED) return
|
|
9
9
|
telemetry ??= require('./telemetry')
|
|
10
10
|
telemetry.start(config, ...args)
|
|
11
11
|
},
|
|
12
12
|
// This might be called before `start` so we have to trigger loading the
|
|
13
13
|
// underlying module here as well.
|
|
14
14
|
updateConfig (changes, config, ...args) {
|
|
15
|
-
if (!config.telemetry.
|
|
15
|
+
if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED) return
|
|
16
16
|
telemetry ??= require('./telemetry')
|
|
17
17
|
telemetry.updateConfig(changes, config, ...args)
|
|
18
18
|
},
|
|
@@ -87,12 +87,12 @@ function getHeaders (config, application, reqType) {
|
|
|
87
87
|
if (config.DD_ROOT_JS_SESSION_ID) {
|
|
88
88
|
headers['dd-root-session-id'] = config.DD_ROOT_JS_SESSION_ID
|
|
89
89
|
}
|
|
90
|
-
const debug = config.telemetry && config.telemetry.
|
|
90
|
+
const debug = config.telemetry && config.telemetry.DD_TELEMETRY_DEBUG
|
|
91
91
|
if (debug) {
|
|
92
92
|
headers['dd-telemetry-debug-enabled'] = 'true'
|
|
93
93
|
}
|
|
94
|
-
if (config.
|
|
95
|
-
headers['dd-api-key'] = config.
|
|
94
|
+
if (config.DD_API_KEY) {
|
|
95
|
+
headers['dd-api-key'] = config.DD_API_KEY
|
|
96
96
|
}
|
|
97
97
|
return headers
|
|
98
98
|
}
|
|
@@ -137,16 +137,16 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
137
137
|
hostname,
|
|
138
138
|
port,
|
|
139
139
|
isCiVisibility,
|
|
140
|
-
|
|
140
|
+
testOptimization,
|
|
141
141
|
} = config
|
|
142
142
|
|
|
143
143
|
let url = config.url
|
|
144
144
|
|
|
145
|
-
const isCiVisibilityAgentlessMode = isCiVisibility && DD_CIVISIBILITY_AGENTLESS_ENABLED
|
|
145
|
+
const isCiVisibilityAgentlessMode = isCiVisibility && testOptimization.DD_CIVISIBILITY_AGENTLESS_ENABLED
|
|
146
146
|
|
|
147
147
|
if (isCiVisibilityAgentlessMode) {
|
|
148
148
|
try {
|
|
149
|
-
url =
|
|
149
|
+
url = testOptimization.DD_CIVISIBILITY_AGENTLESS_URL ?? new URL(getAgentlessTelemetryEndpoint(config.site))
|
|
150
150
|
} catch (err) {
|
|
151
151
|
log.error('Telemetry endpoint url is invalid', err)
|
|
152
152
|
// No point to do the request if the URL is invalid
|
|
@@ -176,7 +176,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
176
176
|
})
|
|
177
177
|
|
|
178
178
|
request(data, options, (error) => {
|
|
179
|
-
if (error && config.
|
|
179
|
+
if (error && config.DD_API_KEY && config.site) {
|
|
180
180
|
if (agentTelemetry) {
|
|
181
181
|
log.warn('Agent telemetry failed, started agentless telemetry')
|
|
182
182
|
agentTelemetry = false
|
|
@@ -189,7 +189,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
189
189
|
log.error('Invalid Telemetry URL')
|
|
190
190
|
return
|
|
191
191
|
}
|
|
192
|
-
const backendHeader = { ...options.headers, 'DD-API-KEY': config.
|
|
192
|
+
const backendHeader = { ...options.headers, 'DD-API-KEY': config.DD_API_KEY }
|
|
193
193
|
const backendOptions = {
|
|
194
194
|
...options,
|
|
195
195
|
url: backendUrl,
|
|
@@ -40,7 +40,7 @@ function onChildProcessStart (context) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function start (config) {
|
|
43
|
-
if (!config.telemetry
|
|
43
|
+
if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED || subscribed) return
|
|
44
44
|
subscribed = true
|
|
45
45
|
|
|
46
46
|
runtimeId = config.DD_ROOT_JS_SESSION_ID || config.tags['runtime-id']
|