Skip to content

Conversation

@zeitlinger
Copy link
Member

@zeitlinger zeitlinger commented Dec 12, 2025

Fixes #14192

Follow up (or replacement)
for #15339

PR Summary: ConfigProvider API for Declarative Configuration

This PR introduces a new configuration access pattern using ConfigProvider as the foundation for
future configuration management.

Key Changes

1. New ConfigProviderUtil

The new ConfigProviderUtil is used in most places where ConfigPropertiesUtil was used before,
i.e. where we have access to OpenTelemetry instance only. For example, in JdbcInstrumenterFactory.

ConfigProviderUtil.getConfigProvider returns

  • the ConfigProvider from ExtendedOpenTelemetry if declarative configuration is used
  • a bridge to system properties / environment variables otherwise
public final class JdbcInstrumenterFactory {

  public static boolean captureQueryParameters(OpenTelemetry openTelemetry) {
    return Optional.ofNullable(
            InstrumentationConfigUtil.getOrNull( // util from SDK
                ConfigProviderUtil.getConfigProvider(openTelemetry), 
                config -> config.getBoolean("capture_query_parameters/development"),
                "java",
                "jdbc"))
        .orElse(false);
  }
}

2. InstrumentationConfig supports declarative config syntax even when system properties are used

InstrumentationConfig.getConfigProvider returns

  • the ConfigProvider from ExtendedOpenTelemetry if declarative configuration is used
  • a bridge to ConfigProperties otherwise - addressing the limitation
    from PR #15339
    where customizers were not possible with the system properties bridge.

Usage example:

public class ExternalAnnotationInstrumentation implements TypeInstrumentation {
  static Set<String> configureAdditionalTraceAnnotations(InstrumentationConfig config) {
    String configString = config.getDeclarativeConfig("external-annotations").getString("include");
  }
}

But it would also be possible to use config.getConfigProvider() directly, e.g. to use the varargs style from the previous section.

API Design Evaluation

Fluent Style

config.getDeclarativeConfig("external-annotations").getString("include");

Pros:

  • Close to pure ConfigProvider usage
  • Fluent API allows easy chaining

Cons:

  • Requires empty() at each level for absence handling:
    config.getDeclarativeConfig("spring_starter", empty()).getBoolean("debug", false)
  • Java node extraction is built into InstrumentationConfig - needs future resolution

Varargs Style

Optional.ofNullable(
    InstrumentationConfigUtil.getOrNull(
        ConfigProviderUtil.getConfigProvider(openTelemetry),
        config -> config.getBoolean("span_attributes/development"),
        "java",
        "aws_sdk"))
    .orElse(false)

Pros:

  • Uses varargs for path specification
  • No empty() calls needed
  • Leverages SDK's InstrumentationConfigUtil for API feedback

Cons:

  • Less fluent
  • Verbose Optional.ofNullable wrapping required

Proposed Golden Path

Add directly to ConfigProvider in SDK:

  default DeclarativeConfigProperties get(String name) {
    DeclarativeConfigProperties config = getInstrumentationConfig();
    if (config == null) {
      config = DeclarativeConfigProperties.empty();
    }
    return config.get("java").get(name);
  }

Add directly to DeclarativeConfigProperties in SDK:

  default DeclarativeConfigProperties get(String name) {
    return getStructured(name, empty());
  }

Usage:

    List<String> value =
        ConfigProvider.noop()
            .get("http")
            .get("client")
            .getScalarList("known-methods", String.class, Collections.singletonList("GET"));
    assertThat(value).containsExactly("GET");

Alternative Proposal

Add directly to ConfigProvider in SDK:

  default <T> T get(Function<DeclarativeConfigProperties, T> accessor, String... segments) {
    DeclarativeConfigProperties config = getInstrumentationConfig();
    if (config == null) {
      config = DeclarativeConfigProperties.empty();
    }
    config = config.getStructured("java", DeclarativeConfigProperties.empty());

    for (String segment : segments) {
      config = config.getStructured(segment, DeclarativeConfigProperties.empty());
    }
    return accessor.apply(config);
  }

Usage:

    List<String> value =
        ConfigProvider.noop()
            .get(
                config ->
                    config.getScalarList(
                        "known-methods", String.class, Collections.singletonList("GET")),
                "http",
                "client");
    assertThat(value).containsExactly("GET");

Next Steps

  1. Gather feedback on API design from SDK team
  2. Pass ConfigProvider wherever InstrumentationConfig is currently used
  3. Update existing instrumentations to use the new API
  4. Deprecate InstrumentationConfig and DeclarativeConfigPropertiesBridge
  5. Eventually remove deprecated components

@zeitlinger zeitlinger self-assigned this Dec 12, 2025
@zeitlinger zeitlinger requested a review from a team as a code owner December 12, 2025 19:08
@github-actions github-actions bot added the test native This label can be applied to PRs to trigger them to run native tests label Dec 12, 2025
@zeitlinger zeitlinger moved this from In Progress to Awaiting Review in Declarative Configuration: Java Dec 12, 2025
@zeitlinger
Copy link
Member Author

@trask hopefully a better plan now - taking into account all your great feedback!

@zeitlinger zeitlinger force-pushed the system-properties-bridge-interface branch from f97a3aa to c9c7268 Compare December 12, 2025 20:45
@zeitlinger zeitlinger force-pushed the system-properties-bridge-interface branch from 93df559 to a9fd3e2 Compare December 13, 2025 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test native This label can be applied to PRs to trigger them to run native tests

Projects

Status: Awaiting Review

Development

Successfully merging this pull request may close these issues.

use InstrumentationConfig where possible

2 participants