diff --git a/exporters/prometheus/build.gradle.kts b/exporters/prometheus/build.gradle.kts index 81ca190fa0b..166e34aa7ca 100644 --- a/exporters/prometheus/build.gradle.kts +++ b/exporters/prometheus/build.gradle.kts @@ -11,6 +11,8 @@ otelJava.moduleName.set("io.opentelemetry.exporter.prometheus") dependencies { api(project(":sdk:metrics")) + implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly("com.sun.net.httpserver:http") testImplementation("com.google.guava:guava") diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusCustomizerProvider.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusCustomizerProvider.java new file mode 100644 index 00000000000..f657e14eea7 --- /dev/null +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusCustomizerProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.prometheus.internal; + +import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; +import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; + +/** + * SPI implementation for {@link PrometheusHttpServer}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class PrometheusCustomizerProvider implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + autoConfiguration.addMeterProviderCustomizer( + (builder, config) -> { + boolean prometheusEnabled = + config.getList("otel.metrics.exporter").contains("prometheus"); + if (prometheusEnabled) { + builder.registerMetricReader(configurePrometheusHttpServer(config)); + } + return builder; + }); + } + + // Visible for test + static PrometheusHttpServer configurePrometheusHttpServer(ConfigProperties config) { + PrometheusHttpServerBuilder prometheusBuilder = PrometheusHttpServer.builder(); + + Integer port = config.getInt("otel.exporter.prometheus.port"); + if (port != null) { + prometheusBuilder.setPort(port); + } + String host = config.getString("otel.exporter.prometheus.host"); + if (host != null) { + prometheusBuilder.setHost(host); + } + return prometheusBuilder.build(); + } +} diff --git a/exporters/prometheus/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/exporters/prometheus/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider new file mode 100644 index 00000000000..1522bd5c348 --- /dev/null +++ b/exporters/prometheus/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -0,0 +1 @@ +io.opentelemetry.exporter.prometheus.internal.PrometheusCustomizerProvider \ No newline at end of file diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusCustomizerProviderTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusCustomizerProviderTest.java new file mode 100644 index 00000000000..69aa70072a2 --- /dev/null +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/internal/PrometheusCustomizerProviderTest.java @@ -0,0 +1,134 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.prometheus.internal; + +import static org.assertj.core.api.Assertions.as; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +import com.sun.net.httpserver.HttpServer; +import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class PrometheusCustomizerProviderTest { + + private static final PrometheusCustomizerProvider provider = new PrometheusCustomizerProvider(); + + private SdkMeterProviderBuilder meterProviderBuilder; + + @Mock private ConfigProperties configProperties; + + @Mock private AutoConfigurationCustomizer customizer; + + @BeforeEach + void setup() { + meterProviderBuilder = SdkMeterProvider.builder(); + doAnswer( + invocation -> { + BiFunction + meterProviderCustomizer = invocation.getArgument(0); + meterProviderBuilder = + meterProviderCustomizer.apply(meterProviderBuilder, configProperties); + return null; + }) + .when(customizer) + .addMeterProviderCustomizer(any()); + } + + @Test + void customize_PrometheusEnabled() { + when(configProperties.getList("otel.metrics.exporter")) + .thenReturn(Collections.singletonList("prometheus")); + provider.customize(customizer); + + try (SdkMeterProvider meterProvider = meterProviderBuilder.build()) { + assertThat(meterProvider) + .extracting("registeredReaders", as(InstanceOfAssertFactories.list(Object.class))) + .satisfiesExactly( + registeredReader -> + assertThat(registeredReader) + .extracting("metricReader") + .isInstanceOf(PrometheusHttpServer.class)); + } + } + + @Test + void customize_PrometheusDisabled() { + when(configProperties.getList("otel.metrics.exporter")) + .thenReturn(Collections.singletonList("foo")); + provider.customize(customizer); + + try (SdkMeterProvider meterProvider = meterProviderBuilder.build()) { + assertThat(meterProvider) + .extracting("registeredReaders", as(InstanceOfAssertFactories.list(Object.class))) + .isEmpty(); + } + } + + @Test + void configurePrometheusHttpServer_Default() { + try (PrometheusHttpServer prometheusHttpServer = + PrometheusCustomizerProvider.configurePrometheusHttpServer( + DefaultConfigProperties.createForTest(Collections.emptyMap()))) { + assertThat(prometheusHttpServer) + .extracting("server", as(InstanceOfAssertFactories.type(HttpServer.class))) + .satisfies( + server -> { + assertThat(server.getAddress().getHostName()).isEqualTo("0:0:0:0:0:0:0:0"); + assertThat(server.getAddress().getPort()).isEqualTo(9464); + }); + } + } + + @Test + void configurePrometheusHttpServer_WithConfiguration() throws IOException { + // Find a random unused port. There's a small race if another process takes it before we + // initialize. Consider adding retries to this test if it flakes, presumably it never will on + // CI since there's no prometheus there blocking the well-known port. + int port; + try (ServerSocket socket2 = new ServerSocket(0)) { + port = socket2.getLocalPort(); + } + + Map config = new HashMap<>(); + config.put("otel.exporter.prometheus.host", "localhost"); + config.put("otel.exporter.prometheus.port", String.valueOf(port)); + + try (PrometheusHttpServer prometheusHttpServer = + PrometheusCustomizerProvider.configurePrometheusHttpServer( + DefaultConfigProperties.createForTest(config))) { + assertThat(prometheusHttpServer) + .extracting("server", as(InstanceOfAssertFactories.type(HttpServer.class))) + .satisfies( + server -> { + assertThat(server.getAddress().getHostName()).isEqualTo("localhost"); + assertThat(server.getAddress().getPort()).isEqualTo(port); + }); + } + } +} diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index 834439ef1cd..37134675c47 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -14,8 +14,6 @@ dependencies { implementation(project(":semconv")) - compileOnly(project(":exporters:prometheus")) - annotationProcessor("com.google.auto.value:auto-value") testImplementation(project(":sdk:trace-shaded-deps")) @@ -60,7 +58,6 @@ testing { implementation(project(":exporters:logging")) implementation(project(":exporters:otlp:all")) implementation(project(":exporters:otlp:logs")) - implementation(project(":exporters:prometheus")) implementation(project(":exporters:zipkin")) } } @@ -129,23 +126,6 @@ testing { runtimeOnly("io.grpc:grpc-netty-shaded") } } - val testPrometheus by registering(JvmTestSuite::class) { - dependencies { - implementation(project(":exporters:prometheus")) - - implementation("com.linecorp.armeria:armeria-junit5") - } - - targets { - all { - testTask { - environment("OTEL_TRACES_EXPORTER", "none") - environment("OTEL_METRICS_EXPORTER", "prometheus") - environment("OTEL_METRIC_EXPORT_INTERVAL", "10") - } - } - } - } } } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ClasspathUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ClasspathUtil.java deleted file mode 100644 index 82d67de1145..00000000000 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ClasspathUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.sdk.autoconfigure; - -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; - -final class ClasspathUtil { - - @SuppressWarnings("UnusedException") - static void checkClassExists(String className, String featureName, String requiredLibrary) { - try { - Class.forName(className); - } catch (ClassNotFoundException unused) { - throw new ConfigurationException( - featureName - + " enabled but " - + requiredLibrary - + " not found on classpath. " - + "Make sure to add it as a dependency to enable this feature."); - } - } - - private ClasspathUtil() {} -} diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MeterProviderConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MeterProviderConfiguration.java index fac7c3ac2bf..53f986a8564 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MeterProviderConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MeterProviderConfiguration.java @@ -15,6 +15,7 @@ import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -70,6 +71,7 @@ static List configureMetricReaders( exporterName -> MetricExporterConfiguration.configureReader( exporterName, config, serviceClassLoader, metricExporterCustomizer)) + .filter(Objects::nonNull) .collect(Collectors.toList()); } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java index eec05b99efd..cb9411eeb7f 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java @@ -5,8 +5,6 @@ package io.opentelemetry.sdk.autoconfigure; -import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; -import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; @@ -17,6 +15,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.BiFunction; +import javax.annotation.Nullable; final class MetricExporterConfiguration { @@ -30,6 +29,7 @@ final class MetricExporterConfiguration { EXPORTER_ARTIFACT_ID_BY_NAME.put("otlp", "opentelemetry-exporter-otlp"); } + @Nullable static MetricReader configureReader( String name, ConfigProperties config, @@ -37,7 +37,16 @@ static MetricReader configureReader( BiFunction metricExporterCustomizer) { if (name.equals("prometheus")) { - return configurePrometheusMetricReader(config); + // PrometheusHttpServer is implemented as MetricReader (not MetricExporter) and uses + // the AutoConfigurationCustomizer#addMeterProviderCustomizer SPI hook instead of + // ConfigurableMetricExporterProvider. While the prometheus SPI hook is not handled here, + // the classpath check here provides uniform exception messages. + try { + Class.forName("io.opentelemetry.exporter.prometheus.PrometheusHttpServer"); + return null; + } catch (ClassNotFoundException unused) { + throw missingExporterException("prometheus", "opentelemetry-exporter-prometheus"); + } } NamedSpiManager spiExportersManager = @@ -45,7 +54,10 @@ static MetricReader configureReader( MetricExporter metricExporter = configureExporter(name, spiExportersManager); metricExporter = metricExporterCustomizer.apply(metricExporter, config); - return configurePeriodicMetricReader(config, metricExporter); + + return PeriodicMetricReader.builder(metricExporter) + .setInterval(config.getDuration("otel.metric.export.interval", DEFAULT_EXPORT_INTERVAL)) + .build(); } // Visible for testing @@ -66,42 +78,21 @@ static MetricExporter configureExporter( if (metricExporter == null) { String artifactId = EXPORTER_ARTIFACT_ID_BY_NAME.get(name); if (artifactId != null) { - throw new ConfigurationException( - "otel.metrics.exporter set to \"" - + name - + "\" but " - + artifactId - + " not found on classpath. Make sure to add it as a dependency."); + throw missingExporterException(name, artifactId); } throw new ConfigurationException("Unrecognized value for otel.metrics.exporter: " + name); } return metricExporter; } - private static PeriodicMetricReader configurePeriodicMetricReader( - ConfigProperties config, MetricExporter exporter) { - - return PeriodicMetricReader.builder(exporter) - .setInterval(config.getDuration("otel.metric.export.interval", DEFAULT_EXPORT_INTERVAL)) - .build(); - } - - private static PrometheusHttpServer configurePrometheusMetricReader(ConfigProperties config) { - ClasspathUtil.checkClassExists( - "io.opentelemetry.exporter.prometheus.PrometheusHttpServer", - "Prometheus Metrics Server", - "opentelemetry-exporter-prometheus"); - PrometheusHttpServerBuilder prom = PrometheusHttpServer.builder(); - - Integer port = config.getInt("otel.exporter.prometheus.port"); - if (port != null) { - prom.setPort(port); - } - String host = config.getString("otel.exporter.prometheus.host"); - if (host != null) { - prom.setHost(host); - } - return prom.build(); + private static ConfigurationException missingExporterException( + String exporterName, String artifactId) { + return new ConfigurationException( + "otel.metrics.exporter set to \"" + + exporterName + + "\" but " + + artifactId + + " not found on classpath. Make sure to add it as a dependency."); } private MetricExporterConfiguration() {} diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java index 48a9138d420..c5d6f5187d4 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.autoconfigure; import static io.opentelemetry.sdk.autoconfigure.MetricExporterConfiguration.configureExporter; +import static io.opentelemetry.sdk.autoconfigure.MetricExporterConfiguration.configureReader; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -20,6 +21,21 @@ class MetricExporterConfigurationTest { private static final ConfigProperties EMPTY = DefaultConfigProperties.createForTest(Collections.emptyMap()); + @Test + void configureReader_PrometheusNotOnClasspath() { + assertThatThrownBy( + () -> + configureReader( + "prometheus", + EMPTY, + MetricExporterConfiguration.class.getClassLoader(), + (a, b) -> a)) + .isInstanceOf(ConfigurationException.class) + .hasMessage( + "otel.metrics.exporter set to \"prometheus\" but opentelemetry-exporter-prometheus" + + " not found on classpath. Make sure to add it as a dependency."); + } + @Test void configureExporter_KnownSpiExportersNotOnClasspath() { NamedSpiManager spiExportersManager = @@ -46,19 +62,4 @@ void configureExporter_KnownSpiExportersNotOnClasspath() { assertThatThrownBy(() -> configureExporter("foo", spiExportersManager)) .hasMessage("Unrecognized value for otel.metrics.exporter: foo"); } - - @Test - void configureReader_Prometheus() { - assertThatThrownBy( - () -> - MetricExporterConfiguration.configureReader( - "prometheus", - EMPTY, - MetricExporterConfigurationTest.class.getClassLoader(), - (a, unused) -> a)) - .isInstanceOf(ConfigurationException.class) - .hasMessageContaining( - "Prometheus Metrics Server enabled but opentelemetry-exporter-prometheus not found on " - + "classpath"); - } } diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java index bdfb28aa9c3..319d8c9bd7e 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ConfigurableMetricExporterTest.java @@ -10,7 +10,6 @@ import com.google.common.collect.ImmutableMap; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; -import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; @@ -105,7 +104,7 @@ void defaultExporter() { void configureMultipleMetricExporters() { ConfigProperties config = DefaultConfigProperties.createForTest( - ImmutableMap.of("otel.metrics.exporter", "otlp,prometheus")); + ImmutableMap.of("otel.metrics.exporter", "otlp,logging")); assertThat( MeterProviderConfiguration.configureMetricReaders( @@ -114,7 +113,7 @@ void configureMultipleMetricExporters() { (metricExporter, unused) -> metricExporter)) .hasSize(2) .hasAtLeastOneElementOfType(PeriodicMetricReader.class) - .hasAtLeastOneElementOfType(PrometheusHttpServer.class) + .hasAtLeastOneElementOfType(PeriodicMetricReader.class) .allSatisfy(metricReader -> metricReader.shutdown().join(10, TimeUnit.SECONDS)); } } diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java index cd00addb2f6..dcec4c24042 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfigurationTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.autoconfigure; +import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -12,20 +13,73 @@ import io.opentelemetry.exporter.logging.LoggingMetricExporter; import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricReader; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; class MetricExporterConfigurationTest { + private static final ConfigProperties EMPTY = + DefaultConfigProperties.createForTest(Collections.emptyMap()); + + @Test + void configureReader_PrometheusOnClasspath() { + assertThat( + MetricExporterConfiguration.configureReader( + "prometheus", + EMPTY, + MetricExporterConfigurationTest.class.getClassLoader(), + (a, b) -> a)) + .isNull(); + } + + /** + * Prometheus uses the {@link AutoConfigurationCustomizerProvider} SPI instead of {@link + * ConfigurableMetricExporterProvider} because it is implemented as a {@link MetricReader}. While + * {@link MetricExporterConfiguration} does not load this SPI, the test code lives here alongside + * tests of the other known SPI metric exporters. + */ + @Test + void autoConfiguredOpenTelemetrySdk_PrometheusOnClasspath() { + Map config = new HashMap<>(); + config.put("otel.traces.exporter", "none"); + config.put("otel.metrics.exporter", "prometheus"); + config.put("otel.logs.exporter", "none"); + + OpenTelemetrySdk sdk = + AutoConfiguredOpenTelemetrySdk.builder() + .setResultAsGlobal(false) + .setConfig(DefaultConfigProperties.createForTest(config)) + .build() + .getOpenTelemetrySdk(); + try (SdkMeterProvider meterProvider = sdk.getSdkMeterProvider()) { + assertThat(meterProvider) + .extracting("registeredReaders", as(InstanceOfAssertFactories.list(Object.class))) + .satisfiesExactly( + registeredReader -> + assertThat(registeredReader) + .extracting("metricReader") + .isInstanceOf(PrometheusHttpServer.class)); + } + } + @Test void configureExporter_KnownSpiExportersOnClasspath() { NamedSpiManager spiExportersManager = MetricExporterConfiguration.metricExporterSpiManager( - DefaultConfigProperties.createForTest(Collections.emptyMap()), - ConfigurableMetricExporterTest.class.getClassLoader()); + EMPTY, ConfigurableMetricExporterTest.class.getClassLoader()); assertThat(MetricExporterConfiguration.configureExporter("logging", spiExportersManager)) .isInstanceOf(LoggingMetricExporter.class); diff --git a/sdk-extensions/autoconfigure/src/testPrometheus/java/io/opentelemetry/sdk/autoconfigure/PrometheusTest.java b/sdk-extensions/autoconfigure/src/testPrometheus/java/io/opentelemetry/sdk/autoconfigure/PrometheusTest.java deleted file mode 100644 index 0655f731557..00000000000 --- a/sdk-extensions/autoconfigure/src/testPrometheus/java/io/opentelemetry/sdk/autoconfigure/PrometheusTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.sdk.autoconfigure; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.common.AggregatedHttpResponse; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import java.io.IOException; -import java.net.ServerSocket; -import org.junit.jupiter.api.Test; - -class PrometheusTest { - - @Test - void prometheusExporter() throws Exception { - int port = 9464; - // Just use prometheus standard port if it's available - try (ServerSocket unused = new ServerSocket(port)) { - // Port available - } catch (IOException e) { - // Otherwise use a random port. There's a small race if another process takes it before we - // initialize. Consider adding retries to this test if it flakes, presumably it never will on - // CI since there's no prometheus there blocking the well-known port. - try (ServerSocket socket2 = new ServerSocket(0)) { - port = socket2.getLocalPort(); - } - } - System.setProperty("otel.exporter.prometheus.host", "127.0.0.1"); - System.setProperty("otel.exporter.prometheus.port", String.valueOf(port)); - OpenTelemetrySdk openTelemetrySdk = - AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(); - - openTelemetrySdk - .getMeterProvider() - .get("test") - .gaugeBuilder("test") - .ofLongs() - .buildWithCallback(result -> result.record(2, Attributes.empty())); - - WebClient client = WebClient.of("http://127.0.0.1:" + port); - AggregatedHttpResponse response = client.get("/metrics").aggregate().join(); - assertThat(response.contentUtf8()).contains("test 2.0"); - } -}