From 5c6f8e0cea66e644cb6bff78002507ed8bf90180 Mon Sep 17 00:00:00 2001 From: brunobat Date: Thu, 22 Aug 2024 16:02:40 +0100 Subject: [PATCH] 1st deployment test --- extensions/opentelemetry/deployment/pom.xml | 15 ++ .../common/InMemoryLogRecordExporter.java | 67 +++++++++ .../InMemoryLogRecordExporterProvider.java | 20 +++ .../deployment/logs/LoggingFrameworkTest.java | 138 ++++++++++++++++++ ...logs.ConfigurableLogRecordExporterProvider | 1 + .../resources/application-default.properties | 8 +- .../runtime/logs/OpenTelemetryLogHandler.java | 8 +- 7 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporter.java create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporterProvider.java create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/LoggingFrameworkTest.java create mode 100644 extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider diff --git a/extensions/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/deployment/pom.xml index 9ba18f7033975..cac0090542ced 100644 --- a/extensions/opentelemetry/deployment/pom.xml +++ b/extensions/opentelemetry/deployment/pom.xml @@ -140,6 +140,21 @@ quarkus-security-deployment test + + io.opentelemetry + opentelemetry-sdk-testing + test + + + org.jboss.slf4j + slf4j-jboss-logmanager + test + + + org.jboss.logmanager + log4j2-jboss-logmanager + test + diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporter.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporter.java new file mode 100644 index 0000000000000..e07f6cfd506cc --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporter.java @@ -0,0 +1,67 @@ +package io.quarkus.opentelemetry.deployment.common; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.awaitility.Awaitility; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.export.LogRecordExporter; +import io.quarkus.arc.Unremovable; + +@Unremovable +@ApplicationScoped +public class InMemoryLogRecordExporter implements LogRecordExporter { + final Queue finishedLogItems = new ConcurrentLinkedQueue(); + boolean isStopped = false; + + private InMemoryLogRecordExporter() { + } + + public static InMemoryLogRecordExporter create() { + return new InMemoryLogRecordExporter(); + } + + public List getFinishedLogRecordItemsAtLeast(final int count) { + Awaitility.await().atMost(5, SECONDS) + .untilAsserted(() -> assertThat(getFinishedLogRecordItems().size()).isGreaterThanOrEqualTo(count)); + return getFinishedLogRecordItems(); + } + + private List getFinishedLogRecordItems() { + return Collections.unmodifiableList(new ArrayList(this.finishedLogItems)); + } + + public void reset() { + this.finishedLogItems.clear(); + } + + public CompletableResultCode export(Collection logs) { + if (this.isStopped) { + return CompletableResultCode.ofFailure(); + } else { + this.finishedLogItems.addAll(logs); + return CompletableResultCode.ofSuccess(); + } + } + + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + public CompletableResultCode shutdown() { + this.isStopped = true; + this.finishedLogItems.clear(); + return CompletableResultCode.ofSuccess(); + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporterProvider.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporterProvider.java new file mode 100644 index 0000000000000..9895a32ea9465 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryLogRecordExporterProvider.java @@ -0,0 +1,20 @@ +package io.quarkus.opentelemetry.deployment.common; + +import jakarta.enterprise.inject.spi.CDI; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider; +import io.opentelemetry.sdk.logs.export.LogRecordExporter; +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; + +public class InMemoryLogRecordExporterProvider implements ConfigurableLogRecordExporterProvider { + @Override + public LogRecordExporter createExporter(ConfigProperties configProperties) { + return CDI.current().select(InMemoryLogRecordExporter.class).get(); + } + + @Override + public String getName() { + return "in-memory"; + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/LoggingFrameworkTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/LoggingFrameworkTest.java new file mode 100644 index 0000000000000..952d82bb35a5f --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/LoggingFrameworkTest.java @@ -0,0 +1,138 @@ +package io.quarkus.opentelemetry.deployment.logs; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.jboss.logging.Logger; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.quarkus.opentelemetry.deployment.common.InMemoryLogRecordExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryLogRecordExporterProvider; +import io.quarkus.test.QuarkusUnitTest; + +public class LoggingFrameworkTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addClasses(JBossLoggingBean.class, SLF4JBean.class, JulBean.class, Log4j2Bean.class) + .addClasses(InMemoryLogRecordExporter.class, InMemoryLogRecordExporterProvider.class) + .addAsResource(new StringAsset(InMemoryLogRecordExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider") + .add(new StringAsset( + "quarkus.otel.logs.enabled=true\n" + + "quarkus.otel.traces.enabled=false\n"), + "application.properties")); + + @Inject + InMemoryLogRecordExporter logRecordExporter; + + @Inject + JBossLoggingBean jBossLoggingBean; + + @Inject + SLF4JBean slf4jBean; + + @Inject + JulBean julBean; + + @Inject + Log4j2Bean log4j2Bean; + + @BeforeEach + void setup() { + logRecordExporter.reset(); + } + + @Test + public void testJBossLogging() { + final String message = "JBoss Logging message"; + assertEquals("hello", jBossLoggingBean.hello(message)); + List finishedLogRecordItems = logRecordExporter.getFinishedLogRecordItemsAtLeast(1); + LogRecordData last = finishedLogRecordItems.get(finishedLogRecordItems.size() - 1); + assertThat(last.getBody().asString()).isEqualTo(message); + } + + @Test + public void testSLF4JLogging() { + final String message = "SLF4J Logging message"; + assertEquals("hello", slf4jBean.hello(message)); + List finishedLogRecordItems = logRecordExporter.getFinishedLogRecordItemsAtLeast(1); + LogRecordData last = finishedLogRecordItems.get(finishedLogRecordItems.size() - 1); + assertThat(last.getBody().asString()).isEqualTo(message); + } + + @Test + public void testLog4jLogging() { + final String message = "Log4j Logging message"; + assertEquals("hello", log4j2Bean.hello(message)); + List finishedLogRecordItems = logRecordExporter.getFinishedLogRecordItemsAtLeast(1); + LogRecordData last = finishedLogRecordItems.get(finishedLogRecordItems.size() - 1); + assertThat(last.getBody().asString()).isEqualTo(message); + } + + @Test + public void testJulLogging() { + final String message = "JUL Logging message"; + assertEquals("hello", julBean.hello(message)); + List finishedLogRecordItems = logRecordExporter.getFinishedLogRecordItemsAtLeast(1); + LogRecordData last = finishedLogRecordItems.get(finishedLogRecordItems.size() - 1); + assertThat(last.getBody().asString()).isEqualTo(message); + } + + @ApplicationScoped + public static class JBossLoggingBean { + private static final Logger LOG = Logger.getLogger(JBossLoggingBean.class.getName()); + + public String hello(final String message) { + LOG.info(message); + return "hello"; + } + } + + @ApplicationScoped + public static class SLF4JBean { + // using the logger adapter: https://quarkus.io/guides/logging#add-a-logging-adapter-to-your-application + private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(SLF4JBean.class); + + public String hello(final String message) { + LOG.info(message); + return "hello"; + } + } + + @ApplicationScoped + public static class Log4j2Bean { + // using the logger adapter: https://quarkus.io/guides/logging#add-a-logging-adapter-to-your-application + private static final org.apache.logging.log4j.Logger LOG = org.apache.logging.log4j.LogManager + .getLogger(Log4j2Bean.class); + + public String hello(final String message) { + LOG.info(message); + return "hello"; + } + } + + @ApplicationScoped + public static class JulBean { + private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(JulBean.class.getName()); + + public String hello(final String message) { + LOG.info(message); + return "hello"; + } + } + +} diff --git a/extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider b/extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider new file mode 100644 index 0000000000000..4715dfe663ac9 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider @@ -0,0 +1 @@ +io.quarkus.opentelemetry.deployment.common.InMemoryLogRecordExporterProvider \ No newline at end of file diff --git a/extensions/opentelemetry/deployment/src/test/resources/application-default.properties b/extensions/opentelemetry/deployment/src/test/resources/application-default.properties index 5871e27ba54f5..174216b61c4a6 100644 --- a/extensions/opentelemetry/deployment/src/test/resources/application-default.properties +++ b/extensions/opentelemetry/deployment/src/test/resources/application-default.properties @@ -1,5 +1,11 @@ +# Traces quarkus.otel.traces.exporter=test-span-exporter quarkus.otel.bsp.schedule.delay=50ms quarkus.otel.bsp.export.timeout=1s + +# Metrics quarkus.otel.metrics.exporter=in-memory -quarkus.otel.metric.export.interval=300ms \ No newline at end of file +quarkus.otel.metric.export.interval=300ms + +# Logs +quarkus.otel.logs.exporter=in-memory \ No newline at end of file diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java index b719265272623..8e4bc4584ad68 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java @@ -13,6 +13,7 @@ import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; public class OpenTelemetryLogHandler extends Handler { @@ -20,7 +21,12 @@ public class OpenTelemetryLogHandler extends Handler { @Override public void publish(LogRecord record) { - try (InstanceHandle openTelemetry = Arc.container().instance(OpenTelemetry.class)) { + ArcContainer container = Arc.container(); + if (container == null || !container.instance(OpenTelemetry.class).isAvailable()) { + // "quarkus-opentelemetry-deployment stopped in Xs" will never be sent. + return; // evaluate to perform cache of log entries here and replay them later. + } + try (InstanceHandle openTelemetry = container.instance(OpenTelemetry.class)) { if (openTelemetry.isAvailable()) { LogRecordBuilder logRecordBuilder = openTelemetry.get().getLogsBridge().loggerBuilder(INSTRUMENTATION_NAME) .build().logRecordBuilder()