From 7eaf6f3f8f9b5c0ec57d45e6abfba9403eee6c62 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 19 Dec 2025 17:16:46 -0800 Subject: [PATCH 1/8] Use the one configuration bridge in spring boot starter --- .../build.gradle.kts | 1 + .../OpenTelemetryAutoConfiguration.java | 8 +++-- .../autoconfigure/SpringOpenTelemetrySdk.java | 29 +++++++++++++++++++ ...SpringSchedulingInstrumentationAspect.java | 9 +++--- ...ulingInstrumentationAutoConfiguration.java | 5 ++-- .../web/RestTemplateInstrumentation.java | 2 +- ...bfluxInstrumentationAutoConfiguration.java | 10 +++---- .../webflux/WebClientBeanPostProcessor.java | 19 ++++-------- ...bMvc5InstrumentationAutoConfiguration.java | 5 ++-- .../properties/InstrumentationConfigUtil.java | 13 +++++---- .../web/RestClientBeanPostProcessor.java | 2 +- ...bMvc6InstrumentationAutoConfiguration.java | 5 ++-- .../RestClientBeanPostProcessorSpring4.java | 2 +- .../SchedulingInstrumentationAspectTest.java | 15 ++-------- .../WebClientBeanPostProcessorTest.java | 21 ++++---------- 15 files changed, 74 insertions(+), 72 deletions(-) create mode 100644 instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringOpenTelemetrySdk.java diff --git a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts index 64d16622dcdc..87cb10190a81 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts +++ b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts @@ -99,6 +99,7 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-sdk") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") + testImplementation("io.opentelemetry:opentelemetry-api-incubator") testImplementation(project(":instrumentation:resources:library")) testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators") diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index bbb40a59a2cb..af79bfbc6154 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -13,6 +13,7 @@ import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; +import io.opentelemetry.instrumentation.config.bridge.ConfigPropertiesBackedConfigProvider; import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.DeclarativeConfigDisabled; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.DeclarativeConfigEnabled; @@ -125,9 +126,12 @@ public AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk( @Bean public OpenTelemetry openTelemetry( - AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk, + ConfigProperties otelProperties) { logStart(); - return autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); + OpenTelemetrySdk openTelemetry = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); + ConfigProvider configProvider = ConfigPropertiesBackedConfigProvider.create(otelProperties); + return new SpringOpenTelemetrySdk(openTelemetry, configProvider); } @Bean diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringOpenTelemetrySdk.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringOpenTelemetrySdk.java new file mode 100644 index 000000000000..8fbd36c3c4ec --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringOpenTelemetrySdk.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure; + +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.sdk.OpenTelemetrySdk; + +final class SpringOpenTelemetrySdk extends OpenTelemetrySdk implements ExtendedOpenTelemetry { + + private final ConfigProvider configProvider; + + SpringOpenTelemetrySdk(OpenTelemetrySdk delegate, ConfigProvider configProvider) { + super( + delegate.getSdkTracerProvider(), + delegate.getSdkMeterProvider(), + delegate.getSdkLoggerProvider(), + delegate.getPropagators()); + this.configProvider = configProvider; + } + + @Override + public ConfigProvider getConfigProvider() { + return configProvider; + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java index 6d84cde77985..ba51f605a884 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java @@ -9,7 +9,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.api.incubator.config.internal.DeclarativeConfigUtil; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor; @@ -42,8 +42,7 @@ final class SpringSchedulingInstrumentationAspect { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-boot-autoconfigure"; private final Instrumenter instrumenter; - public SpringSchedulingInstrumentationAspect( - OpenTelemetry openTelemetry, InstrumentationConfig config) { + public SpringSchedulingInstrumentationAspect(OpenTelemetry openTelemetry) { CodeAttributesGetter codedAttributesGetter = ClassAndMethod.codeAttributesGetter(); InstrumenterBuilder builder = @@ -52,8 +51,8 @@ public SpringSchedulingInstrumentationAspect( INSTRUMENTATION_NAME, CodeSpanNameExtractor.create(codedAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codedAttributesGetter)); - if (config.getBoolean( - "otel.instrumentation.spring-scheduling.experimental-span-attributes", false)) { + if (DeclarativeConfigUtil.getInstrumentationConfig(openTelemetry, "spring_scheduling") + .getBoolean("experimental-span-attributes/development", false)) { builder.addAttributesExtractor( AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "spring_scheduling")); } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java index 85a54625e455..cfeae0637929 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import org.aspectj.lang.annotation.Aspect; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -26,7 +25,7 @@ class SpringSchedulingInstrumentationAutoConfiguration { @Bean SpringSchedulingInstrumentationAspect springSchedulingInstrumentationAspect( - OpenTelemetry openTelemetry, InstrumentationConfig config) { - return new SpringSchedulingInstrumentationAspect(openTelemetry, config); + OpenTelemetry openTelemetry) { + return new SpringSchedulingInstrumentationAspect(openTelemetry); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java index ca4c0c97c466..137b1e8fe4ac 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java @@ -25,7 +25,7 @@ static RestTemplate addIfNotPresent( ClientHttpRequestInterceptor instrumentationInterceptor = InstrumentationConfigUtil.configureClientBuilder( - config, + openTelemetry, SpringWebTelemetry.builder(openTelemetry), WebTelemetryUtil.getBuilderExtractor()) .build() diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java index 79bacde3ccc2..93e9a1fde417 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -33,14 +32,13 @@ public SpringWebfluxInstrumentationAutoConfiguration() {} // static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning @Bean static WebClientBeanPostProcessor otelWebClientBeanPostProcessor( - ObjectProvider openTelemetryProvider, - ObjectProvider configProvider) { - return new WebClientBeanPostProcessor(openTelemetryProvider, configProvider); + ObjectProvider openTelemetryProvider) { + return new WebClientBeanPostProcessor(openTelemetryProvider); } @Bean - WebFilter telemetryFilter(OpenTelemetry openTelemetry, InstrumentationConfig config) { - return WebClientBeanPostProcessor.getWebfluxServerTelemetry(openTelemetry, config) + WebFilter telemetryFilter(OpenTelemetry openTelemetry) { + return WebClientBeanPostProcessor.getWebfluxServerTelemetry(openTelemetry) .createWebFilterAndRegisterReactorHook(); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java index 9b501c410be3..355bf8ea2450 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxClientTelemetry; import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxServerTelemetry; @@ -23,28 +22,22 @@ final class WebClientBeanPostProcessor implements BeanPostProcessor { private final ObjectProvider openTelemetryProvider; - private final ObjectProvider configProvider; - WebClientBeanPostProcessor( - ObjectProvider openTelemetryProvider, - ObjectProvider configProvider) { + WebClientBeanPostProcessor(ObjectProvider openTelemetryProvider) { this.openTelemetryProvider = openTelemetryProvider; - this.configProvider = configProvider; } - static SpringWebfluxClientTelemetry getWebfluxClientTelemetry( - OpenTelemetry openTelemetry, InstrumentationConfig config) { + static SpringWebfluxClientTelemetry getWebfluxClientTelemetry(OpenTelemetry openTelemetry) { return InstrumentationConfigUtil.configureClientBuilder( - config, + openTelemetry, SpringWebfluxClientTelemetry.builder(openTelemetry), SpringWebfluxBuilderUtil.getClientBuilderExtractor()) .build(); } - static SpringWebfluxServerTelemetry getWebfluxServerTelemetry( - OpenTelemetry openTelemetry, InstrumentationConfig config) { + static SpringWebfluxServerTelemetry getWebfluxServerTelemetry(OpenTelemetry openTelemetry) { return InstrumentationConfigUtil.configureServerBuilder( - config, + openTelemetry, SpringWebfluxServerTelemetry.builder(openTelemetry), SpringWebfluxBuilderUtil.getServerBuilderExtractor()) .build(); @@ -64,7 +57,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { private WebClient.Builder wrapBuilder(WebClient.Builder webClientBuilder) { SpringWebfluxClientTelemetry instrumentation = - getWebfluxClientTelemetry(openTelemetryProvider.getObject(), configProvider.getObject()); + getWebfluxClientTelemetry(openTelemetryProvider.getObject()); return webClientBuilder.filters(instrumentation::addFilter); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java index 73d3f657504c..8e47a741b7f8 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webmvc.v5_3.SpringWebMvcTelemetry; @@ -28,9 +27,9 @@ public class SpringWebMvc5InstrumentationAutoConfiguration { @Bean - Filter otelWebMvcFilter(OpenTelemetry openTelemetry, InstrumentationConfig config) { + Filter otelWebMvcFilter(OpenTelemetry openTelemetry) { return InstrumentationConfigUtil.configureServerBuilder( - config, + openTelemetry, SpringWebMvcTelemetry.builder(openTelemetry), SpringMvcBuilderUtil.getBuilderExtractor()) .build() diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java index 683529a18b29..8019fcb21649 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; @@ -21,24 +22,24 @@ private InstrumentationConfigUtil() {} @CanIgnoreReturnValue public static T configureClientBuilder( - InstrumentationConfig config, + OpenTelemetry openTelemetry, T builder, Function> getBuilder) { - getBuilder.apply(builder).configure(getConfig(config)); + getBuilder.apply(builder).configure(getConfig(openTelemetry)); return builder; } @CanIgnoreReturnValue public static T configureServerBuilder( - InstrumentationConfig config, + OpenTelemetry openTelemetry, T builder, Function> getBuilder) { - getBuilder.apply(builder).configure(getConfig(config)); + getBuilder.apply(builder).configure(getConfig(openTelemetry)); return builder; } - private static CommonConfig getConfig(InstrumentationConfig config) { - return new CommonConfig(config); + private static CommonConfig getConfig(OpenTelemetry openTelemetry) { + return new CommonConfig(openTelemetry); } public static boolean isStatementSanitizationEnabled(InstrumentationConfig config, String key) { diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java index 9ef5b7f19194..118354dc04c4 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java @@ -57,7 +57,7 @@ private static RestClient addRestClientInterceptorIfNotPresent( static ClientHttpRequestInterceptor getInterceptor( OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureClientBuilder( - config, + openTelemetry, SpringWebTelemetry.builder(openTelemetry), WebTelemetryUtil.getBuilderExtractor()) .build() diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java index b8653d4d3936..84e553dfeb6b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webmvc.v6_0.SpringWebMvcTelemetry; @@ -28,9 +27,9 @@ public class SpringWebMvc6InstrumentationAutoConfiguration { @Bean - Filter otelWebMvcFilter(OpenTelemetry openTelemetry, InstrumentationConfig config) { + Filter otelWebMvcFilter(OpenTelemetry openTelemetry) { return InstrumentationConfigUtil.configureServerBuilder( - config, + openTelemetry, SpringWebMvcTelemetry.builder(openTelemetry), SpringMvcBuilderUtil.getBuilderExtractor()) .build() diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring4/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessorSpring4.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring4/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessorSpring4.java index 03aef6f6d7a6..409c0b1bf9d5 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring4/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessorSpring4.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring4/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessorSpring4.java @@ -67,7 +67,7 @@ private static boolean isInterceptorNotPresent( static ClientHttpRequestInterceptor getInterceptor( OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureClientBuilder( - config, + openTelemetry, SpringWebTelemetry.builder(openTelemetry), WebTelemetryUtil.getBuilderExtractor()) .build() diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java index 3306b280869c..6e66cfa96d7c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java @@ -13,14 +13,10 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; -import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil; -import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.StatusData; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -39,9 +35,8 @@ class SchedulingInstrumentationAspectTest { private String unproxiedTesterSimpleClassName; private String unproxiedTesterClassName; - SpringSchedulingInstrumentationAspect newAspect( - OpenTelemetry openTelemetry, InstrumentationConfig config) { - return new SpringSchedulingInstrumentationAspect(openTelemetry, config); + SpringSchedulingInstrumentationAspect newAspect(OpenTelemetry openTelemetry) { + return new SpringSchedulingInstrumentationAspect(openTelemetry); } @BeforeEach @@ -54,11 +49,7 @@ void setup() { AspectJProxyFactory factory = new AspectJProxyFactory(); factory.setTarget(unproxiedTester); - SpringSchedulingInstrumentationAspect aspect = - newAspect( - testing.getOpenTelemetry(), - new ConfigPropertiesBridge( - DefaultConfigProperties.createFromMap(Collections.emptyMap()))); + SpringSchedulingInstrumentationAspect aspect = newAspect(testing.getOpenTelemetry()); factory.addAspect(aspect); schedulingTester = factory.getProxy(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java index 4c25da12a112..5911ef85c815 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; @@ -34,9 +33,7 @@ class WebClientBeanPostProcessorTest { "when processed bean is NOT of type WebClient or WebClientBuilder should return Object") void returnsObject() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor( - beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(InstrumentationConfig.class)); + new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); assertThat(underTest.postProcessAfterInitialization(new Object(), "testObject")) .isExactlyInstanceOf(Object.class); @@ -46,9 +43,7 @@ void returnsObject() { @DisplayName("when processed bean is of type WebClient should return WebClient") void returnsWebClient() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor( - beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(InstrumentationConfig.class)); + new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); assertThat(underTest.postProcessAfterInitialization(WebClient.create(), "testWebClient")) .isInstanceOf(WebClient.class); @@ -58,9 +53,7 @@ void returnsWebClient() { @DisplayName("when processed bean is of type WebClientBuilder should return WebClientBuilder") void returnsWebClientBuilder() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor( - beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(InstrumentationConfig.class)); + new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); assertThat( underTest.postProcessAfterInitialization(WebClient.builder(), "testWebClientBuilder")) @@ -71,9 +64,7 @@ void returnsWebClientBuilder() { @DisplayName("when processed bean is of type WebClient should add exchange filter to WebClient") void addsExchangeFilterWebClient() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor( - beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(InstrumentationConfig.class)); + new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); WebClient webClient = WebClient.create(); Object processedWebClient = @@ -96,9 +87,7 @@ void addsExchangeFilterWebClient() { "when processed bean is of type WebClientBuilder should add ONE exchange filter to WebClientBuilder") void addsExchangeFilterWebClientBuilder() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor( - beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(InstrumentationConfig.class)); + new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); WebClient.Builder webClientBuilder = WebClient.builder(); underTest.postProcessAfterInitialization(webClientBuilder, "testWebClientBuilder"); From 6bc1c0f770c2826987004bbbb8af095c28421ecb Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 22 Dec 2025 11:19:09 -0800 Subject: [PATCH 2/8] fix --- .../src/testDeclarativeConfig/resources/application.yaml | 7 ++++--- .../spring-boot-3.2/src/main/resources/application.yaml | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml index 6b24c0ee67fa..3d6312aa0f4a 100644 --- a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml @@ -40,8 +40,9 @@ otel: instrumentation/development: java: + common: + http: + client: + emit_experimental_telemetry: true runtime-telemetry: emit_experimental_telemetry: true - http: - client: - emit_experimental_telemetry: true diff --git a/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml index 0533d63c9b88..be3ba1dedd1b 100644 --- a/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml @@ -40,8 +40,9 @@ otel: instrumentation/development: java: + common: + http: + client: + emit_experimental_telemetry: true runtime-telemetry: emit_experimental_telemetry: true - http: - client: - emit_experimental_telemetry: true From 277d5e87928735f5027049d04d4da6702a790a03 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 22 Dec 2025 16:08:23 -0800 Subject: [PATCH 3/8] remove --- .../config/internal/CommonConfig.java | 89 ------------------- .../config/internal/EnduserConfig.java | 19 ---- .../resources/application.yaml | 6 +- .../src/main/resources/application.yaml | 6 +- 4 files changed, 6 insertions(+), 114 deletions(-) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java index 4c381df9ea69..b37ab59c6730 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java @@ -5,12 +5,7 @@ package io.opentelemetry.instrumentation.api.incubator.config.internal; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; - import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.incubator.config.ConfigProvider; -import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.api.incubator.log.LoggingContextConstants; import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceResolver; import io.opentelemetry.instrumentation.api.internal.HttpConstants; @@ -18,8 +13,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Supplier; -import javax.annotation.Nullable; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -43,74 +36,6 @@ public final class CommonConfig { private final String loggingSpanIdKey; private final String loggingTraceFlagsKey; - interface ValueProvider { - @Nullable - T get(ConfigProvider configProvider); - } - - public CommonConfig(InstrumentationConfig config) { - peerServiceResolver = - PeerServiceResolver.create( - getFromConfigProviderOrFallback( - config, - InstrumentationConfigUtil::peerServiceMapping, - emptyMap(), - () -> - config.getMap("otel.instrumentation.common.peer-service-mapping", emptyMap()))); - - clientRequestHeaders = - getFromConfigProviderOrFallback( - config, - InstrumentationConfigUtil::httpClientRequestCapturedHeaders, - emptyList(), - () -> config.getList("otel.instrumentation.http.client.capture-request-headers")); - clientResponseHeaders = - getFromConfigProviderOrFallback( - config, - InstrumentationConfigUtil::httpClientResponseCapturedHeaders, - emptyList(), - () -> config.getList("otel.instrumentation.http.client.capture-response-headers")); - serverRequestHeaders = - getFromConfigProviderOrFallback( - config, - InstrumentationConfigUtil::httpServerRequestCapturedHeaders, - emptyList(), - () -> config.getList("otel.instrumentation.http.server.capture-request-headers")); - serverResponseHeaders = - getFromConfigProviderOrFallback( - config, - InstrumentationConfigUtil::httpServerResponseCapturedHeaders, - emptyList(), - () -> config.getList("otel.instrumentation.http.server.capture-response-headers")); - knownHttpRequestMethods = - new HashSet<>( - config.getList( - "otel.instrumentation.http.known-methods", - new ArrayList<>(HttpConstants.KNOWN_METHODS))); - statementSanitizationEnabled = - config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true); - sqlCommenterEnabled = - config.getBoolean( - "otel.instrumentation.common.experimental.db-sqlcommenter.enabled", false); - emitExperimentalHttpClientTelemetry = - config.getBoolean("otel.instrumentation.http.client.emit-experimental-telemetry", false); - redactQueryParameters = - config.getBoolean( - "otel.instrumentation.http.client.experimental.redact-query-parameters", true); - emitExperimentalHttpServerTelemetry = - config.getBoolean("otel.instrumentation.http.server.emit-experimental-telemetry", false); - enduserConfig = new EnduserConfig(config); - loggingTraceIdKey = - config.getString( - "otel.instrumentation.common.logging.trace-id", LoggingContextConstants.TRACE_ID); - loggingSpanIdKey = - config.getString( - "otel.instrumentation.common.logging.span-id", LoggingContextConstants.SPAN_ID); - loggingTraceFlagsKey = - config.getString( - "otel.instrumentation.common.logging.trace-flags", LoggingContextConstants.TRACE_FLAGS); - } - public CommonConfig(OpenTelemetry openTelemetry) { ExtendedDeclarativeConfigProperties generalConfig = DeclarativeConfigUtil.getGeneralInstrumentationConfig(openTelemetry); @@ -231,18 +156,4 @@ public String getSpanIdKey() { public String getTraceFlagsKey() { return loggingTraceFlagsKey; } - - private static T getFromConfigProviderOrFallback( - InstrumentationConfig config, - ValueProvider getFromConfigProvider, - T defaultValue, - Supplier fallback) { - ConfigProvider configProvider = config.getConfigProvider(); - if (configProvider != null) { - T value = getFromConfigProvider.get(configProvider); - return value != null ? value : defaultValue; - } - // fallback doesn't return null, so we can safely call it - return fallback.get(); - } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java index ce956d598e95..6ecccbe8f742 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java @@ -39,25 +39,6 @@ public class EnduserConfig { private final boolean roleEnabled; private final boolean scopeEnabled; - EnduserConfig(InstrumentationConfig instrumentationConfig) { - Objects.requireNonNull(instrumentationConfig, "instrumentationConfig must not be null"); - - /* - * Capturing enduser.* attributes is disabled by default, because of this requirement in the specification: - * - * Given the sensitive nature of this information, SDKs and exporters SHOULD drop these attributes by default and then provide a configuration parameter to turn on retention for use cases where the information is required and would not violate any policies or regulations. - * - * https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-identity-attributes - */ - this.idEnabled = - instrumentationConfig.getBoolean("otel.instrumentation.common.enduser.id.enabled", false); - this.roleEnabled = - instrumentationConfig.getBoolean("otel.instrumentation.common.enduser.role.enabled", false); - this.scopeEnabled = - instrumentationConfig.getBoolean( - "otel.instrumentation.common.enduser.scope.enabled", false); - } - EnduserConfig(ExtendedDeclarativeConfigProperties commonConfig) { Objects.requireNonNull(commonConfig, "commonConfig must not be null"); diff --git a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml index 3d6312aa0f4a..4c3abc3e2354 100644 --- a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml @@ -43,6 +43,6 @@ otel: common: http: client: - emit_experimental_telemetry: true - runtime-telemetry: - emit_experimental_telemetry: true + emit_experimental_telemetry/development: true + runtime_telemetry: + emit_experimental_telemetry/development: true diff --git a/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml index be3ba1dedd1b..48687a310de1 100644 --- a/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-3.2/src/main/resources/application.yaml @@ -43,6 +43,6 @@ otel: common: http: client: - emit_experimental_telemetry: true - runtime-telemetry: - emit_experimental_telemetry: true + emit_experimental_telemetry/development: true + runtime_telemetry: + emit_experimental_telemetry/development: true From 91374324bf336a2645a9ffc7c3fd8524bbb074d4 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 22 Dec 2025 16:21:53 -0800 Subject: [PATCH 4/8] Fix types --- .../autoconfigure/EmbeddedConfigFile.java | 21 ++++++++++++------- .../autoconfigure/EmbeddedConfigFileTest.java | 16 +++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java index b2222baf489f..7da9cee2408a 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java @@ -46,20 +46,24 @@ class EmbeddedConfigFile { private EmbeddedConfigFile() {} static OpenTelemetryConfigurationModel extractModel(ConfigurableEnvironment environment) { - Map props = extractSpringProperties(environment); + Map props = extractSpringProperties(environment); return convertToOpenTelemetryConfigurationModel(props); } - private static Map extractSpringProperties(ConfigurableEnvironment environment) { + private static Map extractSpringProperties(ConfigurableEnvironment environment) { MutablePropertySources propertySources = environment.getPropertySources(); - Map props = new HashMap<>(); + Map props = new HashMap<>(); for (PropertySource propertySource : propertySources) { if (propertySource instanceof EnumerablePropertySource) { for (String propertyName : ((EnumerablePropertySource) propertySource).getPropertyNames()) { if (propertyName.startsWith("otel.")) { - String property = environment.getProperty(propertyName); + Object property = propertySource.getProperty(propertyName); + // Resolve ${} placeholders in String values while preserving types for others + if (property instanceof String) { + property = environment.resolvePlaceholders((String) property); + } if (Objects.equals(property, "")) { property = null; // spring returns empty string for yaml null } @@ -75,6 +79,7 @@ private static Map extractSpringProperties(ConfigurableEnvironme .replace("]", "") .replace(".", "_") .toUpperCase(Locale.ROOT); + // Use environment.getProperty() to search all property sources for the env var String envVarValue = environment.getProperty(envVarName); if (envVarValue != null) { property = envVarValue; @@ -96,7 +101,7 @@ private static Map extractSpringProperties(ConfigurableEnvironme } static OpenTelemetryConfigurationModel convertToOpenTelemetryConfigurationModel( - Map flatProps) { + Map flatProps) { Map nested = convertFlatPropsToNested(flatProps); return getObjectMapper().convertValue(nested, OpenTelemetryConfigurationModel.class); @@ -112,12 +117,12 @@ static ObjectMapper getObjectMapper() { * ["one", "two"]}}}} */ @SuppressWarnings("unchecked") - static Map convertFlatPropsToNested(Map flatProps) { + static Map convertFlatPropsToNested(Map flatProps) { Map result = new HashMap<>(); - for (Map.Entry entry : flatProps.entrySet()) { + for (Map.Entry entry : flatProps.entrySet()) { String key = entry.getKey(); - String value = entry.getValue(); + Object value = entry.getValue(); // Split the key by dots String[] parts = key.split("\\."); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFileTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFileTest.java index 2396a1147ad3..201fda267d22 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFileTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFileTest.java @@ -16,7 +16,7 @@ class EmbeddedConfigFileTest { @Test void convertFlatPropsToNested_simpleProperties() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("resource.service.name", "my-service"); flatProps.put("traces.exporter", "otlp"); @@ -55,7 +55,7 @@ void convertFlatPropsToNested_simpleProperties() { @Test void convertFlatPropsToNested_arrayProperties() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("instrumentation.java.list[0]", "one"); flatProps.put("instrumentation.java.list[1]", "two"); flatProps.put("instrumentation.java.list[2]", "three"); @@ -89,7 +89,7 @@ void convertFlatPropsToNested_arrayProperties() { @Test void convertFlatPropsToNested_mixedPropertiesAndArrays() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("resource.service.name", "test-service"); flatProps.put("resource.attributes[0]", "key1=value1"); flatProps.put("resource.attributes[1]", "key2=value2"); @@ -124,7 +124,7 @@ void convertFlatPropsToNested_mixedPropertiesAndArrays() { @Test void convertFlatPropsToNested_emptyMap() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); Map result = EmbeddedConfigFile.convertFlatPropsToNested(flatProps); @@ -133,7 +133,7 @@ void convertFlatPropsToNested_emptyMap() { @Test void convertFlatPropsToNested_singleLevelProperty() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("enabled", "true"); Map result = EmbeddedConfigFile.convertFlatPropsToNested(flatProps); @@ -143,7 +143,7 @@ void convertFlatPropsToNested_singleLevelProperty() { @Test void convertFlatPropsToNested_arrayWithGaps() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("list[0]", "first"); flatProps.put("list[2]", "third"); @@ -161,7 +161,7 @@ void convertFlatPropsToNested_arrayWithGaps() { @Test void convertFlatPropsToNested_deeplyNestedProperties() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("a.b.c.d.e", "deep-value"); Map result = EmbeddedConfigFile.convertFlatPropsToNested(flatProps); @@ -183,7 +183,7 @@ void convertFlatPropsToNested_deeplyNestedProperties() { @Test void convertFlatPropsToNested_nestedArrays() { - Map flatProps = new HashMap<>(); + Map flatProps = new HashMap<>(); flatProps.put("outer[0].inner[0]", "value1"); flatProps.put("outer[0].inner[1]", "value2"); flatProps.put("outer[1].inner[0]", "value3"); From 3af7e858046bcb82fc3caee9b32e0a64e8695e89 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 22 Dec 2025 17:26:51 -0800 Subject: [PATCH 5/8] revert --- .../config/internal/CommonConfig.java | 89 +++++++++++++++++++ .../config/internal/EnduserConfig.java | 19 ++++ 2 files changed, 108 insertions(+) diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java index b37ab59c6730..4c381df9ea69 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java @@ -5,7 +5,12 @@ package io.opentelemetry.instrumentation.api.incubator.config.internal; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; + import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.api.incubator.log.LoggingContextConstants; import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceResolver; import io.opentelemetry.instrumentation.api.internal.HttpConstants; @@ -13,6 +18,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Supplier; +import javax.annotation.Nullable; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -36,6 +43,74 @@ public final class CommonConfig { private final String loggingSpanIdKey; private final String loggingTraceFlagsKey; + interface ValueProvider { + @Nullable + T get(ConfigProvider configProvider); + } + + public CommonConfig(InstrumentationConfig config) { + peerServiceResolver = + PeerServiceResolver.create( + getFromConfigProviderOrFallback( + config, + InstrumentationConfigUtil::peerServiceMapping, + emptyMap(), + () -> + config.getMap("otel.instrumentation.common.peer-service-mapping", emptyMap()))); + + clientRequestHeaders = + getFromConfigProviderOrFallback( + config, + InstrumentationConfigUtil::httpClientRequestCapturedHeaders, + emptyList(), + () -> config.getList("otel.instrumentation.http.client.capture-request-headers")); + clientResponseHeaders = + getFromConfigProviderOrFallback( + config, + InstrumentationConfigUtil::httpClientResponseCapturedHeaders, + emptyList(), + () -> config.getList("otel.instrumentation.http.client.capture-response-headers")); + serverRequestHeaders = + getFromConfigProviderOrFallback( + config, + InstrumentationConfigUtil::httpServerRequestCapturedHeaders, + emptyList(), + () -> config.getList("otel.instrumentation.http.server.capture-request-headers")); + serverResponseHeaders = + getFromConfigProviderOrFallback( + config, + InstrumentationConfigUtil::httpServerResponseCapturedHeaders, + emptyList(), + () -> config.getList("otel.instrumentation.http.server.capture-response-headers")); + knownHttpRequestMethods = + new HashSet<>( + config.getList( + "otel.instrumentation.http.known-methods", + new ArrayList<>(HttpConstants.KNOWN_METHODS))); + statementSanitizationEnabled = + config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true); + sqlCommenterEnabled = + config.getBoolean( + "otel.instrumentation.common.experimental.db-sqlcommenter.enabled", false); + emitExperimentalHttpClientTelemetry = + config.getBoolean("otel.instrumentation.http.client.emit-experimental-telemetry", false); + redactQueryParameters = + config.getBoolean( + "otel.instrumentation.http.client.experimental.redact-query-parameters", true); + emitExperimentalHttpServerTelemetry = + config.getBoolean("otel.instrumentation.http.server.emit-experimental-telemetry", false); + enduserConfig = new EnduserConfig(config); + loggingTraceIdKey = + config.getString( + "otel.instrumentation.common.logging.trace-id", LoggingContextConstants.TRACE_ID); + loggingSpanIdKey = + config.getString( + "otel.instrumentation.common.logging.span-id", LoggingContextConstants.SPAN_ID); + loggingTraceFlagsKey = + config.getString( + "otel.instrumentation.common.logging.trace-flags", LoggingContextConstants.TRACE_FLAGS); + } + public CommonConfig(OpenTelemetry openTelemetry) { ExtendedDeclarativeConfigProperties generalConfig = DeclarativeConfigUtil.getGeneralInstrumentationConfig(openTelemetry); @@ -156,4 +231,18 @@ public String getSpanIdKey() { public String getTraceFlagsKey() { return loggingTraceFlagsKey; } + + private static T getFromConfigProviderOrFallback( + InstrumentationConfig config, + ValueProvider getFromConfigProvider, + T defaultValue, + Supplier fallback) { + ConfigProvider configProvider = config.getConfigProvider(); + if (configProvider != null) { + T value = getFromConfigProvider.get(configProvider); + return value != null ? value : defaultValue; + } + // fallback doesn't return null, so we can safely call it + return fallback.get(); + } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java index 6ecccbe8f742..ce956d598e95 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/EnduserConfig.java @@ -39,6 +39,25 @@ public class EnduserConfig { private final boolean roleEnabled; private final boolean scopeEnabled; + EnduserConfig(InstrumentationConfig instrumentationConfig) { + Objects.requireNonNull(instrumentationConfig, "instrumentationConfig must not be null"); + + /* + * Capturing enduser.* attributes is disabled by default, because of this requirement in the specification: + * + * Given the sensitive nature of this information, SDKs and exporters SHOULD drop these attributes by default and then provide a configuration parameter to turn on retention for use cases where the information is required and would not violate any policies or regulations. + * + * https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-identity-attributes + */ + this.idEnabled = + instrumentationConfig.getBoolean("otel.instrumentation.common.enduser.id.enabled", false); + this.roleEnabled = + instrumentationConfig.getBoolean("otel.instrumentation.common.enduser.role.enabled", false); + this.scopeEnabled = + instrumentationConfig.getBoolean( + "otel.instrumentation.common.enduser.scope.enabled", false); + } + EnduserConfig(ExtendedDeclarativeConfigProperties commonConfig) { Objects.requireNonNull(commonConfig, "commonConfig must not be null"); From 3b81610ce54f53b9ed8b5cac43498df14721ce20 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Mon, 22 Dec 2025 17:28:41 -0800 Subject: [PATCH 6/8] revert --- .../instrumentation/spring/autoconfigure/EmbeddedConfigFile.java | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java index 7da9cee2408a..5fce4844f7af 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java @@ -79,7 +79,6 @@ private static Map extractSpringProperties(ConfigurableEnvironme .replace("]", "") .replace(".", "_") .toUpperCase(Locale.ROOT); - // Use environment.getProperty() to search all property sources for the env var String envVarValue = environment.getProperty(envVarName); if (envVarValue != null) { property = envVarValue; From d3d35f62d958263112bafd6631f2e77ecf61bf62 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 26 Dec 2025 08:51:32 -0800 Subject: [PATCH 7/8] simplify --- .../autoconfigure/OpenTelemetryAutoConfiguration.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index af79bfbc6154..97bf29714202 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -8,6 +8,7 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.common.ComponentLoader; @@ -175,9 +176,11 @@ public OpenTelemetry openTelemetry( return sdk; } + // TODO remove once ConfigProperties usage is removed from remaining + // spring boot starter instrumentations @Bean - public ConfigProvider configProvider(OpenTelemetryConfigurationModel model) { - return SpringConfigProvider.create(model); + public ConfigProvider configProvider(OpenTelemetry openTelemetry) { + return ((ExtendedOpenTelemetry) openTelemetry).getConfigProvider(); } /** @@ -186,12 +189,16 @@ public ConfigProvider configProvider(OpenTelemetryConfigurationModel model) { *

Not using spring boot properties directly, because declarative configuration does not * integrate with spring boot properties. */ + // TODO remove once ConfigProperties usage is removed from remaining + // spring boot starter instrumentations @Bean public ConfigProperties otelProperties(ConfigProvider configProvider) { return new DeclarativeConfigPropertiesBridgeBuilder() .buildFromInstrumentationConfig(configProvider.getInstrumentationConfig()); } + // TODO remove once ConfigProperties usage is removed from remaining + // spring boot starter instrumentations @Bean public InstrumentationConfig instrumentationConfig( ConfigProperties properties, ConfigProvider configProvider) { From b24558289c9975b3906e394322890279c1c6104e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 26 Dec 2025 13:22:12 -0800 Subject: [PATCH 8/8] remove unused --- .../autoconfigure/EmbeddedConfigFile.java | 14 +- .../autoconfigure/SpringConfigProvider.java | 68 ---- .../SpringDeclarativeConfigProperties.java | 319 ------------------ 3 files changed, 2 insertions(+), 399 deletions(-) delete mode 100644 instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringConfigProvider.java delete mode 100644 instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringDeclarativeConfigProperties.java diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java index 5fce4844f7af..46409dc52149 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java @@ -47,7 +47,8 @@ private EmbeddedConfigFile() {} static OpenTelemetryConfigurationModel extractModel(ConfigurableEnvironment environment) { Map props = extractSpringProperties(environment); - return convertToOpenTelemetryConfigurationModel(props); + Map nested = convertFlatPropsToNested(props); + return MAPPER.convertValue(nested, OpenTelemetryConfigurationModel.class); } private static Map extractSpringProperties(ConfigurableEnvironment environment) { @@ -99,17 +100,6 @@ private static Map extractSpringProperties(ConfigurableEnvironme return props; } - static OpenTelemetryConfigurationModel convertToOpenTelemetryConfigurationModel( - Map flatProps) { - Map nested = convertFlatPropsToNested(flatProps); - - return getObjectMapper().convertValue(nested, OpenTelemetryConfigurationModel.class); - } - - static ObjectMapper getObjectMapper() { - return MAPPER; - } - /** * Convert flat property map to nested structure. e.g. "otel.instrumentation.java.list[0]" = "one" * and "otel.instrumentation.java.list[1]" = "two" becomes: {otel: {instrumentation: {java: {list: diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringConfigProvider.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringConfigProvider.java deleted file mode 100644 index f18378409814..000000000000 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringConfigProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.autoconfigure; - -import com.fasterxml.jackson.core.type.TypeReference; -import io.opentelemetry.api.incubator.config.ConfigProvider; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.common.ComponentLoader; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import java.util.Collections; -import java.util.Map; -import javax.annotation.Nullable; - -/** - * Spring flavor of {@link io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider} - * that tries to coerce types, because spring doesn't tell what the original type was. - * - *

The entire class is a copy of SdkConfigProvider - * which uses {@link SpringDeclarativeConfigProperties} instead of {@link - * io.opentelemetry.sdk.extension.incubator.fileconfig.YamlDeclarativeConfigProperties}. - */ -final class SpringConfigProvider implements ConfigProvider { - - @Nullable private final DeclarativeConfigProperties instrumentationConfig; - - private SpringConfigProvider( - OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) { - DeclarativeConfigProperties configProperties = toConfigProperties(model, componentLoader); - this.instrumentationConfig = configProperties.getStructured("instrumentation/development"); - } - - private static DeclarativeConfigProperties toConfigProperties( - Object model, ComponentLoader componentLoader) { - Map configurationMap = - EmbeddedConfigFile.getObjectMapper() - .convertValue(model, new TypeReference>() {}); - if (configurationMap == null) { - configurationMap = Collections.emptyMap(); - } - return SpringDeclarativeConfigProperties.create(configurationMap, componentLoader); - } - - /** - * Create a {@link SpringConfigProvider} from the {@code model}. - * - * @param model the configuration model - * @return the {@link SpringConfigProvider} - */ - static SpringConfigProvider create(OpenTelemetryConfigurationModel model) { - return new SpringConfigProvider( - model, ComponentLoader.forClassLoader(SpringConfigProvider.class.getClassLoader())); - } - - @Nullable - @Override - public DeclarativeConfigProperties getInstrumentationConfig() { - return instrumentationConfig; - } - - @Override - public String toString() { - return "SpringConfigProvider{" + "instrumentationConfig=" + instrumentationConfig + '}'; - } -} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringDeclarativeConfigProperties.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringDeclarativeConfigProperties.java deleted file mode 100644 index 26b2e462c99f..000000000000 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/SpringDeclarativeConfigProperties.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.autoconfigure; - -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; - -import io.opentelemetry.api.incubator.config.DeclarativeConfigException; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.common.ComponentLoader; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; -import javax.annotation.Nullable; - -/** - * Spring flavor of {@link - * io.opentelemetry.sdk.extension.incubator.fileconfig.YamlDeclarativeConfigProperties}, that tries - * to coerce types, because spring doesn't tell what the original type was. - * - *

The entire class is a copy of YamlDeclarativeConfigProperties - * with only minor modifications to type coercion logic. - */ -final class SpringDeclarativeConfigProperties implements DeclarativeConfigProperties { - - private static final Set> SUPPORTED_SCALAR_TYPES = - Collections.unmodifiableSet( - new LinkedHashSet<>( - Arrays.asList(String.class, Boolean.class, Long.class, Double.class))); - - /** Values are {@link #isPrimitive(Object)}, {@link List} of scalars. */ - private final Map simpleEntries; - - private final Map> listEntries; - private final Map mapEntries; - private final ComponentLoader componentLoader; - - private SpringDeclarativeConfigProperties( - Map simpleEntries, - Map> listEntries, - Map mapEntries, - ComponentLoader componentLoader) { - this.simpleEntries = simpleEntries; - this.listEntries = listEntries; - this.mapEntries = mapEntries; - this.componentLoader = componentLoader; - } - - /** - * Create a {@link SpringDeclarativeConfigProperties} from the {@code properties} map. - * - *

{@code properties} is expected to be the output of YAML parsing (i.e. with Jackson {@code - * com.fasterxml.jackson.databind.ObjectMapper}), and have values which are scalars, lists of - * scalars, lists of maps, and maps. - * - * @see DeclarativeConfiguration#toConfigProperties(Object) - */ - @SuppressWarnings("unchecked") - public static SpringDeclarativeConfigProperties create( - Map properties, ComponentLoader componentLoader) { - Map simpleEntries = new LinkedHashMap<>(); - Map> listEntries = new LinkedHashMap<>(); - Map mapEntries = new LinkedHashMap<>(); - for (Map.Entry entry : properties.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - if (isPrimitive(value) || value == null) { - simpleEntries.put(key, value); - continue; - } - if (isPrimitiveList(value)) { - simpleEntries.put(key, value); - continue; - } - if (isListOfMaps(value)) { - List list = - ((List>) value) - .stream() - .map(map -> SpringDeclarativeConfigProperties.create(map, componentLoader)) - .collect(toList()); - listEntries.put(key, list); - continue; - } - if (isMap(value)) { - SpringDeclarativeConfigProperties configProperties = - SpringDeclarativeConfigProperties.create((Map) value, componentLoader); - mapEntries.put(key, configProperties); - continue; - } - throw new DeclarativeConfigException( - "Unable to initialize ExtendedConfigProperties. Key \"" - + key - + "\" has unrecognized object type " - + value.getClass().getName()); - } - return new SpringDeclarativeConfigProperties( - simpleEntries, listEntries, mapEntries, componentLoader); - } - - private static boolean isPrimitiveList(Object object) { - if (object instanceof List) { - List list = (List) object; - return list.stream().allMatch(SpringDeclarativeConfigProperties::isPrimitive); - } - return false; - } - - private static boolean isPrimitive(Object object) { - return object instanceof String - || object instanceof Integer - || object instanceof Long - || object instanceof Float - || object instanceof Double - || object instanceof Boolean; - } - - private static boolean isListOfMaps(Object object) { - if (object instanceof List) { - List list = (List) object; - return list.stream() - .allMatch( - entry -> - entry instanceof Map - && ((Map) entry) - .keySet().stream().allMatch(key -> key instanceof String)); - } - return false; - } - - private static boolean isMap(Object object) { - if (object instanceof Map) { - Map map = (Map) object; - return map.keySet().stream().allMatch(entry -> entry instanceof String); - } - return false; - } - - @Nullable - @Override - public String getString(String name) { - return stringOrNull(simpleEntries.get(name)); - } - - @Nullable - @Override - public Boolean getBoolean(String name) { - return booleanOrNull(simpleEntries.get(name)); - } - - @Nullable - @Override - public Integer getInt(String name) { - Object value = simpleEntries.get(name); - if (value == null) { - return null; - } - if (value instanceof Integer) { - return (Integer) value; - } - if (value instanceof Long) { - return ((Long) value).intValue(); - } - return Integer.parseInt(value.toString()); - } - - @Nullable - @Override - public Long getLong(String name) { - return longOrNull(simpleEntries.get(name)); - } - - @Nullable - @Override - public Double getDouble(String name) { - return doubleOrNull(simpleEntries.get(name)); - } - - @Nullable - @Override - public List getScalarList(String name, Class scalarType) { - if (!SUPPORTED_SCALAR_TYPES.contains(scalarType)) { - throw new DeclarativeConfigException( - "Unsupported scalar type " - + scalarType.getName() - + ". Supported types include " - + SUPPORTED_SCALAR_TYPES.stream() - .map(Class::getName) - .collect(joining(",", "[", "]"))); - } - Object value = simpleEntries.get(name); - if (value instanceof List) { - List objectList = ((List) value); - if (objectList.isEmpty()) { - return Collections.emptyList(); - } - List result = - objectList.stream() - .map( - entry -> { - if (scalarType == String.class) { - return stringOrNull(entry); - } else if (scalarType == Boolean.class) { - return booleanOrNull(entry); - } else if (scalarType == Long.class) { - return longOrNull(entry); - } else if (scalarType == Double.class) { - return doubleOrNull(entry); - } - return null; - }) - .filter(Objects::nonNull) - .map(scalarType::cast) - .collect(toList()); - if (result.isEmpty()) { - return null; - } - return result; - } - return null; - } - - @Nullable - private static String stringOrNull(@Nullable Object value) { - if (value == null) { - return null; - } - return value.toString(); - } - - @Nullable - private static Boolean booleanOrNull(@Nullable Object value) { - if (value == null) { - return null; - } - if (value instanceof Boolean) { - return (Boolean) value; - } - return Boolean.parseBoolean(value.toString()); - } - - @Nullable - private static Long longOrNull(@Nullable Object value) { - if (value == null) { - return null; - } - if (value instanceof Integer) { - return ((Integer) value).longValue(); - } - if (value instanceof Long) { - return (Long) value; - } - return Long.parseLong(value.toString()); - } - - @Nullable - private static Double doubleOrNull(@Nullable Object value) { - if (value == null) { - return null; - } - if (value instanceof Float) { - return ((Float) value).doubleValue(); - } - if (value instanceof Double) { - return (Double) value; - } - return Double.parseDouble(value.toString()); - } - - @Nullable - @Override - public DeclarativeConfigProperties getStructured(String name) { - return mapEntries.get(name); - } - - @Nullable - @Override - public List getStructuredList(String name) { - List value = listEntries.get(name); - if (value != null) { - return Collections.unmodifiableList(value); - } - return null; - } - - @Override - public Set getPropertyKeys() { - Set keys = new LinkedHashSet<>(); - keys.addAll(simpleEntries.keySet()); - keys.addAll(listEntries.keySet()); - keys.addAll(mapEntries.keySet()); - return Collections.unmodifiableSet(keys); - } - - @Override - public String toString() { - StringJoiner joiner = new StringJoiner(", ", "SpringDeclarativeConfigProperties{", "}"); - simpleEntries.forEach((key, value) -> joiner.add(key + "=" + value)); - listEntries.forEach((key, value) -> joiner.add(key + "=" + value)); - mapEntries.forEach((key, value) -> joiner.add(key + "=" + value)); - return joiner.toString(); - } - - /** Return the {@link ComponentLoader}. */ - @Override - public ComponentLoader getComponentLoader() { - return componentLoader; - } -}