From 696ea966b4ee2c7e61f40fb18867a4124d6365db Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 11 Mar 2024 11:06:34 +0100 Subject: [PATCH] support properties customizer and default properties --- .../build.gradle.kts | 9 ---- .../OpenTelemetryAutoConfiguration.java | 31 +++++-------- .../properties/SpringConfigProperties.java | 44 ++++++++++++++----- .../OtlpExporterPropertiesTest.java | 5 ++- .../resources/SpringConfigPropertiesTest.java | 12 ++++- .../resources/SpringResourceProviderTest.java | 17 ++++++- smoke-tests-otel-starter/build.gradle.kts | 9 ---- .../smoketest/OtelSpringStarterSmokeTest.java | 19 +++++++- 8 files changed, 95 insertions(+), 51 deletions(-) diff --git a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts index 6ad64894baee..ffe05a2e8c9b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts +++ b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts @@ -9,16 +9,7 @@ group = "io.opentelemetry.instrumentation" val versions: Map by project val springBootVersion = versions["org.springframework.boot"] -configurations.all { - resolutionStrategy { - force("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.36.0-SNAPSHOT") - force("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.36.0-SNAPSHOT") - } -} - dependencies { - implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.36.0-SNAPSHOT") - implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.36.0-SNAPSHOT") implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion") annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion") 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 2e0e2fb3fe21..378bfe9ee4c7 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 @@ -17,7 +17,6 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringResourceProvider; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import java.util.Optional; import org.springframework.beans.factory.ObjectProvider; @@ -30,7 +29,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import org.springframework.expression.spel.standard.SpelExpressionParser; /** * Create {@link io.opentelemetry.api.OpenTelemetry} bean if bean is missing. @@ -61,21 +59,6 @@ public MapConverter mapConverter() { return new MapConverter(); } - @Bean - @ConditionalOnMissingBean - ConfigProperties configProperties( - Environment env, - OtlpExporterProperties otlpExporterProperties, - OtelResourceProperties resourceProperties, - PropagationProperties propagationProperties) { - return new SpringConfigProperties( - env, - new SpelExpressionParser(), - otlpExporterProperties, - resourceProperties, - propagationProperties); - } - @Bean public SpringComponentLoader springComponentLoader(ApplicationContext applicationContext) { return new SpringComponentLoader(applicationContext); @@ -95,14 +78,24 @@ public ResourceProvider otelDistroVersionResourceProvider() { // If you change the bean name, also change it in the OpenTelemetryJdbcDriverAutoConfiguration // class public OpenTelemetry openTelemetry( + Environment env, + OtlpExporterProperties otlpExporterProperties, + OtelResourceProperties resourceProperties, + PropagationProperties propagationProperties, SpringComponentLoader componentLoader, - ConfigProperties configProperties, ObjectProvider openTelemetryConsumerProvider) { OpenTelemetry openTelemetry = AutoConfigureUtil.setComponentLoader( AutoConfigureUtil.setConfigPropertiesCustomizer( - AutoConfiguredOpenTelemetrySdk.builder(), c -> configProperties), + AutoConfiguredOpenTelemetrySdk.builder(), + c -> + SpringConfigProperties.create( + env, + otlpExporterProperties, + resourceProperties, + propagationProperties, + c)), componentLoader) .build() .getOpenTelemetrySdk(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/SpringConfigProperties.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/SpringConfigProperties.java index dd9c6c96ea3c..1f707ff6b426 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/SpringConfigProperties.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/SpringConfigProperties.java @@ -15,6 +15,7 @@ import javax.annotation.Nullable; import org.springframework.core.env.Environment; import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; public class SpringConfigProperties implements ConfigProperties { private final Environment environment; @@ -23,18 +24,37 @@ public class SpringConfigProperties implements ConfigProperties { private final OtlpExporterProperties otlpExporterProperties; private final OtelResourceProperties resourceProperties; private final PropagationProperties propagationProperties; + private final ConfigProperties fallback; public SpringConfigProperties( Environment environment, ExpressionParser parser, OtlpExporterProperties otlpExporterProperties, OtelResourceProperties resourceProperties, - PropagationProperties propagationProperties) { + PropagationProperties propagationProperties, + ConfigProperties fallback) { this.environment = environment; this.parser = parser; this.otlpExporterProperties = otlpExporterProperties; this.resourceProperties = resourceProperties; this.propagationProperties = propagationProperties; + this.fallback = fallback; + } + + // visible for testing + public static ConfigProperties create( + Environment env, + OtlpExporterProperties otlpExporterProperties, + OtelResourceProperties resourceProperties, + PropagationProperties propagationProperties, + ConfigProperties fallback) { + return new SpringConfigProperties( + env, + new SpelExpressionParser(), + otlpExporterProperties, + resourceProperties, + propagationProperties, + fallback); } @Nullable @@ -46,31 +66,31 @@ public String getString(String name) { // in specification to default to `http/protobuf` return OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF; } - return value; + return or(value, fallback.getString(name)); } @Nullable @Override public Boolean getBoolean(String name) { - return environment.getProperty(name, Boolean.class); + return or(environment.getProperty(name, Boolean.class), fallback.getBoolean(name)); } @Nullable @Override public Integer getInt(String name) { - return environment.getProperty(name, Integer.class); + return or(environment.getProperty(name, Integer.class), fallback.getInt(name)); } @Nullable @Override public Long getLong(String name) { - return environment.getProperty(name, Long.class); + return or(environment.getProperty(name, Long.class), fallback.getLong(name)); } @Nullable @Override public Double getDouble(String name) { - return environment.getProperty(name, Double.class); + return or(environment.getProperty(name, Double.class), fallback.getDouble(name)); } @SuppressWarnings("unchecked") @@ -80,8 +100,7 @@ public List getList(String name) { return propagationProperties.getPropagators(); } - List value = environment.getProperty(name, List.class); - return value == null ? Collections.emptyList() : value; + return or(environment.getProperty(name, List.class), fallback.getList(name)); } @Nullable @@ -89,7 +108,7 @@ public List getList(String name) { public Duration getDuration(String name) { String value = getString(name); if (value == null) { - return null; + return fallback.getDuration(name); } return DefaultConfigProperties.createFromMap(Collections.singletonMap(name, value)) .getDuration(name); @@ -116,8 +135,13 @@ public Map getMap(String name) { String value = environment.getProperty(name); if (value == null) { - return Collections.emptyMap(); + return fallback.getMap(name); } return (Map) parser.parseExpression(value).getValue(); } + + @Nullable + private static T or(@Nullable T first, @Nullable T second) { + return first != null ? first : second; + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/OtlpExporterPropertiesTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/OtlpExporterPropertiesTest.java index 0d019fca21d5..1df883e0c0a1 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/OtlpExporterPropertiesTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/properties/OtlpExporterPropertiesTest.java @@ -10,7 +10,9 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Arrays; +import java.util.Collections; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -90,6 +92,7 @@ private static ConfigProperties getConfig(AssertableApplicationContext context) new SpelExpressionParser(), context.getBean(OtlpExporterProperties.class), new OtelResourceProperties(), - new PropagationProperties()); + new PropagationProperties(), + DefaultConfigProperties.createFromMap(Collections.emptyMap())); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringConfigPropertiesTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringConfigPropertiesTest.java index 40be98540cc8..76cbe9c76c63 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringConfigPropertiesTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringConfigPropertiesTest.java @@ -12,6 +12,9 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtlpExporterProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.properties.PropagationProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.properties.SpringConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -30,19 +33,26 @@ void shouldInitializeAttributesByMapInArow() { .run( context -> { Environment env = context.getBean("environment", Environment.class); + Map fallback = new HashMap<>(); + fallback.put("fallback", "fallbackVal"); + fallback.put("otel.springboot.test.map", "hidden"); + SpringConfigProperties config = new SpringConfigProperties( env, new SpelExpressionParser(), new OtlpExporterProperties(), new OtelResourceProperties(), - new PropagationProperties()); + new PropagationProperties(), + DefaultConfigProperties.createFromMap(fallback)); assertThat(config.getMap("otel.springboot.test.map")) .contains( entry("environment", "dev"), entry("xyz", "foo"), entry("service.instance.id", "id-example")); + + assertThat(config.getString("fallback")).isEqualTo("fallbackVal"); }); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProviderTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProviderTest.java index 6685a6f06555..ed5aad0bbe90 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProviderTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProviderTest.java @@ -9,9 +9,15 @@ import static io.opentelemetry.semconv.ResourceAttributes.SERVICE_VERSION; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtelResourceProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtlpExporterProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.PropagationProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.SpringConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import java.util.Collections; import java.util.Properties; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -19,6 +25,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.core.env.Environment; public class SpringResourceProviderTest { @@ -56,10 +63,18 @@ void shouldDetermineServiceNameAndVersionBySpringApplicationVersion() { } private static AttributesAssert assertResourceAttributes(AssertableApplicationContext context) { + ConfigProperties configProperties = + SpringConfigProperties.create( + context.getBean(Environment.class), + new OtlpExporterProperties(), + new OtelResourceProperties(), + new PropagationProperties(), + DefaultConfigProperties.createFromMap(Collections.emptyMap())); + return OpenTelemetryAssertions.assertThat( context .getBean(SpringResourceProvider.class) - .createResource(context.getBean(ConfigProperties.class)) + .createResource(configProperties) .getAttributes()); } } diff --git a/smoke-tests-otel-starter/build.gradle.kts b/smoke-tests-otel-starter/build.gradle.kts index 02622d10613e..35459869ee8b 100644 --- a/smoke-tests-otel-starter/build.gradle.kts +++ b/smoke-tests-otel-starter/build.gradle.kts @@ -10,16 +10,7 @@ otelJava { minJavaVersionSupported.set(JavaVersion.VERSION_17) } -configurations.all { - resolutionStrategy { - force("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.36.0-SNAPSHOT") - force("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.36.0-SNAPSHOT") - } -} - dependencies { - implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.36.0-SNAPSHOT") - implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.36.0-SNAPSHOT") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-data-jdbc") implementation("com.h2database:h2") diff --git a/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java index f7f0bbc130fa..8e1836933ba6 100644 --- a/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java @@ -9,7 +9,12 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtelResourceProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtlpExporterProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.PropagationProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.properties.SpringConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider; import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; @@ -27,6 +32,7 @@ import io.opentelemetry.semconv.SemanticAttributes; import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestApplication; import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestController; +import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +40,7 @@ import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; @SpringBootTest( classes = { @@ -63,7 +70,10 @@ class OtelSpringStarterSmokeTest { @Autowired private TestRestTemplate testRestTemplate; - @Autowired private ConfigProperties configProperties; + @Autowired private Environment environment; + @Autowired private PropagationProperties propagationProperties; + @Autowired private OtelResourceProperties otelResourceProperties; + @Autowired private OtlpExporterProperties otlpExporterProperties; @Configuration(proxyBeanMethods = false) static class TestConfiguration { @@ -116,6 +126,13 @@ public String getName() { @Test void propertyConversion() { + ConfigProperties configProperties = + SpringConfigProperties.create( + environment, + otlpExporterProperties, + otelResourceProperties, + propagationProperties, + DefaultConfigProperties.createFromMap(Collections.emptyMap())); assertThat(configProperties.getMap("otel.exporter.otlp.headers")) .containsEntry("a", "1") .containsEntry("b", "2");