From d3f21b5c61c58ff3fa8bfef9c312cc644d710e81 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Wed, 20 May 2026 14:44:56 -0500 Subject: [PATCH 1/6] Add OSGi support for autoconfigure and declarative config --- exporters/otlp/all/build.gradle.kts | 6 + extensions/trace-propagators/build.gradle.kts | 5 + integration-tests/osgi/build.gradle.kts | 78 ++++++++- .../osgi/AutoconfigureTest.java | 108 ++++++++++++ ...stAutoConfigurationCustomizerProvider.java | 26 +++ .../osgi/TestMetricReaderProvider.java | 53 ++++++ .../osgi/TestSamplerProvider.java | 53 ++++++ ...re.spi.AutoConfigurationCustomizerProvider | 1 + ....internal.ConfigurableMetricReaderProvider | 1 + ...ure.spi.traces.ConfigurableSamplerProvider | 1 + .../AutoconfigureDeclarativeConfigTest.java | 155 ++++++++++++++++++ ...stAutoConfigurationCustomizerProvider.java | 26 +++ .../osgi/TestMetricReaderProvider.java | 53 ++++++ .../osgi/TestSamplerProvider.java | 53 ++++++ ...re.spi.AutoConfigurationCustomizerProvider | 1 + ....internal.ConfigurableMetricReaderProvider | 1 + ...ure.spi.traces.ConfigurableSamplerProvider | 1 + sdk-extensions/autoconfigure/build.gradle.kts | 21 ++- .../DeclarativeConfigContext.java | 5 + 19 files changed, 641 insertions(+), 7 deletions(-) create mode 100644 integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/AutoconfigureTest.java create mode 100644 integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider create mode 100644 integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider create mode 100644 integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider diff --git a/exporters/otlp/all/build.gradle.kts b/exporters/otlp/all/build.gradle.kts index 62fee8cfc87..3ab8bf3d499 100644 --- a/exporters/otlp/all/build.gradle.kts +++ b/exporters/otlp/all/build.gradle.kts @@ -12,6 +12,12 @@ otelJava.moduleName.set("io.opentelemetry.exporter.otlp") otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator.config")) // io.grpc and org.jspecify.annotations are not OSGi bundles; must use unversioned optional. otelJava.osgiUnversionedOptionalPackages.set(listOf("io.grpc", "org.jspecify.annotations")) +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) base.archivesName.set("opentelemetry-exporter-otlp") dependencies { diff --git a/extensions/trace-propagators/build.gradle.kts b/extensions/trace-propagators/build.gradle.kts index 8ede73f0b0e..192e638b224 100644 --- a/extensions/trace-propagators/build.gradle.kts +++ b/extensions/trace-propagators/build.gradle.kts @@ -8,6 +8,11 @@ plugins { description = "OpenTelemetry Extension : Trace Propagators" otelJava.moduleName.set("io.opentelemetry.extension.trace.propagation") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator")) +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { api(project(":api:all")) diff --git a/integration-tests/osgi/build.gradle.kts b/integration-tests/osgi/build.gradle.kts index c13bbfdf892..ef479e95f70 100644 --- a/integration-tests/osgi/build.gradle.kts +++ b/integration-tests/osgi/build.gradle.kts @@ -55,6 +55,9 @@ fun registerOsgiSuite( suiteName: String, extraRunrequires: List = emptyList(), extraRunsystempackages: List = emptyList(), + // SPI types that the testing bundle provides via META-INF/services (noop test implementations). + // Generates Provide-Capability + Require-Capability registrar so SPI Fly picks them up. + serviceLoaderProvides: List = emptyList(), minJavaVersion: Int? = null, configureDependencies: OsgiSuiteDependencies.() -> Unit = {} ): TaskProvider { @@ -84,10 +87,15 @@ fun registerOsgiSuite( // @Testable annotation to populate Test-Cases). Without this, testImplementation dependencies // like junit-jupiter are invisible to BND, causing Test-Cases to be empty and 0 tests to run. classpath(sourceSet.runtimeClasspath) - bnd( + val bndArgs = mutableListOf( "Bundle-SymbolicName: $bsn", "Test-Cases: \${classes;HIERARCHY_INDIRECTLY_ANNOTATED;org.junit.platform.commons.annotation.Testable;CONCRETE}" ) + if (serviceLoaderProvides.isNotEmpty()) { + bndArgs.add("Provide-Capability: ${serviceLoaderProvides.joinToString(",") { "osgi.serviceloader;osgi.serviceloader=\"$it\"" }}") + bndArgs.add("Require-Capability: osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.registrar)\"") + } + bnd(*bndArgs.toTypedArray()) } } @@ -204,10 +212,6 @@ fun registerOsgiSuite( // bundle which includes those, then mask the fact that OSGi fails when using a bundle without those // until opentelemetry-api OSGi configuration is updated to indicate that they are optional. -// TODO (jack-berg): Add additional test bundles with dependency combinations reflecting popular use cases: -// - with autoconfigure -// - with file configuration - // Suite: sdk — exercises core SDK OSGi metadata in isolation val sdkSuiteTask = registerOsgiSuite("sdk") { implementation(project(":sdk:all")) @@ -248,6 +252,61 @@ val otlpGrpcOkHttpSuiteTask = registerOsgiSuite( implementation(project(":exporters:otlp:all")) } +// Autoconfigure suites. Use real exporter modules so both the consumer side +// (autoconfigure Require-Capability) and the provider side (exporter Provide-Capability) are +// validated by the BND resolver. + +// Noop SPI implementations provided by the autoconfigure testing bundles. These cover SPI types +// that autoconfigure loads but whose only real implementations (jaeger-remote-sampler, prometheus) +// are too heavyweight to include in a basic OSGi smoke test. +val autoconfigureNoopProvides = listOf( + "io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider", +) + +// Suite: autoconfigure with OTLP + JDK sender. Exercises the full SPI loading chain across all +// relevant SPI types: ConfigurableSpanExporterProvider / ConfigurableMetricExporterProvider / +// ConfigurableLogRecordExporterProvider (otlp:all), ConfigurablePropagatorProvider +// (trace-propagators), ResourceProvider (autoconfigure itself), plus noops for the rest. +val autoconfigureSuiteTask = registerOsgiSuite( + "autoconfigure", + extraRunrequires = listOf( + "opentelemetry-exporter-sender-jdk", + "opentelemetry-exporter-otlp", + "opentelemetry-extension-trace-propagators", + ), + serviceLoaderProvides = autoconfigureNoopProvides, + minJavaVersion = 11, +) { + implementation(project(":sdk:all")) + implementation(project(":sdk-extensions:autoconfigure")) + implementation(project(":exporters:otlp:all")) + implementation(project(":extensions:trace-propagators")) + runtimeOnly(project(":exporters:sender:jdk")) +} + +// Suite: autoconfigure + declarative-config. Same as above but with declarative-config on the +// classpath, additionally exercising declarative-config bundle OSGi metadata. +val autoconfigureDeclarativeConfigSuiteTask = registerOsgiSuite( + "autoconfigureDeclarativeConfig", + extraRunrequires = listOf( + "opentelemetry-exporter-sender-jdk", + "opentelemetry-exporter-otlp", + "opentelemetry-extension-trace-propagators", + "opentelemetry-sdk-extension-declarative-config", + ), + serviceLoaderProvides = autoconfigureNoopProvides, + minJavaVersion = 11, +) { + implementation(project(":sdk:all")) + implementation(project(":sdk-extensions:autoconfigure")) + implementation(project(":sdk-extensions:declarative-config")) + implementation(project(":exporters:otlp:all")) + implementation(project(":extensions:trace-propagators")) + runtimeOnly(project(":exporters:sender:jdk")) +} + tasks { jar { enabled = false @@ -256,7 +315,14 @@ tasks { // We need to replace junit testing with the testOSGi tasks, so we clear test actions and add // dependencies on all suite tasks. As a result, running :test runs all OSGi suites. actions.clear() - dependsOn(sdkSuiteTask, otlpHttpJdkSuiteTask, otlpHttpOkHttpSuiteTask, otlpGrpcOkHttpSuiteTask) + dependsOn( + sdkSuiteTask, + otlpHttpJdkSuiteTask, + otlpHttpOkHttpSuiteTask, + otlpGrpcOkHttpSuiteTask, + autoconfigureSuiteTask, + autoconfigureDeclarativeConfigSuiteTask + ) } } diff --git a/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/AutoconfigureTest.java b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/AutoconfigureTest.java new file mode 100644 index 00000000000..fb79075a1d7 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/AutoconfigureTest.java @@ -0,0 +1,108 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.osgi.test.junit5.context.BundleContextExtension; + +/** Verifies autoconfigure works in OSGi. */ +@ExtendWith(BundleContextExtension.class) +public class AutoconfigureTest { + + @Test + void autoConfiguredSdkInitializes() { + AutoConfiguredOpenTelemetrySdk autoConfigured = + AutoConfiguredOpenTelemetrySdk.builder() + .addPropertiesSupplier(AutoconfigureTest::config) + .build(); + + // The component loader autoconfigure uses: autoconfigure bundle's classloader. + ComponentLoader autoConfigureLoader = + ComponentLoader.forClassLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader()); + + Resource resource = + Resource.getDefault() + .merge( + Resource.create( + Attributes.of( + AttributeKey.stringKey("test.customizer"), "test-osgi-customizer"))); + OpenTelemetrySdk expected = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .setResource(resource) + .setSampler(new TestSamplerProvider.NoopSampler()) + .addSpanProcessor( + BatchSpanProcessor.builder( + OtlpHttpSpanExporter.builder() + .setComponentLoader(autoConfigureLoader) + .build()) + .build()) + .build()) + .setMeterProvider( + SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader(new TestMetricReaderProvider.NoopMetricReader()) + .build()) + .setLoggerProvider( + SdkLoggerProvider.builder() + .setResource(resource) + .addLogRecordProcessor( + BatchLogRecordProcessor.builder( + OtlpHttpLogRecordExporter.builder() + .setComponentLoader(autoConfigureLoader) + .build()) + .build()) + .build()) + .setPropagators( + ContextPropagators.create( + TextMapPropagator.composite( + W3CTraceContextPropagator.getInstance(), + W3CBaggagePropagator.getInstance(), + B3Propagator.injectingSingleHeader()))) + .build(); + try { + assertThat(autoConfigured.getOpenTelemetrySdk().toString()).isEqualTo(expected.toString()); + } finally { + autoConfigured.getOpenTelemetrySdk().close(); + expected.close(); + } + } + + private static Map config() { + Map props = new HashMap<>(); + props.put("otel.traces.exporter", "otlp"); + props.put("otel.metrics.exporter", "test-noop-reader"); + props.put("otel.logs.exporter", "otlp"); + props.put("otel.exporter.otlp.protocol", "http/protobuf"); + props.put("otel.propagators", "tracecontext,baggage,b3"); + props.put("otel.traces.sampler", "test-noop"); + return props; + } +} diff --git a/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java new file mode 100644 index 00000000000..830b4f5e123 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.resources.Resource; + +public class TestAutoConfigurationCustomizerProvider + implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addResourceCustomizer( + (resource, config) -> + resource.merge( + Resource.create( + Attributes.of( + AttributeKey.stringKey("test.customizer"), "test-osgi-customizer")))); + } +} diff --git a/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java new file mode 100644 index 00000000000..f54bbd93e71 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.export.CollectionRegistration; +import io.opentelemetry.sdk.metrics.export.MetricReader; + +public class TestMetricReaderProvider implements ConfigurableMetricReaderProvider { + + @Override + public String getName() { + return "test-noop-reader"; + } + + @Override + public MetricReader createMetricReader(ConfigProperties config) { + return new NoopMetricReader(); + } + + static final class NoopMetricReader implements MetricReader { + + @Override + public void register(CollectionRegistration registration) {} + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } + + @Override + public CompletableResultCode forceFlush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public String toString() { + return "TestNoopMetricReader"; + } + } +} diff --git a/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java new file mode 100644 index 00000000000..2f50e8dc69a --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import java.util.List; + +public class TestSamplerProvider implements ConfigurableSamplerProvider { + + @Override + public String getName() { + return "test-noop"; + } + + @Override + public Sampler createSampler(ConfigProperties config) { + return new NoopSampler(); + } + + static final class NoopSampler implements Sampler { + + @Override + public SamplingResult shouldSample( + Context parentContext, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks) { + return SamplingResult.recordAndSample(); + } + + @Override + public String getDescription() { + return "TestNoopSampler"; + } + + @Override + public String toString() { + return "TestNoopSampler"; + } + } +} diff --git a/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider new file mode 100644 index 00000000000..fd2a3e7b34e --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestAutoConfigurationCustomizerProvider diff --git a/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider b/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider new file mode 100644 index 00000000000..49f70c84af6 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestMetricReaderProvider diff --git a/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider b/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider new file mode 100644 index 00000000000..14663a9a1ab --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigure/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestSamplerProvider diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java new file mode 100644 index 00000000000..19e9e8baf20 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java @@ -0,0 +1,155 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; +import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.internal.OpenTelemetrySdkBuilderUtil; +import io.opentelemetry.sdk.internal.SdkConfigProvider; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.osgi.test.junit5.context.BundleContextExtension; + +/** Verifies autoconfigure with declarative config works in OSGi. */ +@ExtendWith(BundleContextExtension.class) +public class AutoconfigureDeclarativeConfigTest { + + @Test + void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { + // No explicit endpoint — exporters use their defaults (e.g. http://localhost:4318/v1/traces). + // This avoids coupling the expected SDK to endpoint URL formatting details in the YAML parser. + String yaml = + "file_format: \"1.0\"\n" + + "resource:\n" + + " attributes:\n" + + " - name: service.name\n" + + " value: test-osgi-declarative\n" + + "propagator:\n" + + " composite:\n" + + " - tracecontext:\n" + + " - b3:\n" + + "tracer_provider:\n" + + " processors:\n" + + " - simple:\n" + + " exporter:\n" + + " otlp_http: {}\n" + + "meter_provider:\n" + + " readers:\n" + + " - periodic:\n" + + " exporter:\n" + + " otlp_http: {}\n" + + "logger_provider:\n" + + " processors:\n" + + " - simple:\n" + + " exporter:\n" + + " otlp_http: {}\n"; + Path configFile = tempDir.resolve("otel-config.yaml"); + Files.write(configFile, yaml.getBytes(StandardCharsets.UTF_8)); + + // System.setProperty is required: addPropertiesSupplier because property suppliers are not + // resolved until after otel.config.file check + System.setProperty("otel.config.file", configFile.toString()); + AutoConfiguredOpenTelemetrySdk autoConfigured = + AutoConfiguredOpenTelemetrySdk.builder().build(); + + ComponentLoader delegateLoader = + ComponentLoader.forClassLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader()); + ComponentLoader autoConfigureLoader = + componentLoaderWithToString( + delegateLoader, "DeclarativeConfigContext{componentLoader=" + delegateLoader + "}"); + + Resource resource = + Resource.getDefault() + .merge( + Resource.create( + Attributes.of( + AttributeKey.stringKey("service.name"), "test-osgi-declarative"))); + OpenTelemetrySdk expected = + OpenTelemetrySdkBuilderUtil.setConfigProvider( + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .setResource(resource) + .addSpanProcessor( + SimpleSpanProcessor.create( + OtlpHttpSpanExporter.builder() + .setComponentLoader(autoConfigureLoader) + .build())) + .build()) + .setMeterProvider( + SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader( + PeriodicMetricReader.create( + OtlpHttpMetricExporter.builder() + .setComponentLoader(autoConfigureLoader) + .build())) + .build()) + .setLoggerProvider( + SdkLoggerProvider.builder() + .setResource(resource) + .addLogRecordProcessor( + SimpleLogRecordProcessor.create( + OtlpHttpLogRecordExporter.builder() + .setComponentLoader(autoConfigureLoader) + .build())) + .build()) + .setPropagators( + ContextPropagators.create( + TextMapPropagator.composite( + W3CTraceContextPropagator.getInstance(), + B3Propagator.injectingSingleHeader()))), + SdkConfigProvider.create(DeclarativeConfigProperties.empty())) + .build(); + try { + assertThat(autoConfigured.getOpenTelemetrySdk().toString()).isEqualTo(expected.toString()); + } finally { + autoConfigured.getOpenTelemetrySdk().close(); + expected.close(); + System.clearProperty("otel.config.file"); + } + } + + private static ComponentLoader componentLoaderWithToString( + ComponentLoader componentLoader, String toString) { + return new ComponentLoader() { + @Override + public Iterable load(Class spiClass) { + return componentLoader.load(spiClass); + } + + @Override + public String toString() { + return toString; + } + }; + } +} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java new file mode 100644 index 00000000000..830b4f5e123 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.resources.Resource; + +public class TestAutoConfigurationCustomizerProvider + implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addResourceCustomizer( + (resource, config) -> + resource.merge( + Resource.create( + Attributes.of( + AttributeKey.stringKey("test.customizer"), "test-osgi-customizer")))); + } +} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java new file mode 100644 index 00000000000..f54bbd93e71 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.export.CollectionRegistration; +import io.opentelemetry.sdk.metrics.export.MetricReader; + +public class TestMetricReaderProvider implements ConfigurableMetricReaderProvider { + + @Override + public String getName() { + return "test-noop-reader"; + } + + @Override + public MetricReader createMetricReader(ConfigProperties config) { + return new NoopMetricReader(); + } + + static final class NoopMetricReader implements MetricReader { + + @Override + public void register(CollectionRegistration registration) {} + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } + + @Override + public CompletableResultCode forceFlush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public String toString() { + return "TestNoopMetricReader"; + } + } +} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java new file mode 100644 index 00000000000..2f50e8dc69a --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import java.util.List; + +public class TestSamplerProvider implements ConfigurableSamplerProvider { + + @Override + public String getName() { + return "test-noop"; + } + + @Override + public Sampler createSampler(ConfigProperties config) { + return new NoopSampler(); + } + + static final class NoopSampler implements Sampler { + + @Override + public SamplingResult shouldSample( + Context parentContext, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks) { + return SamplingResult.recordAndSample(); + } + + @Override + public String getDescription() { + return "TestNoopSampler"; + } + + @Override + public String toString() { + return "TestNoopSampler"; + } + } +} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider new file mode 100644 index 00000000000..fd2a3e7b34e --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestAutoConfigurationCustomizerProvider diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider new file mode 100644 index 00000000000..49f70c84af6 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestMetricReaderProvider diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider new file mode 100644 index 00000000000..14663a9a1ab --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestSamplerProvider diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index 9005fa59e94..288fff4be41 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -5,7 +5,26 @@ plugins { description = "OpenTelemetry SDK Auto-configuration" otelJava.moduleName.set("io.opentelemetry.sdk.autoconfigure") -otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.sdk.extension.incubator")) +otelJava.osgiOptionalPackages.set(listOf( + "io.opentelemetry.sdk.extension.incubator", + "io.opentelemetry.api.incubator", + "io.opentelemetry.sdk.autoconfigure.declarativeconfig", +)) +// autoconfigure discovers these SPI types at runtime via ServiceLoader +otelJava.osgiServiceLoaderRequires.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", + "io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider", + "io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider", +)) +// autoconfigure provides EnvironmentResourceProvider via META-INF/services +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", +)) dependencies { api(project(":sdk:all")) diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java index 607a4397df0..a93c0999bea 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java @@ -197,6 +197,11 @@ T loadComponent(Class type, ConfigKeyValue configKeyValue) { } } + @Override + public String toString() { + return "DeclarativeConfigContext{componentLoader=" + componentLoader + '}'; + } + @Override public Iterable load(Class spiClass) { List result = ComponentLoader.loadList(componentLoader, spiClass); From b2f9b11c81fc97ff81a746bb039c4cc29a9446d8 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Wed, 20 May 2026 15:18:34 -0500 Subject: [PATCH 2/6] Refine declarative config osgi test, add missing provides-capability, add unit test to ensure provides-capability always matchines META-INF/services --- .../all/OsgiServiceLoaderManifestTest.java | 117 ++++++++++++++++++ exporters/logging-otlp/build.gradle.kts | 6 + exporters/logging/build.gradle.kts | 6 + exporters/prometheus/build.gradle.kts | 4 + .../grpc-managed-channel/build.gradle.kts | 3 + exporters/zipkin/build.gradle.kts | 3 + integration-tests/osgi/build.gradle.kts | 25 ++-- .../AutoconfigureDeclarativeConfigTest.java | 21 ++-- ...stAutoConfigurationCustomizerProvider.java | 26 ---- .../osgi/TestMetricReaderProvider.java | 53 -------- .../osgi/TestSamplerProvider.java | 53 -------- ...re.spi.AutoConfigurationCustomizerProvider | 1 - ....internal.ConfigurableMetricReaderProvider | 1 - ...ure.spi.traces.ConfigurableSamplerProvider | 1 - .../declarative-config/build.gradle.kts | 3 + sdk-extensions/incubator/build.gradle.kts | 4 + .../jaeger-remote-sampler/build.gradle.kts | 4 + sdk/testing/build.gradle.kts | 3 + 18 files changed, 174 insertions(+), 160 deletions(-) create mode 100644 all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java delete mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java delete mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java delete mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java delete mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider delete mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider delete mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider diff --git a/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java b/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java new file mode 100644 index 00000000000..8b90eec7caa --- /dev/null +++ b/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java @@ -0,0 +1,117 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.all; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import org.junit.jupiter.api.Test; + +/** + * Verifies that every OSGi bundle whose {@code META-INF/services/} directory registers SPI + * implementations also declares the corresponding {@code Provide-Capability: + * osgi.serviceloader;osgi.serviceloader=""} in its manifest. + */ +class OsgiServiceLoaderManifestTest { + + @Test + void allOsgiBundlesAdvertiseTheirServiceLoaderRegistrations() throws IOException { + List lines = Files.readAllLines(Path.of(System.getenv("ARTIFACTS_AND_JARS"))); + // violations: ": META-INF/services/ not in Provide-Capability" + List violations = new ArrayList<>(); + + for (String line : lines) { + String[] parts = line.split(":", 2); + String baseName = parts[0]; + String absolutePath = parts[1]; + + try (JarFile jar = new JarFile(new File(absolutePath))) { + Manifest manifest = jar.getManifest(); + if (manifest == null) { + continue; + } + Attributes mainAttrs = manifest.getMainAttributes(); + + // Only check OSGi bundles. + String bundleManifestVersion = mainAttrs.getValue("Bundle-ManifestVersion"); + if (bundleManifestVersion == null) { + continue; + } + + // Collect all SPI interface names from META-INF/services/. + List registeredSpis = new ArrayList<>(); + jar.stream() + .map(JarEntry::getName) + .filter(name -> name.startsWith("META-INF/services/") && !name.endsWith("/")) + .forEach(name -> registeredSpis.add(name.substring("META-INF/services/".length()))); + + if (registeredSpis.isEmpty()) { + continue; + } + + // Parse Provide-Capability for osgi.serviceloader entries. + String provideCapability = mainAttrs.getValue("Provide-Capability"); + List advertisedSpis = parseOsgiServiceLoaderCapabilities(provideCapability); + + for (String spi : registeredSpis) { + if (!advertisedSpis.contains(spi)) { + violations.add(baseName + ": META-INF/services/" + spi + " not in Provide-Capability"); + } + } + } + } + + assertThat(violations) + .as( + "OSGi bundles with META-INF/services registrations missing from Provide-Capability.\n" + + "Add the missing SPI to osgiServiceLoaderProvides in the module's build.gradle.kts.") + .isEmpty(); + } + + /** + * Parses the {@code Provide-Capability} manifest header and returns all {@code + * osgi.serviceloader} service type names. + * + *

Example: {@code osgi.serviceloader;osgi.serviceloader="com.example.Foo", + * osgi.serviceloader;osgi.serviceloader="com.example.Bar"} → {@code ["com.example.Foo", + * "com.example.Bar"]} + */ + private static List parseOsgiServiceLoaderCapabilities(String provideCapability) { + List result = new ArrayList<>(); + if (provideCapability == null || provideCapability.isEmpty()) { + return result; + } + // The header may be line-folded (continuation lines start with a space) — already unfolded + // by JarFile. Split on commas that are not inside quotes. + String[] clauses = provideCapability.split(",(?=\\s*osgi\\.)"); + for (String clause : clauses) { + clause = clause.trim(); + if (!clause.startsWith("osgi.serviceloader")) { + continue; + } + // Extract osgi.serviceloader="" + int eq = clause.indexOf("osgi.serviceloader=\""); + if (eq < 0) { + continue; + } + int start = eq + "osgi.serviceloader=\"".length(); + int end = clause.indexOf('"', start); + if (end > start) { + result.add(clause.substring(start, end)); + } + } + return result; + } +} diff --git a/exporters/logging-otlp/build.gradle.kts b/exporters/logging-otlp/build.gradle.kts index 0870f2c9937..1e9217386c5 100644 --- a/exporters/logging-otlp/build.gradle.kts +++ b/exporters/logging-otlp/build.gradle.kts @@ -7,6 +7,12 @@ plugins { description = "OpenTelemetry Protocol JSON Logging Exporters" otelJava.moduleName.set("io.opentelemetry.exporter.logging.otlp") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { implementation(project(":sdk:trace")) diff --git a/exporters/logging/build.gradle.kts b/exporters/logging/build.gradle.kts index 82e96771834..27beed5564c 100644 --- a/exporters/logging/build.gradle.kts +++ b/exporters/logging/build.gradle.kts @@ -7,6 +7,12 @@ plugins { description = "OpenTelemetry - Logging Exporter" otelJava.moduleName.set("io.opentelemetry.exporter.logging") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { api(project(":sdk:all")) diff --git a/exporters/prometheus/build.gradle.kts b/exporters/prometheus/build.gradle.kts index 6587f82784d..bf6ce726f60 100644 --- a/exporters/prometheus/build.gradle.kts +++ b/exporters/prometheus/build.gradle.kts @@ -5,6 +5,10 @@ plugins { description = "OpenTelemetry Prometheus Exporter" otelJava.moduleName.set("io.opentelemetry.exporter.prometheus") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { api(project(":sdk:metrics")) diff --git a/exporters/sender/grpc-managed-channel/build.gradle.kts b/exporters/sender/grpc-managed-channel/build.gradle.kts index 9ee493fb7a1..a92e6b54017 100644 --- a/exporters/sender/grpc-managed-channel/build.gradle.kts +++ b/exporters/sender/grpc-managed-channel/build.gradle.kts @@ -7,6 +7,9 @@ plugins { description = "OpenTelemetry gRPC Upstream Sender" otelJava.moduleName.set("io.opentelemetry.exporter.sender.grpc.managedchannel.internal") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.common.export.GrpcSenderProvider", +)) dependencies { annotationProcessor("com.google.auto.value:auto-value") diff --git a/exporters/zipkin/build.gradle.kts b/exporters/zipkin/build.gradle.kts index bee1da945c4..a80484500ae 100644 --- a/exporters/zipkin/build.gradle.kts +++ b/exporters/zipkin/build.gradle.kts @@ -7,6 +7,9 @@ plugins { description = "OpenTelemetry - Zipkin Exporter" otelJava.moduleName.set("io.opentelemetry.exporter.zipkin") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", +)) dependencies { api(project(":sdk:all")) diff --git a/integration-tests/osgi/build.gradle.kts b/integration-tests/osgi/build.gradle.kts index ef479e95f70..7768d7cee43 100644 --- a/integration-tests/osgi/build.gradle.kts +++ b/integration-tests/osgi/build.gradle.kts @@ -252,23 +252,10 @@ val otlpGrpcOkHttpSuiteTask = registerOsgiSuite( implementation(project(":exporters:otlp:all")) } -// Autoconfigure suites. Use real exporter modules so both the consumer side -// (autoconfigure Require-Capability) and the provider side (exporter Provide-Capability) are -// validated by the BND resolver. - -// Noop SPI implementations provided by the autoconfigure testing bundles. These cover SPI types -// that autoconfigure loads but whose only real implementations (jaeger-remote-sampler, prometheus) -// are too heavyweight to include in a basic OSGi smoke test. -val autoconfigureNoopProvides = listOf( - "io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider", - "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", - "io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider", -) +// Autoconfigure suites. // Suite: autoconfigure with OTLP + JDK sender. Exercises the full SPI loading chain across all -// relevant SPI types: ConfigurableSpanExporterProvider / ConfigurableMetricExporterProvider / -// ConfigurableLogRecordExporterProvider (otlp:all), ConfigurablePropagatorProvider -// (trace-propagators), ResourceProvider (autoconfigure itself), plus noops for the rest. +// SPI types. val autoconfigureSuiteTask = registerOsgiSuite( "autoconfigure", extraRunrequires = listOf( @@ -276,7 +263,12 @@ val autoconfigureSuiteTask = registerOsgiSuite( "opentelemetry-exporter-otlp", "opentelemetry-extension-trace-propagators", ), - serviceLoaderProvides = autoconfigureNoopProvides, + // Some SPIs have implementations in project modules. Others do not. To verify the ones without implementation, we provide noop implementations here. + serviceLoaderProvides = listOf( + "io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider", + ), minJavaVersion = 11, ) { implementation(project(":sdk:all")) @@ -296,7 +288,6 @@ val autoconfigureDeclarativeConfigSuiteTask = registerOsgiSuite( "opentelemetry-extension-trace-propagators", "opentelemetry-sdk-extension-declarative-config", ), - serviceLoaderProvides = autoconfigureNoopProvides, minJavaVersion = 11, ) { implementation(project(":sdk:all")) diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java index 19e9e8baf20..56f1e8a8a8d 100644 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.common.ComponentLoader; @@ -20,6 +19,7 @@ import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.ServiceResourceDetector; import io.opentelemetry.sdk.internal.OpenTelemetrySdkBuilderUtil; import io.opentelemetry.sdk.internal.SdkConfigProvider; import io.opentelemetry.sdk.logs.SdkLoggerProvider; @@ -44,14 +44,15 @@ public class AutoconfigureDeclarativeConfigTest { @Test void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { - // No explicit endpoint — exporters use their defaults (e.g. http://localhost:4318/v1/traces). - // This avoids coupling the expected SDK to endpoint URL formatting details in the YAML parser. String yaml = "file_format: \"1.0\"\n" + "resource:\n" + " attributes:\n" + " - name: service.name\n" + " value: test-osgi-declarative\n" + + " detection/development:\n" + + " detectors:\n" + + " - service: {}\n" + "propagator:\n" + " composite:\n" + " - tracecontext:\n" @@ -86,12 +87,16 @@ void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { componentLoaderWithToString( delegateLoader, "DeclarativeConfigContext{componentLoader=" + delegateLoader + "}"); + // ServiceResourceDetector.RANDOM_SERVICE_INSTANCE_ID is a static field — same value within + // a JVM run, so the expected and actual resources match deterministically. + Resource detectedResource = + new ServiceResourceDetector().create(DeclarativeConfigProperties.empty()); Resource resource = - Resource.getDefault() - .merge( - Resource.create( - Attributes.of( - AttributeKey.stringKey("service.name"), "test-osgi-declarative"))); + Resource.getDefault().toBuilder() + .putAll(detectedResource.getAttributes()) + .put(AttributeKey.stringKey("service.name"), "test-osgi-declarative") + .build(); + OpenTelemetrySdk expected = OpenTelemetrySdkBuilderUtil.setConfigProvider( OpenTelemetrySdk.builder() diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java deleted file mode 100644 index 830b4f5e123..00000000000 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestAutoConfigurationCustomizerProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.integrationtest.osgi; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; -import io.opentelemetry.sdk.resources.Resource; - -public class TestAutoConfigurationCustomizerProvider - implements AutoConfigurationCustomizerProvider { - - @Override - public void customize(AutoConfigurationCustomizer autoConfiguration) { - autoConfiguration.addResourceCustomizer( - (resource, config) -> - resource.merge( - Resource.create( - Attributes.of( - AttributeKey.stringKey("test.customizer"), "test-osgi-customizer")))); - } -} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java deleted file mode 100644 index f54bbd93e71..00000000000 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestMetricReaderProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.integrationtest.osgi; - -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.metrics.InstrumentType; -import io.opentelemetry.sdk.metrics.data.AggregationTemporality; -import io.opentelemetry.sdk.metrics.export.CollectionRegistration; -import io.opentelemetry.sdk.metrics.export.MetricReader; - -public class TestMetricReaderProvider implements ConfigurableMetricReaderProvider { - - @Override - public String getName() { - return "test-noop-reader"; - } - - @Override - public MetricReader createMetricReader(ConfigProperties config) { - return new NoopMetricReader(); - } - - static final class NoopMetricReader implements MetricReader { - - @Override - public void register(CollectionRegistration registration) {} - - @Override - public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { - return AggregationTemporality.CUMULATIVE; - } - - @Override - public CompletableResultCode forceFlush() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode shutdown() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public String toString() { - return "TestNoopMetricReader"; - } - } -} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java deleted file mode 100644 index 2f50e8dc69a..00000000000 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestSamplerProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.integrationtest.osgi; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.sdk.trace.samplers.SamplingResult; -import java.util.List; - -public class TestSamplerProvider implements ConfigurableSamplerProvider { - - @Override - public String getName() { - return "test-noop"; - } - - @Override - public Sampler createSampler(ConfigProperties config) { - return new NoopSampler(); - } - - static final class NoopSampler implements Sampler { - - @Override - public SamplingResult shouldSample( - Context parentContext, - String traceId, - String name, - SpanKind spanKind, - Attributes attributes, - List parentLinks) { - return SamplingResult.recordAndSample(); - } - - @Override - public String getDescription() { - return "TestNoopSampler"; - } - - @Override - public String toString() { - return "TestNoopSampler"; - } - } -} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider deleted file mode 100644 index fd2a3e7b34e..00000000000 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider +++ /dev/null @@ -1 +0,0 @@ -io.opentelemetry.integrationtest.osgi.TestAutoConfigurationCustomizerProvider diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider deleted file mode 100644 index 49f70c84af6..00000000000 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider +++ /dev/null @@ -1 +0,0 @@ -io.opentelemetry.integrationtest.osgi.TestMetricReaderProvider diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider deleted file mode 100644 index 14663a9a1ab..00000000000 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider +++ /dev/null @@ -1 +0,0 @@ -io.opentelemetry.integrationtest.osgi.TestSamplerProvider diff --git a/sdk-extensions/declarative-config/build.gradle.kts b/sdk-extensions/declarative-config/build.gradle.kts index ba5f3b18494..441e18f0e7a 100644 --- a/sdk-extensions/declarative-config/build.gradle.kts +++ b/sdk-extensions/declarative-config/build.gradle.kts @@ -12,6 +12,9 @@ plugins { description = "OpenTelemetry SDK Declarative Config" otelJava.moduleName.set("io.opentelemetry.sdk.declarativeconfig") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { api(project(":sdk:all")) diff --git a/sdk-extensions/incubator/build.gradle.kts b/sdk-extensions/incubator/build.gradle.kts index 77bd6ef65a3..f81c6d1180b 100644 --- a/sdk-extensions/incubator/build.gradle.kts +++ b/sdk-extensions/incubator/build.gradle.kts @@ -9,6 +9,10 @@ plugins { description = "OpenTelemetry SDK Incubator" otelJava.moduleName.set("io.opentelemetry.sdk.extension.incubator") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { api(project(":sdk:all")) diff --git a/sdk-extensions/jaeger-remote-sampler/build.gradle.kts b/sdk-extensions/jaeger-remote-sampler/build.gradle.kts index 7852f360870..14b18cf8d05 100644 --- a/sdk-extensions/jaeger-remote-sampler/build.gradle.kts +++ b/sdk-extensions/jaeger-remote-sampler/build.gradle.kts @@ -9,6 +9,10 @@ plugins { description = "OpenTelemetry - Jaeger Remote sampler" otelJava.moduleName.set("io.opentelemetry.sdk.extension.trace.jaeger") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", +)) dependencies { api(project(":sdk:all")) diff --git a/sdk/testing/build.gradle.kts b/sdk/testing/build.gradle.kts index 44bbcad90d8..e68f1b26eeb 100644 --- a/sdk/testing/build.gradle.kts +++ b/sdk/testing/build.gradle.kts @@ -5,6 +5,9 @@ plugins { description = "OpenTelemetry SDK Testing utilities" otelJava.moduleName.set("io.opentelemetry.sdk.testing") +otelJava.osgiServiceLoaderProvides.set(listOf( + "io.opentelemetry.context.ContextStorageProvider", +)) dependencies { api(project(":api:all")) From 429e5777f25107fa585df80e278df16e542ef960 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Wed, 20 May 2026 15:26:48 -0500 Subject: [PATCH 3/6] Exercise DeclarativeConfigurationCustomizerProvider in osgi test --- integration-tests/osgi/build.gradle.kts | 3 + .../AutoconfigureDeclarativeConfigTest.java | 10 +++- ...rativeConfigurationCustomizerProvider.java | 59 +++++++++++++++++++ ...DeclarativeConfigurationCustomizerProvider | 1 + .../declarative-config/build.gradle.kts | 4 ++ 5 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestDeclarativeConfigurationCustomizerProvider.java create mode 100644 integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider diff --git a/integration-tests/osgi/build.gradle.kts b/integration-tests/osgi/build.gradle.kts index 7768d7cee43..8e2f7a122d9 100644 --- a/integration-tests/osgi/build.gradle.kts +++ b/integration-tests/osgi/build.gradle.kts @@ -288,6 +288,9 @@ val autoconfigureDeclarativeConfigSuiteTask = registerOsgiSuite( "opentelemetry-extension-trace-propagators", "opentelemetry-sdk-extension-declarative-config", ), + serviceLoaderProvides = listOf( + "io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider", + ), minJavaVersion = 11, ) { implementation(project(":sdk:all")) diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java index 56f1e8a8a8d..a3a497980c1 100644 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java @@ -105,9 +105,13 @@ void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { .setResource(resource) .addSpanProcessor( SimpleSpanProcessor.create( - OtlpHttpSpanExporter.builder() - .setComponentLoader(autoConfigureLoader) - .build())) + // TestDeclarativeConfigurationCustomizerProvider wraps + // OtlpHttpSpanExporter — proves the SPI was invoked. + new TestDeclarativeConfigurationCustomizerProvider + .TestCustomizedSpanExporter( + OtlpHttpSpanExporter.builder() + .setComponentLoader(autoConfigureLoader) + .build()))) .build()) .setMeterProvider( SdkMeterProvider.builder() diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestDeclarativeConfigurationCustomizerProvider.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestDeclarativeConfigurationCustomizerProvider.java new file mode 100644 index 00000000000..9554c0b7f33 --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/TestDeclarativeConfigurationCustomizerProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtest.osgi; + +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.util.Collection; + +/** + * Noop customizer provider that wraps the OTLP HTTP span exporter with a distinctive toString, + * making the customizer's invocation verifiable in the SDK toString comparison. + */ +public class TestDeclarativeConfigurationCustomizerProvider + implements DeclarativeConfigurationCustomizerProvider { + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + // SpanExporter.class matches all span exporters (OtlpHttpSpanExporter in this test). + // OtlpHttpSpanExporter is final so we can't use it as type bound for the wrapper return type. + customizer.addSpanExporterCustomizer( + SpanExporter.class, (exporter, props) -> new TestCustomizedSpanExporter(exporter)); + } + + /** Wraps a SpanExporter with a distinctive toString to prove the customizer was invoked. */ + public static final class TestCustomizedSpanExporter implements SpanExporter { + + private final SpanExporter delegate; + + public TestCustomizedSpanExporter(SpanExporter delegate) { + this.delegate = delegate; + } + + @Override + public CompletableResultCode export(Collection spans) { + return delegate.export(spans); + } + + @Override + public CompletableResultCode flush() { + return delegate.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + @Override + public String toString() { + return "TestDeclarativeCustomized{" + delegate + '}'; + } + } +} diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider new file mode 100644 index 00000000000..1135822d04a --- /dev/null +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider @@ -0,0 +1 @@ +io.opentelemetry.integrationtest.osgi.TestDeclarativeConfigurationCustomizerProvider diff --git a/sdk-extensions/declarative-config/build.gradle.kts b/sdk-extensions/declarative-config/build.gradle.kts index 441e18f0e7a..dc079e92b2b 100644 --- a/sdk-extensions/declarative-config/build.gradle.kts +++ b/sdk-extensions/declarative-config/build.gradle.kts @@ -15,6 +15,10 @@ otelJava.moduleName.set("io.opentelemetry.sdk.declarativeconfig") otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", )) +// declarative-config discovers customizer providers at runtime via ServiceLoader +otelJava.osgiServiceLoaderRequires.set(listOf( + "io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider", +)) dependencies { api(project(":sdk:all")) From 780b0198209eb45129acd140c31fe744b65e9614 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Wed, 20 May 2026 15:35:33 -0500 Subject: [PATCH 4/6] Tweaks --- .../opentelemetry/all/OsgiServiceLoaderManifestTest.java | 4 ++-- .../osgi/AutoconfigureDeclarativeConfigTest.java | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java b/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java index 8b90eec7caa..6d67ee67101 100644 --- a/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java +++ b/all/src/test/java/io/opentelemetry/all/OsgiServiceLoaderManifestTest.java @@ -93,8 +93,8 @@ private static List parseOsgiServiceLoaderCapabilities(String provideCap if (provideCapability == null || provideCapability.isEmpty()) { return result; } - // The header may be line-folded (continuation lines start with a space) — already unfolded - // by JarFile. Split on commas that are not inside quotes. + // JarFile already unfolds line-folded headers. Split into individual capability clauses + // on commas immediately followed by an OSGi namespace (osgi.*). String[] clauses = provideCapability.split(",(?=\\s*osgi\\.)"); for (String clause : clauses) { clause = clause.trim(); diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java index a3a497980c1..005fbfc9752 100644 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java @@ -78,8 +78,13 @@ void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { // System.setProperty is required: addPropertiesSupplier because property suppliers are not // resolved until after otel.config.file check System.setProperty("otel.config.file", configFile.toString()); - AutoConfiguredOpenTelemetrySdk autoConfigured = - AutoConfiguredOpenTelemetrySdk.builder().build(); + AutoConfiguredOpenTelemetrySdk autoConfigured; + try { + autoConfigured = AutoConfiguredOpenTelemetrySdk.builder().build(); + } catch (RuntimeException e) { + System.clearProperty("otel.config.file"); + throw e; + } ComponentLoader delegateLoader = ComponentLoader.forClassLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader()); From 2a5f5a7aa1f5dd91d180e2062418dfc811202d04 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Thu, 21 May 2026 08:51:41 -0500 Subject: [PATCH 5/6] Remove curly braces from declarative config --- .../osgi/AutoconfigureDeclarativeConfigTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java index 005fbfc9752..4a877494317 100644 --- a/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java +++ b/integration-tests/osgi/src/testAutoconfigureDeclarativeConfig/java/io/opentelemetry/integrationtest/osgi/AutoconfigureDeclarativeConfigTest.java @@ -52,7 +52,7 @@ void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { + " value: test-osgi-declarative\n" + " detection/development:\n" + " detectors:\n" - + " - service: {}\n" + + " - service:\n" + "propagator:\n" + " composite:\n" + " - tracecontext:\n" @@ -61,17 +61,17 @@ void declarativeConfigSdkInitializes(@TempDir Path tempDir) throws IOException { + " processors:\n" + " - simple:\n" + " exporter:\n" - + " otlp_http: {}\n" + + " otlp_http:\n" + "meter_provider:\n" + " readers:\n" + " - periodic:\n" + " exporter:\n" - + " otlp_http: {}\n" + + " otlp_http:\n" + "logger_provider:\n" + " processors:\n" + " - simple:\n" + " exporter:\n" - + " otlp_http: {}\n"; + + " otlp_http:\n"; Path configFile = tempDir.resolve("otel-config.yaml"); Files.write(configFile, yaml.getBytes(StandardCharsets.UTF_8)); From 07982c6c9d788218c64e877191741468e28b77c5 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:02:18 -0500 Subject: [PATCH 6/6] Move autoconfigure api to compileOnly dependency for all SPI implementation provider modules --- exporters/common/build.gradle.kts | 5 ++- .../exporter/internal/EndpointUtil.java | 37 +++++++++++++++++++ .../internal/ExporterBuilderUtil.java | 19 ---------- exporters/logging-otlp/build.gradle.kts | 4 +- exporters/logging/build.gradle.kts | 3 +- exporters/otlp/all/build.gradle.kts | 5 ++- .../otlp/internal/GrpcExporterBuilder.java | 4 +- .../otlp/internal/HttpExporterBuilder.java | 6 +-- exporters/prometheus/build.gradle.kts | 4 +- exporters/zipkin/build.gradle.kts | 4 +- extensions/trace-propagators/build.gradle.kts | 2 +- .../declarative-config/build.gradle.kts | 4 +- sdk-extensions/incubator/build.gradle.kts | 3 +- .../jaeger-remote-sampler/build.gradle.kts | 1 + .../sampler/JaegerRemoteSamplerBuilder.java | 4 +- 15 files changed, 68 insertions(+), 37 deletions(-) create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/EndpointUtil.java diff --git a/exporters/common/build.gradle.kts b/exporters/common/build.gradle.kts index d5ac6ff1473..c12a945698a 100644 --- a/exporters/common/build.gradle.kts +++ b/exporters/common/build.gradle.kts @@ -7,7 +7,7 @@ plugins { description = "OpenTelemetry Exporter Common" otelJava.moduleName.set("io.opentelemetry.exporter.internal") -otelJava.osgiOptionalPackages.set(listOf("com.fasterxml.jackson.core", "com.google.common.io", "io.opentelemetry.api.incubator.config")) +otelJava.osgiOptionalPackages.set(listOf("com.fasterxml.jackson.core", "com.google.common.io", "io.opentelemetry.api.incubator.config", "io.opentelemetry.sdk.autoconfigure.spi")) // sun.misc, io.grpc, and org.jspecify are not OSGi bundles and have no package versioning; must use unversioned optional. otelJava.osgiUnversionedOptionalPackages.set(listOf("sun.misc", "io.grpc", "org.jspecify.annotations")) // This bundle's exporters load sender implementations via SPI. @@ -59,7 +59,7 @@ if (javaVersion >= JavaVersion.VERSION_1_9) { val versions: Map by project dependencies { api(project(":api:all")) - api(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) compileOnly(project(":api:incubator")) compileOnly(project(":sdk:common")) @@ -96,6 +96,7 @@ testing { implementation(project(":exporters:sender:jdk")) implementation(project(":exporters:sender:okhttp")) implementation(project(":exporters:sender:grpc-managed-channel")) + implementation(project(":sdk:common")) } targets { all { diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/EndpointUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/EndpointUtil.java new file mode 100644 index 00000000000..c347d738edd --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/EndpointUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal; + +import java.net.URI; +import java.net.URISyntaxException; + +/** + * Utilities for validating exporter endpoints. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class EndpointUtil { + + /** Validate an exporter endpoint. */ + public static URI validateEndpoint(String endpoint) { + URI uri; + try { + uri = new URI(endpoint); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid endpoint, must be a URL: " + endpoint, e); + } + + if (uri.getScheme() == null + || (!uri.getScheme().equals("http") && !uri.getScheme().equals("https"))) { + throw new IllegalArgumentException( + "Invalid endpoint, must start with http:// or https://: " + uri); + } + return uri; + } + + private EndpointUtil() {} +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java index 9e93cc38ab3..7a6bcbf0fe9 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java @@ -16,8 +16,6 @@ import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Locale; import java.util.function.Consumer; @@ -29,23 +27,6 @@ */ public final class ExporterBuilderUtil { - /** Validate OTLP endpoint. */ - public static URI validateEndpoint(String endpoint) { - URI uri; - try { - uri = new URI(endpoint); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Invalid endpoint, must be a URL: " + endpoint, e); - } - - if (uri.getScheme() == null - || (!uri.getScheme().equals("http") && !uri.getScheme().equals("https"))) { - throw new IllegalArgumentException( - "Invalid endpoint, must start with http:// or https://: " + uri); - } - return uri; - } - /** Invoke the {@code memoryModeConsumer} with the configured {@link MemoryMode}. */ public static void configureExporterMemoryMode( ConfigProperties config, Consumer memoryModeConsumer) { diff --git a/exporters/logging-otlp/build.gradle.kts b/exporters/logging-otlp/build.gradle.kts index 1e9217386c5..bdec906e21c 100644 --- a/exporters/logging-otlp/build.gradle.kts +++ b/exporters/logging-otlp/build.gradle.kts @@ -7,6 +7,7 @@ plugins { description = "OpenTelemetry Protocol JSON Logging Exporters" otelJava.moduleName.set("io.opentelemetry.exporter.logging.otlp") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator", "io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", @@ -21,12 +22,13 @@ dependencies { implementation(project(":exporters:otlp:common")) compileOnly(project(":api:incubator")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) implementation("com.fasterxml.jackson.core:jackson-core") testImplementation(project(":api:incubator")) testImplementation(project(":sdk:testing")) + testImplementation(project(":sdk-extensions:autoconfigure-spi")) testImplementation("com.google.guava:guava") testImplementation("org.skyscreamer:jsonassert") diff --git a/exporters/logging/build.gradle.kts b/exporters/logging/build.gradle.kts index 27beed5564c..695fd5af92e 100644 --- a/exporters/logging/build.gradle.kts +++ b/exporters/logging/build.gradle.kts @@ -7,6 +7,7 @@ plugins { description = "OpenTelemetry - Logging Exporter" otelJava.moduleName.set("io.opentelemetry.exporter.logging") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator", "io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", @@ -17,7 +18,7 @@ otelJava.osgiServiceLoaderProvides.set(listOf( dependencies { api(project(":sdk:all")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) compileOnly(project(":api:incubator")) testImplementation(project(":sdk:testing")) diff --git a/exporters/otlp/all/build.gradle.kts b/exporters/otlp/all/build.gradle.kts index 3ab8bf3d499..d50db33c533 100644 --- a/exporters/otlp/all/build.gradle.kts +++ b/exporters/otlp/all/build.gradle.kts @@ -9,7 +9,7 @@ apply() description = "OpenTelemetry Protocol (OTLP) Exporters" otelJava.moduleName.set("io.opentelemetry.exporter.otlp") -otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator.config")) +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator.config", "io.opentelemetry.sdk.autoconfigure.spi")) // io.grpc and org.jspecify.annotations are not OSGi bundles; must use unversioned optional. otelJava.osgiUnversionedOptionalPackages.set(listOf("io.grpc", "org.jspecify.annotations")) otelJava.osgiServiceLoaderProvides.set(listOf( @@ -27,7 +27,7 @@ dependencies { implementation(project(":exporters:otlp:common")) implementation(project(":exporters:sender:okhttp")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) compileOnly(project(":api:incubator")) @@ -36,6 +36,7 @@ dependencies { compileOnly("io.grpc:grpc-stub") testImplementation(project(":exporters:otlp:testing-internal")) + testImplementation(project(":sdk-extensions:autoconfigure-spi")) testImplementation("com.linecorp.armeria:armeria-junit5") testImplementation("com.linecorp.armeria:armeria-grpc-protocol") testImplementation("io.grpc:grpc-stub") diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/GrpcExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/GrpcExporterBuilder.java index 411dabd7c21..ad5b9cbd0d1 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/GrpcExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/GrpcExporterBuilder.java @@ -11,7 +11,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.common.ComponentLoader; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.exporter.internal.EndpointUtil; import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.Compressor; @@ -99,7 +99,7 @@ public GrpcExporterBuilder setConnectTimeout(Duration timeout) { } public GrpcExporterBuilder setEndpoint(String endpoint) { - this.endpoint = ExporterBuilderUtil.validateEndpoint(endpoint); + this.endpoint = EndpointUtil.validateEndpoint(endpoint); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporterBuilder.java index 12586aff2ce..98a5a2e2b85 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporterBuilder.java @@ -8,7 +8,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.common.ComponentLoader; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.exporter.internal.EndpointUtil; import io.opentelemetry.exporter.internal.SenderUtil; import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.sdk.common.InternalTelemetryVersion; @@ -72,7 +72,7 @@ public final class HttpExporterBuilder { public HttpExporterBuilder( StandardComponentId.ExporterType exporterType, String defaultEndpoint) { - this(exporterType, ExporterBuilderUtil.validateEndpoint(defaultEndpoint)); + this(exporterType, EndpointUtil.validateEndpoint(defaultEndpoint)); } HttpExporterBuilder(StandardComponentId.ExporterType exporterType, URI endpoint) { @@ -91,7 +91,7 @@ public HttpExporterBuilder setConnectTimeout(Duration duration) { } public HttpExporterBuilder setEndpoint(String endpoint) { - this.endpoint = ExporterBuilderUtil.validateEndpoint(endpoint); + this.endpoint = EndpointUtil.validateEndpoint(endpoint); return this; } diff --git a/exporters/prometheus/build.gradle.kts b/exporters/prometheus/build.gradle.kts index bf6ce726f60..7cac2d74c84 100644 --- a/exporters/prometheus/build.gradle.kts +++ b/exporters/prometheus/build.gradle.kts @@ -5,6 +5,7 @@ plugins { description = "OpenTelemetry Prometheus Exporter" otelJava.moduleName.set("io.opentelemetry.exporter.prometheus") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator", "io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.internal.ConfigurableMetricReaderProvider", "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", @@ -15,7 +16,7 @@ dependencies { compileOnly(project(":api:incubator")) implementation(project(":exporters:common")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) implementation("io.prometheus:prometheus-metrics-exporter-httpserver") { exclude(group = "io.prometheus", module = "prometheus-metrics-exposition-formats") } @@ -27,6 +28,7 @@ dependencies { annotationProcessor("com.google.auto.value:auto-value") testImplementation(project(":sdk:testing")) + testImplementation(project(":sdk-extensions:autoconfigure-spi")) testImplementation("io.opentelemetry.proto:opentelemetry-proto") testImplementation("com.sun.net.httpserver:http") testImplementation("com.google.guava:guava") diff --git a/exporters/zipkin/build.gradle.kts b/exporters/zipkin/build.gradle.kts index a80484500ae..c94a85a176c 100644 --- a/exporters/zipkin/build.gradle.kts +++ b/exporters/zipkin/build.gradle.kts @@ -7,6 +7,7 @@ plugins { description = "OpenTelemetry - Zipkin Exporter" otelJava.moduleName.set("io.opentelemetry.exporter.zipkin") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", )) @@ -17,12 +18,13 @@ dependencies { api("io.zipkin.reporter2:zipkin-reporter") implementation(project(":exporters:common")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) compileOnly(project(":api:incubator")) implementation("io.zipkin.reporter2:zipkin-sender-okhttp3") testImplementation(project(":sdk:testing")) + testImplementation(project(":sdk-extensions:autoconfigure-spi")) testImplementation("com.linecorp.armeria:armeria") testImplementation("org.testcontainers:testcontainers-junit-jupiter") diff --git a/extensions/trace-propagators/build.gradle.kts b/extensions/trace-propagators/build.gradle.kts index 192e638b224..329ff056341 100644 --- a/extensions/trace-propagators/build.gradle.kts +++ b/extensions/trace-propagators/build.gradle.kts @@ -8,7 +8,7 @@ plugins { description = "OpenTelemetry Extension : Trace Propagators" otelJava.moduleName.set("io.opentelemetry.extension.trace.propagation") -otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator")) +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator", "io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider", "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", diff --git a/sdk-extensions/declarative-config/build.gradle.kts b/sdk-extensions/declarative-config/build.gradle.kts index dc079e92b2b..452e89d8c1e 100644 --- a/sdk-extensions/declarative-config/build.gradle.kts +++ b/sdk-extensions/declarative-config/build.gradle.kts @@ -12,6 +12,7 @@ plugins { description = "OpenTelemetry SDK Declarative Config" otelJava.moduleName.set("io.opentelemetry.sdk.declarativeconfig") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", )) @@ -23,7 +24,7 @@ otelJava.osgiServiceLoaderRequires.set(listOf( dependencies { api(project(":sdk:all")) api(project(":api:incubator")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) // Needed for composable samplers implementation(project(":sdk-extensions:incubator")) @@ -34,6 +35,7 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind") testImplementation(project(":sdk:testing")) + testImplementation(project(":sdk-extensions:autoconfigure-spi")) testImplementation(project(":exporters:logging")) testImplementation(project(":exporters:logging-otlp")) testImplementation(project(":exporters:otlp:all")) diff --git a/sdk-extensions/incubator/build.gradle.kts b/sdk-extensions/incubator/build.gradle.kts index f81c6d1180b..af02be590af 100644 --- a/sdk-extensions/incubator/build.gradle.kts +++ b/sdk-extensions/incubator/build.gradle.kts @@ -9,6 +9,7 @@ plugins { description = "OpenTelemetry SDK Incubator" otelJava.moduleName.set("io.opentelemetry.sdk.extension.incubator") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator", "io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", @@ -21,7 +22,7 @@ dependencies { compileOnly(project(":api:incubator")) - implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":sdk-extensions:autoconfigure-spi")) testImplementation(project(":sdk:testing")) testImplementation(project(":sdk-extensions:autoconfigure")) diff --git a/sdk-extensions/jaeger-remote-sampler/build.gradle.kts b/sdk-extensions/jaeger-remote-sampler/build.gradle.kts index 14b18cf8d05..831774de06b 100644 --- a/sdk-extensions/jaeger-remote-sampler/build.gradle.kts +++ b/sdk-extensions/jaeger-remote-sampler/build.gradle.kts @@ -9,6 +9,7 @@ plugins { description = "OpenTelemetry - Jaeger Remote sampler" otelJava.moduleName.set("io.opentelemetry.sdk.extension.trace.jaeger") +otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.sdk.autoconfigure.declarativeconfig", "io.opentelemetry.sdk.autoconfigure.spi")) otelJava.osgiServiceLoaderProvides.set(listOf( "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", diff --git a/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerBuilder.java b/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerBuilder.java index b382d997d64..2228956c47f 100644 --- a/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerBuilder.java +++ b/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerBuilder.java @@ -10,7 +10,7 @@ import io.grpc.ManagedChannel; import io.opentelemetry.api.internal.Utils; import io.opentelemetry.common.ComponentLoader; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.exporter.internal.EndpointUtil; import io.opentelemetry.exporter.internal.SenderUtil; import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.sdk.common.export.GrpcSender; @@ -74,7 +74,7 @@ public JaegerRemoteSamplerBuilder setServiceName(String serviceName) { */ public JaegerRemoteSamplerBuilder setEndpoint(String endpoint) { requireNonNull(endpoint, "endpoint"); - this.endpoint = ExporterBuilderUtil.validateEndpoint(endpoint); + this.endpoint = EndpointUtil.validateEndpoint(endpoint); return this; }