From a6a7d0326b7794905a28ee428bc9b8d9cca8a8ca Mon Sep 17 00:00:00 2001 From: brunobat Date: Tue, 29 Apr 2025 17:18:16 +0100 Subject: [PATCH] Disable logger exporter metrics if metrics disabled. Clarify documentation of properties enabling OTel logs and metrics --- .../deployment/OpenTelemetryProcessor.java | 15 ++ ...uredOpenTelemetrySdkBuilderCustomizer.java | 37 ++-- .../runtime/config/build/LogsBuildConfig.java | 5 +- .../config/build/MetricsBuildConfig.java | 5 +- .../config/build/TracesBuildConfig.java | 5 +- .../opentelemetry-exporter-logging/pom.xml | 170 ++++++++++++++++++ .../main/java/org/acme/GreetingResource.java | 19 ++ .../src/main/resources/application.properties | 4 + .../org/acme/LoggingMetricsNotPresentIT.java | 11 ++ .../acme/LoggingMetricsNotPresentTest.java | 79 ++++++++ integration-tests/pom.xml | 1 + 11 files changed, 335 insertions(+), 16 deletions(-) create mode 100644 integration-tests/opentelemetry-exporter-logging/pom.xml create mode 100644 integration-tests/opentelemetry-exporter-logging/src/main/java/org/acme/GreetingResource.java create mode 100644 integration-tests/opentelemetry-exporter-logging/src/main/resources/application.properties create mode 100644 integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentIT.java create mode 100644 integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentTest.java diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java index 116f90d2f32b6..a81dc737a9224 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java @@ -1,5 +1,6 @@ package io.quarkus.opentelemetry.deployment; +import static io.quarkus.bootstrap.classloading.QuarkusClassLoader.isClassPresentAtRuntime; import static io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem.SPI_ROOT; import static io.quarkus.opentelemetry.runtime.OpenTelemetryRecorder.OPEN_TELEMETRY_DRIVER; import static java.util.stream.Collectors.toList; @@ -60,11 +61,13 @@ import io.quarkus.deployment.builditem.RemovedResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.util.ServiceUtil; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.opentelemetry.OpenTelemetryDestroyer; +import io.quarkus.opentelemetry.deployment.metric.MetricProcessor; import io.quarkus.opentelemetry.runtime.AutoConfiguredOpenTelemetrySdkBuilderCustomizer; import io.quarkus.opentelemetry.runtime.DelayedAttributes; import io.quarkus.opentelemetry.runtime.OpenTelemetryRecorder; @@ -94,6 +97,18 @@ public boolean test(AnnotationInstance annotationInstance) { private static final DotName ADD_SPAN_ATTRIBUTES_INTERCEPTOR = DotName .createSimple(AddingSpanAttributesInterceptor.class.getName()); + @BuildStep(onlyIfNot = MetricProcessor.MetricEnabled.class) + void registerForReflection(BuildProducer reflectiveItem) { + if (isClassPresentAtRuntime( + "io.opentelemetry.exporter.logging.LoggingMetricExporter")) { + reflectiveItem.produce(new ReflectiveMethodBuildItem( + "Used by OpenTelemetry Export Logging", + false, + "io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil", + "addMeterConfiguratorCondition")); + } + } + @BuildStep AdditionalBeanBuildItem ensureProducerIsRetained() { return AdditionalBeanBuildItem.builder() diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java index 011efa6b4f8f6..40191b99f1594 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java @@ -1,5 +1,6 @@ package io.quarkus.opentelemetry.runtime; +import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals; import static java.lang.Boolean.TRUE; import static java.util.Collections.emptyList; @@ -25,6 +26,8 @@ import io.opentelemetry.sdk.logs.export.LogRecordExporter; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.internal.MeterConfig; +import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.IdGenerator; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; @@ -258,20 +261,34 @@ public MetricProviderCustomizer(OTelBuildConfig oTelBuildConfig, @Override public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) { - if (oTelBuildConfig.metrics().enabled().orElse(TRUE)) { - builder.addMeterProviderCustomizer( - new BiFunction() { - @Override - public SdkMeterProviderBuilder apply(SdkMeterProviderBuilder metricProvider, - ConfigProperties configProperties) { + builder.addMeterProviderCustomizer( + new BiFunction() { + @Override + public SdkMeterProviderBuilder apply(SdkMeterProviderBuilder meterProviderBuilder, + ConfigProperties configProperties) { + + if (oTelBuildConfig.metrics().enabled().orElse(TRUE)) { + if (clock.isUnsatisfied()) { throw new IllegalStateException("No Clock bean found"); } - metricProvider.setClock(clock.get()); - return metricProvider; + meterProviderBuilder.setClock(clock.get()); + } else { + // disable batch exporter metrics if metrics disabled + // In the future we can inject scopes here. + Set dropInstrumentationScopes = Set.of( + "io.opentelemetry.sdk.trace", + "io.opentelemetry.sdk.logs"); + for (String target : dropInstrumentationScopes) { + SdkMeterProviderUtil.addMeterConfiguratorCondition( + meterProviderBuilder, + nameEquals(target), + MeterConfig.disabled()); + } } - }); - } + return meterProviderBuilder; + } + }); } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/LogsBuildConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/LogsBuildConfig.java index 6c81a56be0c3f..038d33524a0ad 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/LogsBuildConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/LogsBuildConfig.java @@ -13,8 +13,9 @@ public interface LogsBuildConfig { *

* This property is not available in the Open Telemetry SDK. It's Quarkus specific. *

- * Support for logs will be enabled if OpenTelemetry support is enabled - * and either this value is true, or this value is unset. + * Support for OpenTelemetry Logs will be enabled if this value is true. + * The OpenTelemetry SDK ( {@link io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig#enabled()} ) + * is enabled by default and if disabled, OpenTelemetry Logs will also be disabled. */ @WithDefault("false") Optional enabled(); diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java index eb58074ef2e77..4e0b3ce50de9f 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java @@ -14,8 +14,9 @@ public interface MetricsBuildConfig { *

* This property is not available in the Open Telemetry SDK. It's Quarkus specific. *

- * Support for metrics will be enabled if OpenTelemetry support is enabled - * and either this value is true, or this value is unset. + * Support for OpenTelemetry Metrics will be enabled if this value is true. + * The OpenTelemetry SDK ( {@link io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig#enabled()} ) + * is enabled by default and if disabled, OpenTelemetry Metrics will also be disabled. */ @WithDefault("false") Optional enabled(); diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/TracesBuildConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/TracesBuildConfig.java index d387253747d62..6d67c746a4292 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/TracesBuildConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/TracesBuildConfig.java @@ -20,8 +20,9 @@ public interface TracesBuildConfig { *

* This property is not available in the Open Telemetry SDK. It's Quarkus specific. *

- * Support for tracing will be enabled if OpenTelemetry support is enabled - * and either this value is true, or this value is unset. + * Support for OpenTelemetry Tracing will be enabled if this value is true. + * The OpenTelemetry SDK ( {@link io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig#enabled()} ) + * is enabled by default and if disabled, OpenTelemetry Tracing will also be disabled. */ @Deprecated @WithDefault("true") diff --git a/integration-tests/opentelemetry-exporter-logging/pom.xml b/integration-tests/opentelemetry-exporter-logging/pom.xml new file mode 100644 index 0000000000000..a16777018f8f2 --- /dev/null +++ b/integration-tests/opentelemetry-exporter-logging/pom.xml @@ -0,0 +1,170 @@ + + + + 4.0.0 + + + io.quarkus + quarkus-integration-tests-parent + 999-SNAPSHOT + + + quarkus-integration-test-opentelemetry-exporter-logging + Quarkus - Integration Tests - OpenTelemetry exporter logging + + + + io.quarkus + quarkus-rest-jackson + + + io.quarkus + quarkus-opentelemetry + + + io.opentelemetry + opentelemetry-exporter-logging + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-junit5 + test + + + org.assertj + assertj-core + test + + + org.awaitility + awaitility + test + + + io.rest-assured + rest-assured + test + + + + io.quarkus + quarkus-arc-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-rest-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + false + + + + + + + + + native-image + + + native + + + + + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + false + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + false + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + diff --git a/integration-tests/opentelemetry-exporter-logging/src/main/java/org/acme/GreetingResource.java b/integration-tests/opentelemetry-exporter-logging/src/main/java/org/acme/GreetingResource.java new file mode 100644 index 0000000000000..2c46088d48cf1 --- /dev/null +++ b/integration-tests/opentelemetry-exporter-logging/src/main/java/org/acme/GreetingResource.java @@ -0,0 +1,19 @@ +package org.acme; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.quarkus.logging.Log; + +@Path("/hello") +public class GreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + Log.info("request received"); + return "Hello from Quarkus REST"; + } +} diff --git a/integration-tests/opentelemetry-exporter-logging/src/main/resources/application.properties b/integration-tests/opentelemetry-exporter-logging/src/main/resources/application.properties new file mode 100644 index 0000000000000..5536c28288e47 --- /dev/null +++ b/integration-tests/opentelemetry-exporter-logging/src/main/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.application.name=myservice +quarkus.otel.metrics.exporter=logging +# Force quick output of metrics to increase test reliability. +quarkus.otel.metric.export.interval=100ms \ No newline at end of file diff --git a/integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentIT.java b/integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentIT.java new file mode 100644 index 0000000000000..6d40d57c1a478 --- /dev/null +++ b/integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentIT.java @@ -0,0 +1,11 @@ +package org.acme; + +import org.junit.jupiter.api.Disabled; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@Disabled("Manual only") +@QuarkusIntegrationTest +class LoggingMetricsNotPresentIT extends LoggingMetricsNotPresentTest { + // Execute the same tests but in packaged mode. +} diff --git a/integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentTest.java b/integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentTest.java new file mode 100644 index 0000000000000..8ad6265f1825a --- /dev/null +++ b/integration-tests/opentelemetry-exporter-logging/src/test/java/org/acme/LoggingMetricsNotPresentTest.java @@ -0,0 +1,79 @@ +package org.acme; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.is; + +import java.util.ArrayList; +import java.util.logging.Handler; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class LoggingMetricsNotPresentTest { + + @Test + void testHelloEndpoint() throws InterruptedException { + + final TestingJulHandler julHandler = new TestingJulHandler(); + final Logger rootLogger = LogManager.getLogManager().getLogger(""); + rootLogger.addHandler(julHandler); + + Thread.sleep(100);// give time to export metrics records (but must not be there) + + given() + .when().get("/hello") + .then() + .statusCode(200) + .body(is("Hello from Quarkus REST")); + + await().untilAsserted(() -> { + assertThat(julHandler.logRecords).hasSizeGreaterThanOrEqualTo(1); + }); + + ArrayList logRecords = julHandler.logRecords; + rootLogger.removeHandler(julHandler); + + assertThat(logRecords.stream() + .anyMatch(logRecord -> logRecord.getLoggerName().startsWith(GreetingResource.class.getName()))) + .as("Log line from the service must be logged") + .isTrue(); + // Only present if opentelemetry-exporter-logging is used + // But we are turning it off if metrics are disabled + assertThat(logRecords.stream() + .noneMatch(logRecord -> logRecord.getLoggerName() + .startsWith("io.opentelemetry.exporter.logging.LoggingMetricExporter"))) + .as("Log lines from the OTel logging metrics exporter must NOT be logged") + .isTrue(); + } + + private static class TestingJulHandler extends Handler { + + private final ArrayList logRecords = new ArrayList<>(); + + public ArrayList getLogRecords() { + return logRecords; + } + + @Override + public void publish(LogRecord record) { + logRecords.add(record); + } + + @Override + public void flush() { + // Do nothing + } + + @Override + public void close() throws SecurityException { + // Do nothing + } + } +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 6344f5e879177..bcd41ab980fe2 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -377,6 +377,7 @@ opentelemetry-vertx-exporter opentelemetry-reactive-messaging opentelemetry-redis-instrumentation + opentelemetry-exporter-logging logging-json jaxb jaxp