diff --git a/.github/native-tests.json b/.github/native-tests.json index aefe350592169d..cb8dbea659b1dd 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -117,7 +117,7 @@ { "category": "Misc4", "timeout": 120, - "test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, opentelemetry, webjars-locator", + "test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, opentelemetry, opentelemetry-jdbc-instrumentation, webjars-locator", "os-name": "ubuntu-latest" }, { diff --git a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java index 62a088d433fa8a..ecdef893fcaab6 100644 --- a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java +++ b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import javax.sql.XADataSource; @@ -31,6 +32,7 @@ import io.quarkus.agroal.runtime.TransactionIntegration; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDriverBuildItem; +import io.quarkus.agroal.spi.OpenTelemetryInitBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -51,10 +53,12 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.RemovedResourceBuildItem; import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; +import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.narayana.jta.deployment.NarayanaInitBuildItem; import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; @@ -64,6 +68,7 @@ class AgroalProcessor { private static final Logger log = Logger.getLogger(AgroalProcessor.class); + private static final String OPEN_TELEMETRY_DRIVER = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"; private static final DotName DATA_SOURCE = DotName.createSimple(javax.sql.DataSource.class.getName()); @BuildStep @@ -248,6 +253,7 @@ void generateDataSourceSupportBean(AgroalRecorder recorder, @Record(ExecutionTime.RUNTIME_INIT) @BuildStep + @Consume(OpenTelemetryInitBuildItem.class) @Consume(NarayanaInitBuildItem.class) void generateDataSourceBeans(AgroalRecorder recorder, DataSourcesRuntimeConfig dataSourcesRuntimeConfig, @@ -383,4 +389,27 @@ HealthBuildItem addHealthCheck(Capabilities capabilities, DataSourcesBuildTimeCo return null; } } + + /** + * TODO: remove the step when https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8080 is closed + */ + @BuildStep + void adaptOpenTelemetryJdbcInstrumentationForNative(BuildProducer producer, + Capabilities capabilities) { + // remove 'JdbcSingletons' as it initialize OpenTelemetry at build time + // 'OpenTelemetryDriver' is removed as it is directly using 'JdbcSingletons' + // we also need to check for the driver presence at classpath as it is possible that both OpenTelemetry + // and Agroal extensions are used, but dependency 'opentelemetry-jdbc' is not present + if (capabilities.isPresent(OPENTELEMETRY_TRACER) && QuarkusClassLoader.isClassPresentAtRuntime(OPEN_TELEMETRY_DRIVER)) { + producer.produce( + new RemovedResourceBuildItem(ArtifactKey.fromString("io.opentelemetry.instrumentation:opentelemetry-jdbc"), + Set.of("META-INF/services/java.sql.Driver"))); + producer.produce( + new RemovedResourceBuildItem(ArtifactKey.fromString("io.opentelemetry.instrumentation:opentelemetry-jdbc"), + Set.of("io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriver"))); + producer.produce( + new RemovedResourceBuildItem(ArtifactKey.fromString("io.opentelemetry.instrumentation:opentelemetry-jdbc"), + Set.of("io/opentelemetry/instrumentation.jdbc/internal/JdbcSingletons"))); + } + } } diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/OpenTelemetryAgroalDataSource.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/OpenTelemetryAgroalDataSource.java index 9b5210fd79237d..360b6059d68ac6 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/OpenTelemetryAgroalDataSource.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/OpenTelemetryAgroalDataSource.java @@ -9,8 +9,9 @@ import io.agroal.api.AgroalDataSourceMetrics; import io.agroal.api.AgroalPoolInterceptor; import io.agroal.api.configuration.AgroalDataSourceConfiguration; -import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.jdbc.datasource.OpenTelemetryDataSource; +import io.quarkus.arc.Arc; /** * The {@link AgroalDataSource} wrapper that activates OpenTelemetry JDBC instrumentation. @@ -20,7 +21,7 @@ public class OpenTelemetryAgroalDataSource extends OpenTelemetryDataSource imple private final AgroalDataSource delegate; public OpenTelemetryAgroalDataSource(AgroalDataSource delegate) { - super(delegate, GlobalOpenTelemetry.get()); + super(delegate, Arc.container().instance(OpenTelemetry.class).get()); this.delegate = delegate; } diff --git a/extensions/agroal/spi/src/main/java/io/quarkus/agroal/spi/OpenTelemetryInitBuildItem.java b/extensions/agroal/spi/src/main/java/io/quarkus/agroal/spi/OpenTelemetryInitBuildItem.java new file mode 100644 index 00000000000000..c9f818427ab240 --- /dev/null +++ b/extensions/agroal/spi/src/main/java/io/quarkus/agroal/spi/OpenTelemetryInitBuildItem.java @@ -0,0 +1,9 @@ +package io.quarkus.agroal.spi; + +import io.quarkus.builder.item.EmptyBuildItem; + +/** + * Marker build item that indicates that the OpenTelemetry extension has been initialized. + */ +public final class OpenTelemetryInitBuildItem extends EmptyBuildItem { +} 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 2d1b35d43313d0..423415487d151e 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 @@ -25,6 +25,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; +import io.quarkus.agroal.spi.OpenTelemetryInitBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.InterceptorBindingRegistrarBuildItem; @@ -36,6 +37,7 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Produce; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; @@ -155,6 +157,7 @@ public void transform(TransformationContext context) { @BuildStep @Record(ExecutionTime.RUNTIME_INIT) + @Produce(OpenTelemetryInitBuildItem.class) void createOpenTelemetry( OpenTelemetryRecorder recorder, InstrumentationRecorder instrumentationRecorder, diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java index 6e570426ed1a2f..1a7f03ab3d8e12 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java @@ -3,6 +3,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; +import java.time.Duration; import java.util.List; import jakarta.annotation.PostConstruct; @@ -16,6 +17,8 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.awaitility.Awaitility; +import org.awaitility.core.ThrowingRunnable; import org.hamcrest.Matchers; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Test; @@ -41,6 +44,7 @@ public class OpenTelemetryDevServicesDatasourcesTest { "quarkus.datasource.db-kind=h2\n" + "quarkus.datasource.jdbc.telemetry=true\n" + "quarkus.otel.traces.exporter=test-span-exporter\n" + + "quarkus.otel.bsp.export.timeout=1s\n" + "quarkus.otel.bsp.schedule.delay=50\n"), "application.properties")); @@ -60,9 +64,12 @@ void devDatasource() { } private void verifyNumOfInsertedTraces(int expectedSpans, int insertCount) { - RestAssured.get("/hello/greetings-insert-count/" + expectedSpans).then() + ThrowingRunnable assertInsertCount = () -> RestAssured + .get("/hello/greetings-insert-count/" + expectedSpans) + .then() .statusCode(200) .body(Matchers.is(Integer.toString(insertCount))); + Awaitility.await().atMost(Duration.ofMinutes(1)).untilAsserted(assertInsertCount); } private void createGreeting(String greeting) { diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java index b646d59aceaf87..41a27b3731d8b3 100644 --- a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java @@ -24,7 +24,7 @@ public abstract class OpenTelemetryJdbcInstrumentationTest { @AfterEach void reset() { given().get("/reset").then().statusCode(HTTP_OK); - await().atMost(5, SECONDS).until(() -> { + await().atMost(30, SECONDS).until(() -> { // make sure spans are cleared List> spans = getSpans(); if (spans.size() > 0) { @@ -47,7 +47,7 @@ protected void testQueryTraced(String dbKind, String expectedTable) { .statusCode(200) .body("message", Matchers.equalTo("Hit message.")); - Awaitility.await().during(Duration.ofSeconds(2)).until(() -> !getSpans().isEmpty()); + Awaitility.await().atMost(Duration.ofSeconds(55)).until(() -> !getSpans().isEmpty()); // Assert insert has been traced boolean hitInserted = false;