Skip to content

Conversation

@mtoffl01
Copy link
Contributor

@mtoffl01 mtoffl01 commented Oct 17, 2025

What does this PR do?

Previously, only product-enablement configs were supported for Declarative Config (application_monitoring.yaml files). This PR adds support for all env-var configs. Now, all 3 sources -- local file, env vars, and fleet file -- go through the same core logic in new function _applyConfigValues.

The following [smaller] changes were also made as a result of necessary refactoring:

  1. Moves processing logic for Otel & DD propagators out of Config constructor and into _applyConfigValues
  2. Moves processing logic for DD_API_KEY, DD_APP_KEY, DD_INSTRUMENTATION_INSTALL_* and DD_TRACE_CLOUD_* settings out of constructor and into _applyConfigValues (See test should support legacy direct-set fields through all stableconfig and env var sources)
  3. Moves logic for determining defaults based on environment (e.g, serverless) into Config constructor

Motivation

RFC
Declarative Config allows APM configuration to be managed at the host level through a YAML-formatted file. This approach is used in the fleet automation workflow, where users can apply configurations to their SDKs via the Fleet UI, or manage them directly through YAML files. Expanding support to all environment-variable-based settings bolsters feature parity and allows greater flexibility for users who prefer or rely on declarative configuration.

Plugin Checklist

Additional Notes

@mtoffl01 mtoffl01 requested review from a team as code owners October 17, 2025 19:20
@mtoffl01 mtoffl01 requested review from BridgeAR and removed request for a team October 17, 2025 19:20
@mtoffl01 mtoffl01 marked this pull request as draft October 17, 2025 19:20
Comment on lines +81 to 82
// This method, and callers of this method, need to be updated to check for declarative config sources as well.
getEnvironmentVariable (name) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getEnvironmentVariable is called 150+ times across the codebase. I'm thinking the changes described on line 81 should be done in a separate PR.

@pr-commenter
Copy link

pr-commenter bot commented Oct 21, 2025

Benchmarks

Benchmark execution time: 2025-10-28 19:47:22

Comparing candidate commit 095fffd in PR branch mtoff/dcfg-extended-configs with baseline commit 90b7c96 in branch master.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 1606 metrics, 64 unstable metrics.

@github-actions
Copy link

github-actions bot commented Oct 21, 2025

Overall package size

Self size: 13.15 MB
Deduped: 115.94 MB
No deduping: 118.15 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | @datadog/libdatadog | 0.7.0 | 35.02 MB | 35.02 MB | | @datadog/native-appsec | 10.3.0 | 20.73 MB | 20.74 MB | | @datadog/native-iast-taint-tracking | 4.0.0 | 11.72 MB | 11.73 MB | | @datadog/pprof | 5.11.1 | 9.96 MB | 10.34 MB | | @opentelemetry/core | 1.30.1 | 908.66 kB | 7.16 MB | | protobufjs | 7.5.4 | 2.95 MB | 5.82 MB | | @datadog/wasm-js-rewriter | 4.0.1 | 2.85 MB | 3.58 MB | | @opentelemetry/resources | 1.9.1 | 306.54 kB | 1.74 MB | | @datadog/native-metrics | 3.1.1 | 1.02 MB | 1.43 MB | | @opentelemetry/api-logs | 0.207.0 | 201.39 kB | 1.42 MB | | @opentelemetry/api | 1.9.0 | 1.22 MB | 1.22 MB | | jsonpath-plus | 10.3.0 | 617.18 kB | 1.08 MB | | import-in-the-middle | 1.15.0 | 127.66 kB | 856.24 kB | | lru-cache | 10.4.3 | 804.3 kB | 804.3 kB | | @datadog/openfeature-node-server | 0.1.0-preview.12 | 95.11 kB | 401.68 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | source-map | 0.7.6 | 185.63 kB | 185.63 kB | | pprof-format | 2.2.1 | 163.06 kB | 163.06 kB | | @datadog/sketches-js | 2.1.1 | 109.9 kB | 109.9 kB | | lodash.sortby | 4.7.0 | 75.76 kB | 75.76 kB | | ignore | 7.0.5 | 63.38 kB | 63.38 kB | | istanbul-lib-coverage | 3.2.2 | 34.37 kB | 34.37 kB | | rfdc | 1.4.1 | 27.15 kB | 27.15 kB | | dc-polyfill | 0.1.10 | 26.73 kB | 26.73 kB | | @isaacs/ttlcache | 1.4.1 | 25.2 kB | 25.2 kB | | tlhunter-sorted-set | 0.1.0 | 24.94 kB | 24.94 kB | | shell-quote | 1.8.3 | 23.74 kB | 23.74 kB | | limiter | 1.1.5 | 23.17 kB | 23.17 kB | | retry | 0.13.1 | 18.85 kB | 18.85 kB | | semifies | 1.0.0 | 15.84 kB | 15.84 kB | | jest-docblock | 29.7.0 | 8.99 kB | 12.76 kB | | crypto-randomuuid | 1.0.0 | 11.18 kB | 11.18 kB | | ttl-set | 1.0.0 | 4.61 kB | 9.69 kB | | mutexify | 1.4.0 | 5.71 kB | 8.74 kB | | path-to-regexp | 0.1.12 | 6.6 kB | 6.6 kB | | module-details-from-path | 1.0.4 | 3.96 kB | 3.96 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@datadog-official

This comment has been minimized.

@codecov
Copy link

codecov bot commented Oct 21, 2025

Codecov Report

❌ Patch coverage is 96.00000% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.04%. Comparing base (90b7c96) to head (095fffd).
⚠️ Report is 14 commits behind head on master.

Files with missing lines Patch % Lines
packages/dd-trace/src/config.js 96.00% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6694      +/-   ##
==========================================
- Coverage   84.06%   84.04%   -0.02%     
==========================================
  Files         506      506              
  Lines       21212    21241      +29     
==========================================
+ Hits        17831    17852      +21     
- Misses       3381     3389       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mtoffl01 mtoffl01 marked this pull request as ready for review October 22, 2025 18:36
Copy link
Collaborator

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great work, fixing multiple issues with the sources!

I left a few comments, mostly as question.

this.#setBoolean(env, 'reportHostname', DD_TRACE_REPORT_HOSTNAME)
this.#setString(target, 'protocolVersion', DD_TRACE_AGENT_PROTOCOL_VERSION)
this.#setString(target, 'queryStringObfuscation', DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP)
this.#setBoolean(target, 'remoteConfig.enabled', DD_REMOTE_CONFIGURATION_ENABLED)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes sense as such, that we expect a user never to define this value if in a serverless environment. If they do however use a single configuration for serverless and non-serverless and they have this set to enabled, it would cause a regression, if I am not mistaken.

this.#setBoolean(target, 'startupLogs', DD_TRACE_STARTUP_LOGS)
this.#setTags(target, 'tags', tags)
target.tagsHeaderMaxLength = DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH
this.#setBoolean(target, 'telemetry.enabled', DD_INSTRUMENTATION_TELEMETRY_ENABLED)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with remote config enabled:

This change makes sense as such, that we expect a user never to define this value if in a serverless environment or when using jest. If they do however use a single configuration for serverless and non-serverless and they have this set to enabled, it would cause a regression, if I am not mistaken.

I wonder if we just want to ignore the value if we are in serverless or when using jest. That way it would indeed fall back to the default value, which we can also define as such. In addition, we could use a unprocessed object for it, so that we still know the user defined it.

It is just an interesting edge case for how to handle it correct.

this.#setBoolean(obj, 'runtimeMetrics.enabled', DD_RUNTIME_METRICS_ENABLED)
this.#setString(obj, 'service', DD_SERVICE)
this.#setString(obj, 'version', DD_VERSION)
this.#applyConfigValues(config, obj, {})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The third argument means we will fill that object but never use it, as far as I can tell. Should we potentially just not set the unprocessed values, if we do not pass through a third argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually do not understand what the "unprocessedTarget" does. I just used {} to maintain existing patterns.

I guess the question is: Does StableConfig need an "unprocessedTarget"? If not, then yes, we could:

  1. Not pass a third argument
  2. Modify the logic in applyConfigValues to only set entries on unprocessedTarget if it exists

WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unprocessed exists because we used to override some values with calculated ones. To still send out the original user defined value, we tracked both cases. That is definitely not ideal and it would be better not to change anything in the first place. That way unprocessed would not be necessary at all.
I believe your changes pretty much resolve most of this already, I just did not check if all cases.

…D defaults to happen in applyDefaults, instead of applyCalculated
if (DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING) {
this.#setBoolean(target, 'cloudPayloadTagging.responsesEnabled', true)
}
target['cloudPayloadTagging.rules'] = appendRules(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
target['cloudPayloadTagging.rules'] = appendRules(
target.cloudPayloadTagging.rules = appendRules(

And the object might have to be initialized up front as well as empty object.

)
}
if (DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH) {
target['cloudPayloadTagging.maxDepth'] = maybeInt(DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
target['cloudPayloadTagging.maxDepth'] = maybeInt(DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH)
target.cloudPayloadTagging.maxDepth = maybeInt(DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH)

if (options.cloudPayloadTagging) {
this.#setBoolean(opts, 'cloudPayloadTagging.requestsEnabled', options.cloudPayloadTagging.requestsEnabled)
this.#setBoolean(opts, 'cloudPayloadTagging.responsesEnabled', options.cloudPayloadTagging.responsesEnabled)
opts['cloudPayloadTagging.maxDepth'] = maybeInt(options.cloudPayloadTagging.maxDepth)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
opts['cloudPayloadTagging.maxDepth'] = maybeInt(options.cloudPayloadTagging.maxDepth)
opts.cloudPayloadTagging.maxDepth = maybeInt(options.cloudPayloadTagging.maxDepth)

this.#setBoolean(opts, 'cloudPayloadTagging.responsesEnabled', options.cloudPayloadTagging.responsesEnabled)
opts['cloudPayloadTagging.maxDepth'] = maybeInt(options.cloudPayloadTagging.maxDepth)
if (options.cloudPayloadTagging.request || options.cloudPayloadTagging.response) {
opts['cloudPayloadTagging.rules'] = appendRules(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
opts['cloudPayloadTagging.rules'] = appendRules(
opts.cloudPayloadTagging.rules = appendRules(

Copy link
Collaborator

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

LGTM

@BridgeAR BridgeAR merged commit af5885e into master Oct 28, 2025
762 checks passed
@BridgeAR BridgeAR deleted the mtoff/dcfg-extended-configs branch October 28, 2025 20:07
dd-octo-sts bot pushed a commit that referenced this pull request Oct 29, 2025
* Sync declarative config and env var support; add tests

* Move runtime specific evaluations to defaults (e.g. remote config, crashtracking, etc)

* Handle cloudPayloadTagging and installSignature
This was referenced Oct 29, 2025
dd-octo-sts bot pushed a commit that referenced this pull request Oct 30, 2025
* Sync declarative config and env var support; add tests

* Move runtime specific evaluations to defaults (e.g. remote config, crashtracking, etc)

* Handle cloudPayloadTagging and installSignature
juan-fernandez pushed a commit that referenced this pull request Oct 31, 2025
* Sync declarative config and env var support; add tests

* Move runtime specific evaluations to defaults (e.g. remote config, crashtracking, etc)

* Handle cloudPayloadTagging and installSignature
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants