Skip to content

Commit f5017bc

Browse files
authored
refactor(config): generate runtime config from supported metadata (#7916)
Config model: Build the tracer runtime config from `supported-configurations.json`. The metadata now defines defaults, aliases, transforms, allowed values, and internal property mapping. Env vars, stable config, code options, remote config, and telemetry now go through the same parsing and normalization path instead of separate logic. Remote config and fallback: Normalize remote config into the same local option names before apply. When a remote value is removed, restore the earlier code value or the default for that one option, instead of keeping an old remote value. This also keeps config source tracking correct when remote config changes over time. Telemetry: Report the same normalized config view that the tracer really uses. Telemetry now keeps the source of each value, sends standard config names such as `DD_*` and `OTEL_*`, and serializes URLs, arrays, objects, functions, and rule sets in one consistent format for config change events and extended heartbeats. DD and OTEL precedence: Use one precedence model for Datadog and OpenTelemetry settings. Datadog config still wins when both are set. Generic `OTEL_EXPORTER_OTLP_*` endpoint, header, protocol, and timeout values now fill in logs and metrics config when the specific setting is not set. `b3` extraction keeps Datadog multi-header behavior when it comes from `DD_TRACE_PROPAGATION_STYLE`, and keeps single-header behavior when it comes from `OTEL_PROPAGATORS`. Config-specific updates: Parse and validate more individual settings through shared parsers. This now covers sample rates, propagation styles and behavior, `OTEL_RESOURCE_ATTRIBUTES`, `DD_TRACE_HEADER_TAGS` spacing, JSON sampling rules, AppSec blocked template file paths, and `DD_GRPC_CLIENT_ERROR_STATUSES` / `DD_GRPC_SERVER_ERROR_STATUSES` range values. Invalid values now warn the same way and fall back more predictably. DD_LOG_INJECTION was renamed to DD_LOGS_INJECTION to be consistent with other platforms. This is an internal only configuration why it is not a breaking change. Calculated behavior: Recalculate derived settings from the remembered source of each value. This keeps service and tag inference, socket and DogStatsD defaults, OTEL logs vs log injection, runtime metrics vs `OTEL_METRICS_EXPORTER=none`, AppSec-driven resource renaming, CI and serverless toggles, Lambda flush behavior, and agentless tracing overrides consistent when inputs change. Runtime consumers: Move profiling, logging, agent URL selection, session propagation, and other runtime code to read the resolved config instead of reparsing env vars. This also keeps proxy startup working when tracing is off but dynamic instrumentation or AppSec standalone still need runtime hooks, and makes profiling exporter and profiler settings follow the same config resolution path.
1 parent 635402e commit f5017bc

60 files changed

Lines changed: 3606 additions & 4071 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

benchmark/sirun/exporting-pipeline/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const SpanProcessor = require('../../../packages/dd-trace/src/span_processor')
77
const Exporter = require('../../../packages/dd-trace/src/exporters/agent/index')
88
const PrioritySampler = require('../../../packages/dd-trace/src/priority_sampler')
99
const id = require('../../../packages/dd-trace/src/id')
10-
const defaults = require('../../../packages/dd-trace/src/config/defaults')
10+
const { defaults } = require('../../../packages/dd-trace/src/config/defaults')
1111

1212
const config = {
1313
url: `http://${defaults.hostname}:${defaults.port}`,

benchmark/sirun/statsd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const dgram = require('dgram')
4-
const defaults = require('../../packages/dd-trace/src/config/defaults')
4+
const { defaults } = require('../../packages/dd-trace/src/config/defaults')
55
const port = process.env.SIRUN_STATSD_PORT || defaults['dogstatsd.port']
66

77
class StatsD {

integration-tests/init.spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,9 @@ describe('init.js', () => {
273273
// or on 18.0.0 in particular.
274274
if (semver.satisfies(process.versions.node, '>=14.13.1')) {
275275
describe('initialize.mjs', () => {
276-
setShouldKill(false)
276+
// Node 20.0.0 can leave short-lived loader-based children alive after they
277+
// print the expected output, so terminate them after a short grace period.
278+
setShouldKill(process.versions.node === '20.0.0')
277279
useSandbox()
278280
stubTracerIfNeeded()
279281

integration-tests/opentelemetry.spec.js

Lines changed: 49 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ function nearNow (ts, now = Date.now(), range = 1000) {
5050
return delta < range && delta >= 0
5151
}
5252

53-
describe('opentelemetry', () => {
54-
let agent
53+
describe('opentelemetry', function () {
54+
this.timeout(20000)
55+
56+
let agent = /** @type {FakeAgent | null} */ (null)
5557
let proc
56-
let cwd
58+
let cwd = /** @type {string} */ ('')
5759
const timeout = 5000
5860
const dependencies = [
5961
'@opentelemetry/api@1.8.0',
@@ -75,14 +77,14 @@ describe('opentelemetry', () => {
7577

7678
after(async () => {
7779
await stopProc(proc)
78-
await agent.stop()
80+
await agent?.stop()
7981
})
8082

8183
it("should not capture telemetry DD and OTEL vars don't conflict", async () => {
8284
proc = fork(join(cwd, 'opentelemetry/basic.js'), {
8385
cwd,
8486
env: {
85-
DD_TRACE_AGENT_PORT: agent.port,
87+
DD_TRACE_AGENT_PORT: agent?.port,
8688
DD_TRACE_OTEL_ENABLED: '1',
8789
DD_TELEMETRY_HEARTBEAT_INTERVAL: '1',
8890
TIMEOUT: '1500',
@@ -114,7 +116,7 @@ describe('opentelemetry', () => {
114116
proc = fork(join(cwd, 'opentelemetry/basic.js'), {
115117
cwd,
116118
env: {
117-
DD_TRACE_AGENT_PORT: agent.port,
119+
DD_TRACE_AGENT_PORT: agent?.port,
118120
DD_TRACE_OTEL_ENABLED: '1',
119121
DD_TELEMETRY_HEARTBEAT_INTERVAL: '1',
120122
TIMEOUT: '1500',
@@ -147,42 +149,20 @@ describe('opentelemetry', () => {
147149

148150
const otelHiding = metrics.series.filter(({ metric }) => metric === 'otel.env.hiding')
149151
const otelInvalid = metrics.series.filter(({ metric }) => metric === 'otel.env.invalid')
150-
assert.strictEqual(otelHiding.length, 9)
151-
assert.strictEqual(otelInvalid.length, 0)
152-
153-
assert.deepStrictEqual(otelHiding[0].tags, [
154-
'config_datadog:dd_trace_log_level', 'config_opentelemetry:otel_log_level',
155-
])
156-
assert.deepStrictEqual(otelHiding[1].tags, [
157-
'config_datadog:dd_trace_propagation_style', 'config_opentelemetry:otel_propagators',
158-
])
159-
assert.deepStrictEqual(otelHiding[2].tags, [
160-
'config_datadog:dd_service', 'config_opentelemetry:otel_service_name',
161-
])
162-
163-
assert.deepStrictEqual(otelHiding[3].tags, [
164-
'config_datadog:dd_trace_sample_rate', 'config_opentelemetry:otel_traces_sampler',
165-
])
166-
167-
assert.deepStrictEqual(otelHiding[4].tags, [
168-
'config_datadog:dd_trace_sample_rate', 'config_opentelemetry:otel_traces_sampler_arg',
169-
])
170-
171-
assert.deepStrictEqual(otelHiding[5].tags, [
172-
'config_datadog:dd_trace_enabled', 'config_opentelemetry:otel_traces_exporter',
173-
])
174-
175-
assert.deepStrictEqual(otelHiding[6].tags, [
176-
'config_datadog:dd_runtime_metrics_enabled', 'config_opentelemetry:otel_metrics_exporter',
177-
])
178152

179-
assert.deepStrictEqual(otelHiding[7].tags, [
180-
'config_datadog:dd_tags', 'config_opentelemetry:otel_resource_attributes',
181-
])
153+
assert.deepStrictEqual(sortMetricTags(otelHiding), sortMetricTags([
154+
['config_datadog:dd_trace_log_level', 'config_opentelemetry:otel_log_level'],
155+
['config_datadog:dd_trace_propagation_style', 'config_opentelemetry:otel_propagators'],
156+
['config_datadog:dd_service', 'config_opentelemetry:otel_service_name'],
157+
['config_datadog:dd_trace_sample_rate', 'config_opentelemetry:otel_traces_sampler'],
158+
['config_datadog:dd_trace_sample_rate', 'config_opentelemetry:otel_traces_sampler_arg'],
159+
['config_datadog:dd_trace_enabled', 'config_opentelemetry:otel_traces_exporter'],
160+
['config_datadog:dd_runtime_metrics_enabled', 'config_opentelemetry:otel_metrics_exporter'],
161+
['config_datadog:dd_tags', 'config_opentelemetry:otel_resource_attributes'],
162+
['config_datadog:dd_trace_otel_enabled', 'config_opentelemetry:otel_sdk_disabled'],
163+
]))
182164

183-
assert.deepStrictEqual(otelHiding[8].tags, [
184-
'config_datadog:dd_trace_otel_enabled', 'config_opentelemetry:otel_sdk_disabled',
185-
])
165+
assert.deepStrictEqual(sortMetricTags(otelInvalid), [])
186166

187167
for (const metric of otelHiding) {
188168
assert.strictEqual(metric.points[0][1], 1)
@@ -194,7 +174,7 @@ describe('opentelemetry', () => {
194174
proc = fork(join(cwd, 'opentelemetry/basic.js'), {
195175
cwd,
196176
env: {
197-
DD_TRACE_AGENT_PORT: agent.port,
177+
DD_TRACE_AGENT_PORT: agent?.port,
198178
DD_TRACE_OTEL_ENABLED: '1',
199179
DD_TELEMETRY_HEARTBEAT_INTERVAL: '1',
200180
TIMEOUT: '1500',
@@ -221,47 +201,20 @@ describe('opentelemetry', () => {
221201
const otelHiding = metrics.series.filter(({ metric }) => metric === 'otel.env.hiding')
222202
const otelInvalid = metrics.series.filter(({ metric }) => metric === 'otel.env.invalid')
223203

224-
assert.strictEqual(otelHiding.length, 1)
225-
assert.strictEqual(otelInvalid.length, 8)
226-
227-
assert.deepStrictEqual(otelHiding[0].tags, [
228-
'config_datadog:dd_trace_otel_enabled', 'config_opentelemetry:otel_sdk_disabled',
229-
])
230-
231-
assert.deepStrictEqual(otelInvalid[0].tags, [
232-
'config_datadog:dd_trace_log_level', 'config_opentelemetry:otel_log_level',
233-
])
234-
235-
assert.deepStrictEqual(otelInvalid[1].tags, [
236-
'config_datadog:dd_trace_sample_rate',
237-
'config_opentelemetry:otel_traces_sampler',
238-
])
239-
240-
assert.deepStrictEqual(otelInvalid[2].tags, [
241-
'config_datadog:dd_trace_sample_rate',
242-
'config_opentelemetry:otel_traces_sampler_arg',
243-
])
244-
assert.deepStrictEqual(otelInvalid[3].tags, [
245-
'config_datadog:dd_trace_enabled', 'config_opentelemetry:otel_traces_exporter',
246-
])
247-
248-
assert.deepStrictEqual(otelInvalid[4].tags, [
249-
'config_datadog:dd_runtime_metrics_enabled',
250-
'config_opentelemetry:otel_metrics_exporter',
251-
])
252-
253-
assert.deepStrictEqual(otelInvalid[5].tags, [
254-
'config_datadog:dd_trace_otel_enabled', 'config_opentelemetry:otel_sdk_disabled',
255-
])
256-
257-
assert.deepStrictEqual(otelInvalid[6].tags, [
258-
'config_opentelemetry:otel_logs_exporter',
259-
])
260-
261-
assert.deepStrictEqual(otelInvalid[7].tags, [
262-
'config_datadog:dd_trace_propagation_style',
263-
'config_opentelemetry:otel_propagators',
264-
])
204+
assert.deepStrictEqual(sortMetricTags(otelHiding), sortMetricTags([
205+
['config_datadog:dd_trace_otel_enabled', 'config_opentelemetry:otel_sdk_disabled'],
206+
]))
207+
208+
assert.deepStrictEqual(sortMetricTags(otelInvalid), sortMetricTags([
209+
['config_datadog:dd_trace_log_level', 'config_opentelemetry:otel_log_level'],
210+
['config_datadog:dd_trace_propagation_style', 'config_opentelemetry:otel_propagators'],
211+
['config_opentelemetry:otel_logs_exporter'],
212+
['config_datadog:dd_trace_sample_rate', 'config_opentelemetry:otel_traces_sampler'],
213+
['config_datadog:dd_trace_sample_rate', 'config_opentelemetry:otel_traces_sampler_arg'],
214+
['config_datadog:dd_trace_enabled', 'config_opentelemetry:otel_traces_exporter'],
215+
['config_datadog:dd_runtime_metrics_enabled', 'config_opentelemetry:otel_metrics_exporter'],
216+
['config_datadog:dd_trace_otel_enabled', 'config_opentelemetry:otel_sdk_disabled'],
217+
]))
265218

266219
for (const metric of otelInvalid) {
267220
assert.strictEqual(metric.points[0][1], 1)
@@ -273,7 +226,7 @@ describe('opentelemetry', () => {
273226
proc = fork(join(cwd, 'opentelemetry/basic.js'), {
274227
cwd,
275228
env: {
276-
DD_TRACE_AGENT_PORT: agent.port,
229+
DD_TRACE_AGENT_PORT: agent?.port,
277230
},
278231
})
279232
await check(agent, proc, timeout, ({ payload }) => {
@@ -292,7 +245,7 @@ describe('opentelemetry', () => {
292245
proc = fork(join(cwd, 'opentelemetry/basic.js'), {
293246
cwd,
294247
env: {
295-
DD_TRACE_AGENT_PORT: agent.port,
248+
DD_TRACE_AGENT_PORT: agent?.port,
296249
DD_TRACE_OTEL_ENABLED: '1',
297250
DD_TELEMETRY_HEARTBEAT_INTERVAL: '1',
298251
TIMEOUT: '1500',
@@ -334,7 +287,7 @@ describe('opentelemetry', () => {
334287
proc = fork(join(cwd, 'opentelemetry/auto-instrumentation.js'), {
335288
cwd,
336289
env: {
337-
DD_TRACE_AGENT_PORT: agent.port,
290+
DD_TRACE_AGENT_PORT: agent?.port,
338291
DD_TRACE_OTEL_ENABLED: '1',
339292
SERVER_PORT,
340293
DD_TRACE_DISABLED_INSTRUMENTATIONS: 'http,dns,express,net',
@@ -378,7 +331,7 @@ describe('opentelemetry', () => {
378331
proc = fork(join(cwd, 'opentelemetry/server.js'), {
379332
cwd,
380333
env: {
381-
DD_TRACE_AGENT_PORT: agent.port,
334+
DD_TRACE_AGENT_PORT: agent?.port,
382335
},
383336
})
384337
await check(agent, proc, timeout, ({ payload }) => {
@@ -407,7 +360,7 @@ describe('opentelemetry', () => {
407360
proc = fork(join(cwd, 'opentelemetry/auto-instrumentation.js'), {
408361
cwd,
409362
env: {
410-
DD_TRACE_AGENT_PORT: agent.port,
363+
DD_TRACE_AGENT_PORT: agent?.port,
411364
DD_TRACE_OTEL_ENABLED: '1',
412365
SERVER_PORT,
413366
DD_TRACE_DISABLED_INSTRUMENTATIONS: 'http,dns,express,net',
@@ -456,18 +409,12 @@ describe('opentelemetry', () => {
456409
proc = fork(join(cwd, 'opentelemetry/env-var.js'), {
457410
cwd,
458411
env: {
459-
DD_TRACE_AGENT_PORT: agent.port,
412+
DD_TRACE_AGENT_PORT: agent?.port,
460413
},
461414
})
462415
await check(agent, proc, timeout, ({ payload }) => {
463-
// Should have a single trace with a single span
464-
assert.strictEqual(payload.length, 1)
465-
const [trace] = payload
466-
assert.strictEqual(trace.length, 1)
467-
const [span] = trace
468-
469-
// Should be the expected otel span
470-
assert.strictEqual(span.name, 'otel-sub')
416+
const trace = payload.find(trace => trace.length === 1 && trace[0].name === 'otel-sub')
417+
assert.ok(trace)
471418
})
472419
})
473420
})
@@ -477,3 +424,9 @@ function isChildOf (childSpan, parentSpan) {
477424
assert.notStrictEqual(childSpan.span_id.toString(), parentSpan.span_id.toString())
478425
assert.strictEqual(childSpan.parent_id.toString(), parentSpan.span_id.toString())
479426
}
427+
428+
function sortMetricTags (metrics) {
429+
return metrics
430+
.map(metric => Array.isArray(metric) ? metric : metric.tags)
431+
.sort((a, b) => a.join(',').localeCompare(b.join(',')))
432+
}

integration-tests/package-guardrails.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const {
1313
const NODE_OPTIONS = '--require dd-trace/init.js'
1414
const DD_TRACE_DEBUG = 'true'
1515
const DD_INJECTION_ENABLED = 'tracing'
16-
const DD_LOG_LEVEL = 'error'
16+
const DD_LOG_LEVEL = 'info'
1717
const NODE_MAJOR = Number(process.versions.node.split('.')[0])
1818
const FASTIFY_DEP = NODE_MAJOR < 20 ? 'fastify@4' : 'fastify'
1919

integration-tests/telemetry.spec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('telemetry', () => {
2626
proc = await spawnProc(startupTestFile, {
2727
cwd,
2828
env: {
29-
AGENT_PORT: agent.port,
29+
AGENT_PORT: String(agent.port),
3030
DD_LOGS_INJECTION: 'true',
3131
},
3232
})
@@ -66,9 +66,9 @@ describe('telemetry', () => {
6666
await agent.assertTelemetryReceived(msg => {
6767
const { configuration } = msg.payload.payload
6868
assertObjectContains(configuration, [
69-
{ name: 'DD_LOG_INJECTION', value: true, origin: 'default' },
70-
{ name: 'DD_LOG_INJECTION', value: true, origin: 'env_var' },
71-
{ name: 'DD_LOG_INJECTION', value: false, origin: 'code' },
69+
{ name: 'DD_LOGS_INJECTION', value: true, origin: 'default' },
70+
{ name: 'DD_LOGS_INJECTION', value: true, origin: 'env_var' },
71+
{ name: 'DD_LOGS_INJECTION', value: false, origin: 'code' },
7272
])
7373
}, 'app-started', 5_000, 1)
7474
})

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"bench": "node benchmark/index.js",
1212
"bench:e2e:test-optimization": "node benchmark/e2e-test-optimization/benchmark-run.js",
1313
"dependencies:dedupe": "yarn-deduplicate yarn.lock",
14+
"generate:config:types": "node scripts/generate-config-types.js",
15+
"verify:config:types": "node scripts/generate-config-types.js --check",
1416
"type:check": "tsc --noEmit -p tsconfig.dev.json",
1517
"type:doc:build": "cd docs && yarn && yarn build",
1618
"type:doc:test": "cd docs && yarn && yarn test",

packages/datadog-plugin-aws-sdk/src/base.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ class BaseAwsSdkPlugin extends ClientPlugin {
2323
return id
2424
}
2525

26+
/** @type {import('../../dd-trace/src/config/config-types').ConfigProperties['cloudPayloadTagging']} */
2627
get cloudTaggingConfig () {
2728
return this._tracerConfig.cloudPayloadTagging
2829
}
2930

3031
get payloadTaggingRules () {
31-
return this.cloudTaggingConfig.rules.aws?.[this.constructor.id]
32+
return this.cloudTaggingConfig.rules?.aws?.[this.constructor.id]
3233
}
3334

3435
constructor (...args) {
@@ -78,7 +79,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
7879
this.requestInject(span, request)
7980
})
8081

81-
if (this.constructor.isPayloadReporter && this.cloudTaggingConfig.requestsEnabled) {
82+
if (this.constructor.isPayloadReporter && this.cloudTaggingConfig.request) {
8283
const maxDepth = this.cloudTaggingConfig.maxDepth
8384
const requestTags = tagsFromRequest(this.payloadTaggingRules, request.params, { maxDepth })
8485
span.addTags(requestTags)
@@ -215,7 +216,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
215216

216217
span.addTags(tags)
217218

218-
if (this.constructor.isPayloadReporter && this.cloudTaggingConfig.responsesEnabled) {
219+
if (this.constructor.isPayloadReporter && this.cloudTaggingConfig.response) {
219220
const maxDepth = this.cloudTaggingConfig.maxDepth
220221
const responseBody = this.extractResponseBody(response)
221222
const responseTags = tagsFromResponse(this.payloadTaggingRules, responseBody, { maxDepth })

packages/datadog-plugin-grpc/test/client.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const loader = require('../../../versions/@grpc/proto-loader').get()
1212
const { withNamingSchema, withPeerService, withVersions } = require('../../dd-trace/test/setup/mocha')
1313
const agent = require('../../dd-trace/test/plugins/agent')
1414
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
15-
const defaults = require('../../dd-trace/src/config/defaults')
15+
const { defaults } = require('../../dd-trace/src/config/defaults')
1616
const { NODE_MAJOR } = require('../../../version')
1717
const getService = require('./service')
1818

packages/datadog-plugin-grpc/test/server.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const { assertObjectContains } = require('../../../integration-tests/helpers')
1111
const { withNamingSchema, withVersions } = require('../../dd-trace/test/setup/mocha')
1212
const agent = require('../../dd-trace/test/plugins/agent')
1313
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
14-
const defaults = require('../../dd-trace/src/config/defaults')
14+
const { defaults } = require('../../dd-trace/src/config/defaults')
1515
const { NODE_MAJOR } = require('../../../version')
1616

1717
const GRPC_SERVER_ERROR_STATUSES = defaults['grpc.server.error.statuses']

0 commit comments

Comments
 (0)