Skip to content

[dynamic control] add initializing from declarative config#2881

Closed
jackshirazi wants to merge 8 commits into
open-telemetry:mainfrom
jackshirazi:policy-phase2-02
Closed

[dynamic control] add initializing from declarative config#2881
jackshirazi wants to merge 8 commits into
open-telemetry:mainfrom
jackshirazi:policy-phase2-02

Conversation

@jackshirazi

Copy link
Copy Markdown
Contributor

Description:

Sorry for the change set being a little larger than normal, but I thought it was better to combine all the declarative config support changes together, rather than adding them piecemeal. I think it may be easier to understand the whole than the parts

This contains

  • a DeclarativeConfigurationCustomizerProvider that reads top-level telemetry_policy/development from declarative config and routes it into PolicyInit
  • a declarative ComponentProvider for telemetry_policy/development to add the delagating sampler - initially for the rate sampling policy, but will later make more generic (includes a TODO for that)
  • Changes to PolicyInit to support the declarative config and also make it a one-shot execution so that a later/repeated/accidental auto-config pass does not accidentally re-use stale declarative config

Existing Issue(s):

part of #2868

Testing:

Unit tests added, plus a manual end-to-end test using declarative config was performed

Documentation:

TBD see #2868

Outstanding items:

see #2868

Copilot AI review requested due to automatic review settings May 27, 2026 23:00
@jackshirazi
jackshirazi requested a review from a team as a code owner May 27, 2026 23:00
@github-actions
github-actions Bot requested a review from LikeTheSalad May 27, 2026 23:01
@jackshirazi
jackshirazi requested review from jack-berg and removed request for LikeTheSalad May 27, 2026 23:01
@github-actions
github-actions Bot requested a review from LikeTheSalad May 27, 2026 23:03
@jackshirazi

Copy link
Copy Markdown
Contributor Author

@jack-berg I mentioned a while back that the documentation for this type of declarative config addition was minimal, so would love for you to review the implementation here.

@jack-berg

Copy link
Copy Markdown
Member

I'm struggling to visualize the UX for this.

From what I can tell:

  • You include this dynamic control jar on your class path.
  • If declarative config is used TelemetryPolicyDeclarativeCustomizerProvider is invoked, which looks for the presence of top level .telemetry_policy/development.
  • If not present, do nothing.
  • If present, try to initialize:
    • Parse PolicyInitConfig from the node
    • Modify the OpenTelemetryConfiguration model to set the tracer provider sampler to be telemetry_policy/development, and some other properties set (sources, resource_attributes, otel.resource.attributes - not sure what these do what its a code smell).
  • TelemetryPolicyComponentProvider sees the sampler set to telemetry_policy/development, and returns a sampler with a dynamic implementation wired up to the policy

If that's about right, then it seems convoluted. Maybe necessarily so based on the hooks available today, but if so, maybe the answer is to go and fix the hooks so this flows more cleanly.

In my head, a user should opt into dynamic control with a config file something like:

file_format: "1.0"
tracer_provider:
  sampler: ..
meter_provider: ...
logger_provider: ...
telemetry_policy/development: ...

When the .telemetry_policy/development is present, the telemetry policy machinery initializes, and ultimately gets a reference to the OpenTelemetrySdk instance returned by declarative config. From there, it can listen for changes to the policy (however those happen) and call update methods on the OpenTelemetrySdk instance.

I.e.

  • if a policy changes that results in a new sampler config, call OpenTelemetrySdk.getSdkTracerProvider().setSampler(newSampler)
  • if a policy changes that results loggers being turned off or on, call OpenTelemetrySdk.getSdkLoggerProvider().setLoggerConfigurator(newLoggerConfigurator)
  • etc

Caveat: I'm not currently doing any dynamic config tasks. Just trying to help and make sure that we're not trying to solve this problem strictly through the tools we already have. We can change / add tools if needed.

@jackshirazi

Copy link
Copy Markdown
Contributor Author

I'm struggling to visualize the UX for this.

The README has the details

some other properties set (sources, resource_attributes, otel.resource.attributes - not sure what these do what its a code smell).

Some components need specific resource items at initialization. In the current particular case an OpAMP client needs to know both the service name and the environment

