From 46fc6b28e067a45d0e34158848ca2ee89d457ec4 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 8 Jul 2025 16:16:45 +0200 Subject: [PATCH 1/9] don't call old plugin when declarative config is in use --- .../AutoConfiguredOpenTelemetrySdkBuilder.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 1b0e43c1f12..61d96a19e66 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -446,15 +446,6 @@ public AutoConfiguredOpenTelemetrySdk build() { private AutoConfiguredOpenTelemetrySdk buildImpl() { SpiHelper spiHelper = SpiHelper.create(componentLoader); - if (!customized) { - customized = true; - mergeSdkTracerProviderConfigurer(); - for (AutoConfigurationCustomizerProvider customizer : - spiHelper.loadOrdered(AutoConfigurationCustomizerProvider.class)) { - customizer.customize(this); - } - } - ConfigProperties config = getConfig(); AutoConfiguredOpenTelemetrySdk fromFileConfiguration = @@ -468,6 +459,15 @@ private AutoConfiguredOpenTelemetrySdk buildImpl() { return fromFileConfiguration; } + if (!customized) { + customized = true; + mergeSdkTracerProviderConfigurer(); + for (AutoConfigurationCustomizerProvider customizer : + spiHelper.loadOrdered(AutoConfigurationCustomizerProvider.class)) { + customizer.customize(this); + } + } + Resource resource = ResourceConfiguration.configureResource(config, spiHelper, resourceCustomizer); From bef6aaec09b4fc2681f0a8958b0b4d14cbc364d4 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 9 Jul 2025 13:03:34 +0200 Subject: [PATCH 2/9] fix --- .../autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 61d96a19e66..17ea4c41416 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -446,10 +446,10 @@ public AutoConfiguredOpenTelemetrySdk build() { private AutoConfiguredOpenTelemetrySdk buildImpl() { SpiHelper spiHelper = SpiHelper.create(componentLoader); - ConfigProperties config = getConfig(); AutoConfiguredOpenTelemetrySdk fromFileConfiguration = - maybeConfigureFromFile(config, componentLoader); + maybeConfigureFromFile( + DefaultConfigProperties.create(Collections.emptyMap()), componentLoader); if (fromFileConfiguration != null) { maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk()); Object configProvider = fromFileConfiguration.getConfigProvider(); @@ -467,6 +467,7 @@ private AutoConfiguredOpenTelemetrySdk buildImpl() { customizer.customize(this); } } + ConfigProperties config = getConfig(); Resource resource = ResourceConfiguration.configureResource(config, spiHelper, resourceCustomizer); From c8ccdaae3d00d43d4f5a21ae8bd65b536ecd4df0 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 9 Jul 2025 13:23:46 +0200 Subject: [PATCH 3/9] fix --- .../autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 17ea4c41416..0c90318ae6c 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -447,9 +447,10 @@ public AutoConfiguredOpenTelemetrySdk build() { private AutoConfiguredOpenTelemetrySdk buildImpl() { SpiHelper spiHelper = SpiHelper.create(componentLoader); + ConfigProperties configProperties = + this.config != null ? this.config : DefaultConfigProperties.create(Collections.emptyMap()); AutoConfiguredOpenTelemetrySdk fromFileConfiguration = - maybeConfigureFromFile( - DefaultConfigProperties.create(Collections.emptyMap()), componentLoader); + maybeConfigureFromFile(configProperties, componentLoader); if (fromFileConfiguration != null) { maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk()); Object configProvider = fromFileConfiguration.getConfigProvider(); From 6242d308e46f25539da2c9d926fd2c52cf7b1080 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 9 Jul 2025 14:00:29 +0200 Subject: [PATCH 4/9] fix --- .../DeclarativeConfigurationTest.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java index a29a9f581d1..8c5f5db3ff4 100644 --- a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java @@ -6,9 +6,6 @@ package io.opentelemetry.sdk.autoconfigure; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static java.util.Collections.singletonMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -96,21 +93,23 @@ void configFile_fileNotFound() { assertThatThrownBy( () -> AutoConfiguredOpenTelemetrySdk.builder() - .addPropertiesSupplier(() -> singletonMap("otel.config.file", "foo")) - .addPropertiesSupplier( - () -> singletonMap("otel.experimental.config.file", "foo")) - .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) + .setConfig( + DefaultConfigProperties.createFromMap( + Collections.singletonMap("otel.experimental.config.file", "foo"))) .build()) .isInstanceOf(ConfigurationException.class) .hasMessageContaining("Configuration file not found"); - assertThatCode( + assertThatThrownBy( () -> AutoConfiguredOpenTelemetrySdk.builder() - .addPropertiesSupplier(() -> singletonMap("otel.experimental.config.file", "")) - .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) + .setConfig( + DefaultConfigProperties.createFromMap( + Collections.singletonMap("otel.experimental.config.file", ""))) .build()) - .doesNotThrowAnyException(); + .hasMessageContaining( + "otel.metrics.exporter set to \"otlp\" but opentelemetry-exporter-otlp not found on " + + "classpath. Make sure to add it as a dependency."); } @Test From 0a15c4a5847e11c22dc05d0a6c172caf1aad56ad Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 23 Jul 2025 11:28:20 +0200 Subject: [PATCH 5/9] fix rebase --- .../AutoConfiguredOpenTelemetrySdkBuilder.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 0c90318ae6c..9211df6f122 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -445,12 +445,12 @@ public AutoConfiguredOpenTelemetrySdk build() { } private AutoConfiguredOpenTelemetrySdk buildImpl() { - SpiHelper spiHelper = SpiHelper.create(componentLoader); - - ConfigProperties configProperties = - this.config != null ? this.config : DefaultConfigProperties.create(Collections.emptyMap()); AutoConfiguredOpenTelemetrySdk fromFileConfiguration = - maybeConfigureFromFile(configProperties, componentLoader); + maybeConfigureFromFile( + this.config != null + ? this.config + : DefaultConfigProperties.create(Collections.emptyMap(), componentLoader), + componentLoader); if (fromFileConfiguration != null) { maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk()); Object configProvider = fromFileConfiguration.getConfigProvider(); @@ -460,6 +460,7 @@ private AutoConfiguredOpenTelemetrySdk buildImpl() { return fromFileConfiguration; } + SpiHelper spiHelper = SpiHelper.create(componentLoader); if (!customized) { customized = true; mergeSdkTracerProviderConfigurer(); From d0248737f30a3e138123880d10fffd3085849b2f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 23 Jul 2025 11:57:16 +0200 Subject: [PATCH 6/9] fix test --- .../DeclarativeConfigurationTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java index 8c5f5db3ff4..62d00718e7b 100644 --- a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java @@ -6,6 +6,9 @@ package io.opentelemetry.sdk.autoconfigure; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -100,16 +103,13 @@ void configFile_fileNotFound() { .isInstanceOf(ConfigurationException.class) .hasMessageContaining("Configuration file not found"); - assertThatThrownBy( + assertThatCode( () -> AutoConfiguredOpenTelemetrySdk.builder() - .setConfig( - DefaultConfigProperties.createFromMap( - Collections.singletonMap("otel.experimental.config.file", ""))) + .addPropertiesSupplier(() -> singletonMap("otel.experimental.config.file", "")) + .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) .build()) - .hasMessageContaining( - "otel.metrics.exporter set to \"otlp\" but opentelemetry-exporter-otlp not found on " - + "classpath. Make sure to add it as a dependency."); + .doesNotThrowAnyException(); } @Test From 5b81ef83939d4b3753f1a489acf5a35bea805bb2 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 2 Sep 2025 14:59:10 +0200 Subject: [PATCH 7/9] add spi to load a declarative config model --- sdk-extensions/autoconfigure/build.gradle.kts | 8 ++ ...AutoConfiguredOpenTelemetrySdkBuilder.java | 8 ++ .../sdk/autoconfigure/IncubatingUtil.java | 84 ++++++++++++++----- .../DeclarativeConfigurationSpiTest.java | 48 +++++++++++ .../TestDeclarativeConfigurationProvider.java | 32 +++++++ ...ileconfig.DeclarativeConfigurationProvider | 1 + .../DeclarativeConfigurationProvider.java | 19 +++++ 7 files changed, 179 insertions(+), 21 deletions(-) create mode 100644 sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java create mode 100644 sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/TestDeclarativeConfigurationProvider.java create mode 100644 sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/resources/META-INF/services/io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider create mode 100644 sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index a00685f6e6d..72d6b88bec6 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -86,6 +86,14 @@ testing { implementation(project(":sdk:testing")) } } + + register("testDeclarativeConfigSpi") { + dependencies { + implementation(project(":sdk-extensions:incubator")) + implementation(project(":exporters:logging")) + implementation(project(":sdk:testing")) + } + } } } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 9211df6f122..7f1a5678959 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -574,6 +574,14 @@ void configureSdk( @Nullable private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile( ConfigProperties config, ComponentLoader componentLoader) { + if (INCUBATOR_AVAILABLE) { + AutoConfiguredOpenTelemetrySdk sdk = IncubatingUtil.configureFromSpi(componentLoader); + if (sdk != null) { + logger.fine("Autoconfigured from SPI by opentelemetry-sdk-extension-incubator"); + return sdk; + } + } + String otelConfigFile = config.getString("otel.config.file"); if (otelConfigFile != null && !otelConfigFile.isEmpty()) { logger.warning( diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java index df2686ece55..e4c0d716071 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java @@ -20,6 +20,7 @@ import java.lang.reflect.Method; import java.util.Objects; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * Utilities for interacting with incubating components ({@code @@ -36,28 +37,12 @@ static AutoConfiguredOpenTelemetrySdk configureFromFile( Logger logger, String configurationFile, ComponentLoader componentLoader) { logger.fine("Autoconfiguring from configuration file: " + configurationFile); try (FileInputStream fis = new FileInputStream(configurationFile)) { - Class declarativeConfiguration = + Object model = Class.forName( - "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration"); - Method parse = declarativeConfiguration.getMethod("parse", InputStream.class); - Object model = parse.invoke(null, fis); - Class openTelemetryConfiguration = - Class.forName( - "io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel"); - Method create = - declarativeConfiguration.getMethod( - "create", openTelemetryConfiguration, ComponentLoader.class); - OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader); - Class sdkConfigProvider = - Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider"); - Method createFileConfigProvider = - sdkConfigProvider.getMethod("create", openTelemetryConfiguration, ComponentLoader.class); - ConfigProvider configProvider = - (ConfigProvider) createFileConfigProvider.invoke(null, model, componentLoader); - // Note: can't access file configuration resource without reflection so setting a dummy - // resource - return AutoConfiguredOpenTelemetrySdk.create( - sdk, Resource.getDefault(), null, configProvider); + "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration") + .getMethod("parse", InputStream.class) + .invoke(null, fis); + return getOpenTelemetrySdk(model, componentLoader); } catch (FileNotFoundException e) { throw new ConfigurationException("Configuration file not found", e); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { @@ -77,6 +62,63 @@ static AutoConfiguredOpenTelemetrySdk configureFromFile( } } + @Nullable + public static AutoConfiguredOpenTelemetrySdk configureFromSpi(ComponentLoader componentLoader) { + try { + Class providerClass = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider"); + Method getConfigurationModel = providerClass.getMethod("getConfigurationModel"); + + for (Object configProvider : componentLoader.load(providerClass)) { + Object model = getConfigurationModel.invoke(configProvider); + if (model != null) { + return getOpenTelemetrySdk(model, componentLoader); + } + } + return null; + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new ConfigurationException( + "Error configuring from SPI. Is opentelemetry-sdk-extension-incubator on the classpath?", + e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof DeclarativeConfigException) { + throw toConfigurationException((DeclarativeConfigException) cause); + } + throw new ConfigurationException("Unexpected error configuring from SPI", e); + } + } + + private static AutoConfiguredOpenTelemetrySdk getOpenTelemetrySdk( + Object model, ComponentLoader componentLoader) + throws IllegalAccessException, + InvocationTargetException, + ClassNotFoundException, + NoSuchMethodException { + + Class openTelemetryConfiguration = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel"); + Class declarativeConfiguration = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration"); + Method create = + declarativeConfiguration.getMethod( + "create", openTelemetryConfiguration, ComponentLoader.class); + + OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader); + Class sdkConfigProvider = + Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider"); + Method createFileConfigProvider = + sdkConfigProvider.getMethod("create", openTelemetryConfiguration, ComponentLoader.class); + ConfigProvider configProvider = + (ConfigProvider) createFileConfigProvider.invoke(null, model, componentLoader); + // Note: can't access file configuration resource without reflection so setting a dummy + // resource + return AutoConfiguredOpenTelemetrySdk.create(sdk, Resource.getDefault(), null, configProvider); + } + private static ConfigurationException toConfigurationException( DeclarativeConfigException exception) { String message = Objects.requireNonNull(exception.getMessage()); diff --git a/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java new file mode 100644 index 00000000000..a3f0d6892ac --- /dev/null +++ b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.internal.testing.CleanupExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DeclarativeConfigurationSpiTest { + + @RegisterExtension private static final CleanupExtension cleanup = new CleanupExtension(); + + @Test + void configFromSpi() { + OpenTelemetrySdk expectedSdk = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .setResource( + Resource.getDefault().toBuilder().put("service.name", "test").build()) + .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) + .build()) + .build(); + cleanup.addCloseable(expectedSdk); + AutoConfiguredOpenTelemetrySdkBuilder builder = spy(AutoConfiguredOpenTelemetrySdk.builder()); + Thread thread = new Thread(); + doReturn(thread).when(builder).shutdownHook(any()); + + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk = builder.build(); + cleanup.addCloseable(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk()); + + assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString()) + .isEqualTo(expectedSdk.toString()); + } +} diff --git a/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/TestDeclarativeConfigurationProvider.java b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/TestDeclarativeConfigurationProvider.java new file mode 100644 index 00000000000..28a33710394 --- /dev/null +++ b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/TestDeclarativeConfigurationProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure; + +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +public class TestDeclarativeConfigurationProvider implements DeclarativeConfigurationProvider { + @Override + public OpenTelemetryConfigurationModel getConfigurationModel() { + String yaml = + "file_format: \"1.0-rc.1\"\n" + + "resource:\n" + + " attributes:\n" + + " - name: service.name\n" + + " value: test\n" + + "tracer_provider:\n" + + " processors:\n" + + " - simple:\n" + + " exporter:\n" + + " console: {}\n"; + + return DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/resources/META-INF/services/io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/resources/META-INF/services/io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider new file mode 100644 index 00000000000..80610d72e48 --- /dev/null +++ b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/resources/META-INF/services/io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider @@ -0,0 +1 @@ +io.opentelemetry.sdk.autoconfigure.TestDeclarativeConfigurationProvider diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java new file mode 100644 index 00000000000..5f64dde6ca3 --- /dev/null +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.extension.incubator.fileconfig; + +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import javax.annotation.Nullable; + +/** A service provider interface (SPI) for providing a declarative configuration model */ +public interface DeclarativeConfigurationProvider { + /** + * Returns an OpenTelemetry configuration model to be used when configuring the SDK, or {@code + * null} if no configuration is provided by this provider. + */ + @Nullable + OpenTelemetryConfigurationModel getConfigurationModel(); +} From 593162e1191f4f230399e7a7d2cadf45d1729513 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 2 Sep 2025 15:11:45 +0200 Subject: [PATCH 8/9] add spi to load a declarative config model --- .../incubator/fileconfig/DeclarativeConfigurationProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java index 5f64dde6ca3..37c181f9401 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationProvider.java @@ -8,7 +8,7 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import javax.annotation.Nullable; -/** A service provider interface (SPI) for providing a declarative configuration model */ +/** A service provider interface (SPI) for providing a declarative configuration model. */ public interface DeclarativeConfigurationProvider { /** * Returns an OpenTelemetry configuration model to be used when configuring the SDK, or {@code From 07c61ef49db4d61f2dbda535b706fa02b718b8a6 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 2 Sep 2025 16:54:40 +0200 Subject: [PATCH 9/9] test coverage --- .../sdk/autoconfigure/IncubatingUtil.java | 99 +++++++++++-------- .../sdk/autoconfigure/IncubatingUtilTest.java | 44 +++++++++ 2 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtilTest.java diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java index e4c0d716071..ce700181c93 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java @@ -5,6 +5,8 @@ package io.opentelemetry.sdk.autoconfigure; +import static java.util.Objects.requireNonNull; + import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.incubator.config.GlobalConfigProvider; @@ -18,7 +20,6 @@ import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Objects; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -33,28 +34,32 @@ final class IncubatingUtil { private IncubatingUtil() {} + // Visible for testing + interface Factory { + @Nullable + AutoConfiguredOpenTelemetrySdk create() + throws ClassNotFoundException, + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException; + } + static AutoConfiguredOpenTelemetrySdk configureFromFile( Logger logger, String configurationFile, ComponentLoader componentLoader) { logger.fine("Autoconfiguring from configuration file: " + configurationFile); try (FileInputStream fis = new FileInputStream(configurationFile)) { - Object model = - Class.forName( - "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration") - .getMethod("parse", InputStream.class) - .invoke(null, fis); - return getOpenTelemetrySdk(model, componentLoader); + return requireNonNull( + createWithFactory( + "file", + () -> + getOpenTelemetrySdk( + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration") + .getMethod("parse", InputStream.class) + .invoke(null, fis), + componentLoader))); } catch (FileNotFoundException e) { throw new ConfigurationException("Configuration file not found", e); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { - throw new ConfigurationException( - "Error configuring from file. Is opentelemetry-sdk-extension-incubator on the classpath?", - e); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof DeclarativeConfigException) { - throw toConfigurationException((DeclarativeConfigException) cause); - } - throw new ConfigurationException("Unexpected error configuring from file", e); } catch (IOException e) { // IOException (other than FileNotFoundException which is caught above) is only thrown // above by FileInputStream.close() @@ -64,30 +69,22 @@ static AutoConfiguredOpenTelemetrySdk configureFromFile( @Nullable public static AutoConfiguredOpenTelemetrySdk configureFromSpi(ComponentLoader componentLoader) { - try { - Class providerClass = - Class.forName( - "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider"); - Method getConfigurationModel = providerClass.getMethod("getConfigurationModel"); + return createWithFactory( + "SPI", + () -> { + Class providerClass = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationProvider"); + Method getConfigurationModel = providerClass.getMethod("getConfigurationModel"); - for (Object configProvider : componentLoader.load(providerClass)) { - Object model = getConfigurationModel.invoke(configProvider); - if (model != null) { - return getOpenTelemetrySdk(model, componentLoader); - } - } - return null; - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { - throw new ConfigurationException( - "Error configuring from SPI. Is opentelemetry-sdk-extension-incubator on the classpath?", - e); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof DeclarativeConfigException) { - throw toConfigurationException((DeclarativeConfigException) cause); - } - throw new ConfigurationException("Unexpected error configuring from SPI", e); - } + for (Object configProvider : componentLoader.load(providerClass)) { + Object model = getConfigurationModel.invoke(configProvider); + if (model != null) { + return getOpenTelemetrySdk(model, componentLoader); + } + } + return null; + }); } private static AutoConfiguredOpenTelemetrySdk getOpenTelemetrySdk( @@ -119,9 +116,29 @@ private static AutoConfiguredOpenTelemetrySdk getOpenTelemetrySdk( return AutoConfiguredOpenTelemetrySdk.create(sdk, Resource.getDefault(), null, configProvider); } + // Visible for testing + @Nullable + static AutoConfiguredOpenTelemetrySdk createWithFactory(String name, Factory factory) { + try { + return factory.create(); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new ConfigurationException( + String.format( + "Error configuring from %s. Is opentelemetry-sdk-extension-incubator on the classpath?", + name), + e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof DeclarativeConfigException) { + throw toConfigurationException((DeclarativeConfigException) cause); + } + throw new ConfigurationException("Unexpected error configuring from " + name, e); + } + } + private static ConfigurationException toConfigurationException( DeclarativeConfigException exception) { - String message = Objects.requireNonNull(exception.getMessage()); + String message = requireNonNull(exception.getMessage()); return new ConfigurationException(message, exception); } diff --git a/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtilTest.java b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtilTest.java new file mode 100644 index 00000000000..12cb8274ed8 --- /dev/null +++ b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtilTest.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThatCode; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import java.lang.reflect.InvocationTargetException; +import org.junit.jupiter.api.Test; + +class IncubatingUtilTest { + + @Test + void classNotFoundException() { + assertThatCode( + () -> + IncubatingUtil.createWithFactory( + "test", + () -> { + Class.forName("foo"); + return null; + })) + .isInstanceOf(ConfigurationException.class) + .hasMessage( + "Error configuring from test. Is opentelemetry-sdk-extension-incubator on the classpath?"); + } + + @Test + void invocationTargetException() { + assertThatCode( + () -> + IncubatingUtil.createWithFactory( + "test", + () -> { + throw new InvocationTargetException(new RuntimeException("test exception")); + })) + .isInstanceOf(ConfigurationException.class) + .hasMessage("Unexpected error configuring from test") + .hasRootCauseMessage("test exception"); + } +}