If that's about right, then it seems convoluted. Maybe necessarily so based on the hooks available today, but if so, maybe the answer is to go and fix the hooks so this flows more cleanly.

There are two different considerations

  1. Getting this extension working now with the current hooks
  2. Making the hooks better to make the implementation nicer

Please keep these separate. I want this extension functioning then we can evolve it. (Note it is already fully functioning for autoconfiguration, this particular PR enables it to function in the same way for declarative config).

In my head, a user should opt into dynamic control with a config file something like:

file_format: "1.0"
tracer_provider:
  sampler: ..
meter_provider: ...
logger_provider: ...
telemetry_policy/development: ...

This is exactly how it works with this PR.

When the .telemetry_policy/development is present, the telemetry policy machinery initializes, and ultimately gets a reference to the OpenTelemetrySdk instance returned by declarative config. From there, it can listen for changes to the policy (however those happen) and call update methods on the OpenTelemetrySdk instance.

I.e.

  • if a policy changes that results in a new sampler config, call OpenTelemetrySdk.getSdkTracerProvider().setSampler(newSampler)
  • if a policy changes that results loggers being turned off or on, call OpenTelemetrySdk.getSdkLoggerProvider().setLoggerConfigurator(newLoggerConfigurator)
  • etc

These don't currently work. Sampler can't be set there, and even if you added it, it's currently final at initialization. Logging is much worse, you need to install your preferred logging framework at exactly the right point, not too early (or you hit cyclic dependencies) nor too late (it just won't install correctly), and definitely not dynamically currently

I agree this is the way it should work. We can open a separate issue on changing the SDK to make it work, but that's really extensive changes even for these two features, and there are many more to consider

What I'm hoping for here in review terms is whether the declarative config implementation is the correct way for what needs to be done here, ie

  • parse the telemetry_policy/development to initialize the policy pipeline
  • install the required sampler needed to support dynamically changing sampling rate

@jackshirazi jackshirazi self-assigned this Jun 15, 2026
@jack-berg

Copy link
Copy Markdown
Member

Please keep these separate.

👍

Sampler can't be set there,

Just because we haven't made it mutable yet. I don't think there is any fundamental limitation from making it mutable like the logger / tracer / meter configurators.

Logging is much worse,

I'm not following. Updating the otel log SDK with the set of loggers that should be enabled / disabled is an independent problem from installing / initializing your preferred log framework.

What I'm hoping for here in review terms is whether the declarative config implementation is the correct way for what needs to be done here, ie

parse the telemetry_policy/development to initialize the policy pipeline
install the required sampler needed to support dynamically changing sampling rate

I won't claim there is a correct way at this point, but it does look like you've managed to make it work despite subpar hooks.

See recent comments for thoughts. None are blocking. Just things to consider since I'm not an owner of this component.

@jackshirazi

Copy link
Copy Markdown
Contributor Author

Just because we haven't made it mutable yet. I don't think there is any fundamental limitation from making it mutable like the logger / tracer / meter configurators.

I'll open an issue in the SDK with changes suggested

Logging is much worse,

I'm not following. Updating the otel log SDK with the set of loggers that should be enabled / disabled is an independent problem from installing / initializing your preferred log framework.

In the Elastic distribution we install log4j as the preferred logging framework. When you do that you also have to initialize it, otherwise weird things happen. If you do that too early, you get a class circularity error (and in fact when too early is changes with different SDK versions because the class circularity is with MethodHandles and is painful). If you leave it too late, log4j doesn't correctly install into the SDK and you get a sort of partial logging.

@jack-berg jack-berg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems reasonable enough. I'm not a code owner and have just scanned this, so take the approval with a grain of salt

Comment thread dynamic-control/build.gradle.kts
@linux-foundation-easycla

Copy link
Copy Markdown

CLA Not Signed

@jackshirazi

Copy link
Copy Markdown
Contributor Author

moved change set to #2967 (unless someone knows how to authorize cursor as co-author)

@jackshirazi jackshirazi closed this Jul 7, 2026
jackshirazi added a commit to jackshirazi/opentelemetry-java-contrib that referenced this pull request Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants