From 42b91aeb47c9349f28d9de146ebb0ca3ce7f1252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Wed, 21 Dec 2022 02:21:47 +0100 Subject: [PATCH 1/2] OpenTelemetry JDBC instrumentation - fix Oracle and DB2 in native mode --- .github/native-tests.json | 2 +- .../io/quarkus/deployment/Capability.java | 1 + .../jdbc/db2/deployment/JDBCDB2Processor.java | 14 +- extensions/jdbc/jdbc-oracle/runtime/pom.xml | 5 + ...lemetryDriverJdbcDataSourcesBuildItem.java | 18 + .../deployment/OpenTelemetryProcessor.java | 64 +++ .../DevServicesOpenTelemetryProcessor.java | 25 +- .../runtime/OpenTelemetryRecorder.java | 34 ++ .../README.md | 36 ++ .../pom.xml | 408 ++++++++++++++++++ .../it/opentelemetry/ExporterResource.java | 46 ++ .../it/opentelemetry/PingPongResource.java | 54 +++ .../quarkus/it/opentelemetry/model/Hit.java | 15 + .../it/opentelemetry/model/db2/Db2Hit.java | 36 ++ .../model/mariadb/MariaDbHit.java | 36 ++ .../opentelemetry/model/oracle/OracleHit.java | 36 ++ .../it/opentelemetry/model/pg/PgHit.java | 36 ++ .../resources/META-INF/resources/test.html | 1 + .../src/main/resources/application.properties | 52 +++ .../it/opentelemetry/Db2LifecycleManager.java | 56 +++ ...Db2OpenTelemetryJdbcInstrumentationIT.java | 11 + ...2OpenTelemetryJdbcInstrumentationTest.java | 19 + .../MariaDbLifecycleManager.java | 55 +++ ...aDbOpenTelemetryJdbcInstrumentationIT.java | 8 + ...bOpenTelemetryJdbcInstrumentationTest.java | 17 + .../OpenTelemetryJdbcInstrumentationTest.java | 55 +++ ...cleOpenTelemetryJdbcInstrumentationIT.java | 8 + ...eOpenTelemetryJdbcInstrumentationTest.java | 26 ++ .../PostgreSqlLifecycleManager.java | 55 +++ ...resOpenTelemetryJdbcInstrumentationIT.java | 8 + ...sOpenTelemetryJdbcInstrumentationTest.java | 17 + integration-tests/pom.xml | 1 + 32 files changed, 1234 insertions(+), 21 deletions(-) create mode 100644 extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/README.md create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/pom.xml create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java create mode 100644 integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java diff --git a/.github/native-tests.json b/.github/native-tests.json index 52b608539dc10..a77ea33a73c1a 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -117,7 +117,7 @@ { "category": "Misc4", "timeout": 75, - "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/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java index 80fecb6802fa5..19cc80cff22d8 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -141,4 +141,5 @@ public interface Capability { String REDIS_CLIENT = QUARKUS_PREFIX + "redis"; String CACHE = QUARKUS_PREFIX + "cache"; + String JDBC_ORACLE = QUARKUS_PREFIX + "jdbc.oracle"; } diff --git a/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java index 8f26efb32a020..b3b50efb6e6a5 100644 --- a/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java +++ b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java @@ -16,12 +16,15 @@ import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; import io.quarkus.deployment.builditem.nativeimage.JPMSExportBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.jdbc.db2.runtime.DB2AgroalConnectionConfigurer; import io.quarkus.jdbc.db2.runtime.DB2ServiceBindingConverter; public class JDBCDB2Processor { + private static final String DB2_DRIVER_CLASS = "com.ibm.db2.jcc.DB2Driver"; + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(Feature.JDBC_DB2); @@ -30,7 +33,7 @@ FeatureBuildItem feature() { @BuildStep void registerDriver(BuildProducer jdbcDriver, SslNativeConfigBuildItem sslNativeConfigBuildItem) { - jdbcDriver.produce(new JdbcDriverBuildItem(DatabaseKind.DB2, "com.ibm.db2.jcc.DB2Driver", + jdbcDriver.produce(new JdbcDriverBuildItem(DatabaseKind.DB2, DB2_DRIVER_CLASS, "com.ibm.db2.jcc.DB2XADataSource")); } @@ -51,6 +54,15 @@ void configureAgroalConnection(BuildProducer additional } } + @BuildStep + void registerDriverForReflection(BuildProducer reflectiveClass) { + //Not strictly necessary when using Agroal, as it also registers + //any JDBC driver being configured explicitly through its configuration. + //We register it for the sake of people not using Agroal, + //for example when the driver is used with OpenTelemetry JDBC instrumentation. + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, DB2_DRIVER_CLASS)); + } + @BuildStep NativeImageConfigBuildItem build() { // The DB2 JDBC driver has been updated with conditional checks for the diff --git a/extensions/jdbc/jdbc-oracle/runtime/pom.xml b/extensions/jdbc/jdbc-oracle/runtime/pom.xml index 52e99e443461e..0e692fb507e75 100644 --- a/extensions/jdbc/jdbc-oracle/runtime/pom.xml +++ b/extensions/jdbc/jdbc-oracle/runtime/pom.xml @@ -43,6 +43,11 @@ io.quarkus quarkus-extension-maven-plugin + + + io.quarkus.jdbc.oracle + + maven-compiler-plugin diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java new file mode 100644 index 0000000000000..543fe2d36ebc1 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.opentelemetry.deployment; + +import java.util.List; + +import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Contains list of all {@link io.quarkus.agroal.spi.JdbcDataSourceBuildItem} using OpenTelemetryDriver. + */ +public final class OpenTelemetryDriverJdbcDataSourcesBuildItem extends SimpleBuildItem { + + public final List jdbcDataSources; + + OpenTelemetryDriverJdbcDataSourcesBuildItem(List jdbcDataSources) { + this.jdbcDataSources = List.copyOf(jdbcDataSources); + } +} 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 9cda1e84141f3..ee63ae6a3a647 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,10 +1,15 @@ package io.quarkus.opentelemetry.deployment; +import static io.quarkus.opentelemetry.runtime.OpenTelemetryRecorder.OPEN_TELEMETRY_DRIVER; + +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.ConfigValue; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationValue; @@ -14,11 +19,17 @@ import io.opentelemetry.instrumentation.annotations.SpanAttribute; import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; +import io.quarkus.agroal.spi.JdbcDriverBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.InterceptorBindingRegistrarBuildItem; import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.InterceptorBindingRegistrar; +import io.quarkus.datasource.common.runtime.DataSourceUtil; +import io.quarkus.datasource.common.runtime.DatabaseKind; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildSteps; @@ -155,4 +166,57 @@ void createOpenTelemetry( void storeVertxOnContextStorage(OpenTelemetryRecorder recorder, CoreVertxBuildItem vertx) { recorder.storeVertxOnContextStorage(vertx.getVertx()); } + + @BuildStep + void collectAllJdbcDataSourcesUsingOTelDriver(BuildProducer resultProducer, + List jdbcDataSources) { + final List result = new ArrayList<>(); + for (JdbcDataSourceBuildItem dataSource : jdbcDataSources) { + // if the datasource is explicitly configured to use the OTel driver... + if (dataSourceUsesOTelJdbcDriver(dataSource.getName())) { + result.add(dataSource); + } + } + if (!result.isEmpty()) { + resultProducer.produce(new OpenTelemetryDriverJdbcDataSourcesBuildItem(result)); + } + } + + private static boolean dataSourceUsesOTelJdbcDriver(String dataSourceName) { + List driverPropertyKeys = DataSourceUtil.dataSourcePropertyKeys(dataSourceName, "jdbc.driver"); + for (String driverPropertyKey : driverPropertyKeys) { + ConfigValue explicitlyConfiguredDriverValue = ConfigProvider.getConfig().getConfigValue(driverPropertyKey); + if (explicitlyConfiguredDriverValue.getValue() != null) { + return explicitlyConfiguredDriverValue.getValue().equals(OPEN_TELEMETRY_DRIVER); + } + } + return false; + } + + /** + * 'OracleDriver' register itself as driver in static initialization block, however we don't want to + * force runtime initialization for compatibility reasons, for more information please check: + * io.quarkus.jdbc.oracle.deployment.OracleMetadataOverrides#runtimeInitializeDriver + */ + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void registerOracleDriver(Optional otJdbcDataSourcesBuildItem, + List driverBuildItems, Capabilities capabilities, OpenTelemetryRecorder recorder) { + // check if there are data sources using OT driver and jdbc-oracle extension is present + if (otJdbcDataSourcesBuildItem.isPresent() && capabilities.isPresent(Capability.JDBC_ORACLE)) { + for (JdbcDataSourceBuildItem jdbcDataSource : otJdbcDataSourcesBuildItem.get().jdbcDataSources) { + if (jdbcDataSource.getDbKind().equals(DatabaseKind.ORACLE)) { + // now we know there is Oracle JDBC datasource + // let's find Oracle driver + for (JdbcDriverBuildItem driverBuildItem : driverBuildItems) { + if (DatabaseKind.ORACLE.equals(driverBuildItem.getDbKind())) { + recorder.registerJdbcDriver(driverBuildItem.getDriverClass()); + break; + } + } + break; + } + } + } + } } diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java index 7ceab490aa88b..1ec00718187e6 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java @@ -3,9 +3,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.ConfigValue; +import java.util.Optional; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -15,17 +13,18 @@ import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.builditem.DevServicesAdditionalConfigBuildItem; import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig; +import io.quarkus.opentelemetry.deployment.OpenTelemetryDriverJdbcDataSourcesBuildItem; import io.quarkus.opentelemetry.deployment.OpenTelemetryEnabled; @BuildSteps(onlyIfNot = IsNormal.class, onlyIf = { OpenTelemetryEnabled.class, GlobalDevServicesConfig.Enabled.class }) public class DevServicesOpenTelemetryProcessor { @BuildStep - void devServicesDatasources(List jdbcDataSources, + void devServicesDatasources(Optional otJdbcDataSourcesBuildItem, BuildProducer devServicesAdditionalConfig) { - for (JdbcDataSourceBuildItem dataSource : jdbcDataSources) { - // if the datasource is explicitly configured to use the OTel driver... - if (dataSourceUsesOTelJdbcDriver(dataSource.getName())) { + if (otJdbcDataSourcesBuildItem.isPresent()) { + // found datasources explicitly configured to use the OTel driver + for (JdbcDataSourceBuildItem dataSource : otJdbcDataSourcesBuildItem.get().jdbcDataSources) { List urlPropertyKeys = DataSourceUtil.dataSourcePropertyKeys(dataSource.getName(), "jdbc.url"); devServicesAdditionalConfig.produce(new DevServicesAdditionalConfigBuildItem(devServicesConfig -> { Map overrides = new HashMap<>(); @@ -42,16 +41,4 @@ void devServicesDatasources(List jdbcDataSources, } } } - - private boolean dataSourceUsesOTelJdbcDriver(String dataSourceName) { - List driverPropertyKeys = DataSourceUtil.dataSourcePropertyKeys(dataSourceName, "jdbc.driver"); - for (String driverPropertyKey : driverPropertyKeys) { - ConfigValue explicitlyConfiguredDriverValue = ConfigProvider.getConfig().getConfigValue(driverPropertyKey); - if (explicitlyConfiguredDriverValue.getValue() != null) { - return explicitlyConfiguredDriverValue.getValue() - .equals("io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"); - } - } - return false; - } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java index a6a4c760feb48..8fb318b8cabce 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java @@ -1,7 +1,11 @@ package io.quarkus.opentelemetry.runtime; +import java.lang.reflect.InvocationTargetException; +import java.sql.Driver; import java.util.function.Supplier; +import org.jboss.logging.Logger; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.ContextStorage; @@ -16,6 +20,9 @@ @Recorder public class OpenTelemetryRecorder { + public static final String OPEN_TELEMETRY_DRIVER = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"; + private static final Logger LOG = Logger.getLogger(OpenTelemetryRecorder.class); + /* STATIC INIT */ public void resetGlobalOpenTelemetryForDevMode() { GlobalOpenTelemetry.resetForTest(); @@ -46,4 +53,31 @@ public void eagerlyCreateContextStorage() { public void storeVertxOnContextStorage(Supplier vertx) { QuarkusContextStorage.vertx = vertx.get(); } + + public void registerJdbcDriver(String driverClass) { + try { + var constructors = Class + .forName(driverClass, true, Thread.currentThread().getContextClassLoader()) + .getConstructors(); + if (constructors.length == 1) { + // create driver + Driver driver = ((Driver) constructors[0].newInstance()); + // register the driver with OpenTelemetryDriver + Class + .forName(OPEN_TELEMETRY_DRIVER, true, Thread.currentThread().getContextClassLoader()) + .getMethod("addDriverCandidate", Driver.class) + .invoke(null, driver); + } else { + // drivers should have default constructor + LOG.warn(String.format( + "Class '%s' has more than one constructor and won't be registered as driver. JDBC instrumentation might not work properly in native mode.", + driverClass)); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException + | ClassNotFoundException e) { + LOG.warn(String.format( + "Failed to register '%s' driver. JDBC instrumentation might not work properly in native mode.", + driverClass)); + } + } } diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/README.md b/integration-tests/opentelemetry-jdbc-instrumentation/README.md new file mode 100644 index 0000000000000..d0228e831345d --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/README.md @@ -0,0 +1,36 @@ +# OpenTelemetry JDBC instrumentation example + +## Running the tests + + +To run the tests in a standard JVM with an Oracle, PostgreSQL and MariaDB databases started as a Docker containers, you can run the following command: + +``` +mvn verify -Dtest-containers -Dstart-containers +``` + +To also test as a native image, add `-Dnative`: + +``` +mvn verify -Dtest-containers -Dstart-containers -Dnative +``` + +You can also run tests with a specific database image, just set the following parameters: + +- `oracle.image` for Oracle +- `postgres.image` for PostgreSQL +- `mariadb.image` for MariaDB +- `db2.image` for Db2 + +For example to run tests with the latest PostgreSQL database image, you can run the following command: + +``` +mvn verify -Dtest-containers -Dstart-containers -Dpostgres.image=docker.io/postgres:latest +``` + +Unfortunately booting DB2 is slow and needs to set a generous timeout, therefore the DB2 test is disabled by default. +You can enable it with `enable-db2` system property like this: + +``` +mvn verify -Dtest-containers -Dstart-containers -Denable-db2 +``` \ No newline at end of file diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/pom.xml b/integration-tests/opentelemetry-jdbc-instrumentation/pom.xml new file mode 100644 index 0000000000000..33a7d936d7833 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/pom.xml @@ -0,0 +1,408 @@ + + + 4.0.0 + + + io.quarkus + quarkus-integration-tests-parent + 999-SNAPSHOT + + + quarkus-integration-test-opentelemetry-jdbc-instrumentation + Quarkus - Integration Tests - OpenTelemetry JDBC instrumentation + + + + io.quarkus + quarkus-opentelemetry + + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + + io.opentelemetry + opentelemetry-sdk-testing + + + + + io.opentelemetry.instrumentation + opentelemetry-jdbc + + + io.quarkus + quarkus-jdbc-oracle + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-jdbc-mariadb + + + io.quarkus + quarkus-jdbc-db2 + + + io.quarkus + quarkus-hibernate-orm-panache + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.awaitility + awaitility + test + + + org.testcontainers + testcontainers + test + + + org.testcontainers + oracle-xe + test + + + junit + junit + + + + + org.testcontainers + postgresql + test + + + junit + junit + + + + + org.testcontainers + mariadb + test + + + junit + junit + + + + + org.testcontainers + db2 + test + + + junit + junit + + + + + + + io.quarkus + quarkus-resteasy-reactive-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-hibernate-orm-panache-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-oracle-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-mariadb-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-postgresql-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-db2-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + + maven-surefire-plugin + + true + + + + maven-failsafe-plugin + + true + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + + native-image + + + native + + + + + native + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + test-jdbc-instrumentation + + + test-containers + + + + + + maven-surefire-plugin + + false + + ${oracle.image} + ${postgres.image} + ${mariadb.image} + ${db2.image} + + + + + maven-failsafe-plugin + + false + + ${oracle.image} + ${postgres.image} + ${mariadb.image} + ${db2.image} + + + + + + + + test-jdbc-instrumentation-docker-oracle + + + start-containers + + + + + + io.fabric8 + docker-maven-plugin + + + + ${oracle.image} + + + 1521:1521 + + + quarkus + + + Oracle Database: + default + red + + + + + + DATABASE IS READY TO USE! + + + + + + true + + + + docker-start + compile + + stop + start + + + + docker-stop + post-integration-test + + stop + + + + + + org.codehaus.mojo + exec-maven-plugin + + + docker-prune + generate-resources + + exec + + + ${docker-prune.location} + + + + + + + + + diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java new file mode 100644 index 0000000000000..144bcc4911d8a --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java @@ -0,0 +1,46 @@ +package io.quarkus.it.opentelemetry; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.data.SpanData; + +@Path("") +public class ExporterResource { + @Inject + InMemorySpanExporter inMemorySpanExporter; + + @GET + @Path("/reset") + public Response reset() { + inMemorySpanExporter.reset(); + return Response.ok().build(); + } + + @GET + @Path("/export") + public List export() { + return inMemorySpanExporter.getFinishedSpanItems() + .stream() + .filter(sd -> !sd.getName().contains("export") && !sd.getName().contains("reset")) + .collect(Collectors.toList()); + } + + @ApplicationScoped + static class InMemorySpanExporterProducer { + @Produces + @Singleton + InMemorySpanExporter inMemorySpanExporter() { + return InMemorySpanExporter.create(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java new file mode 100644 index 0000000000000..f6f2d8cb17d61 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java @@ -0,0 +1,54 @@ +package io.quarkus.it.opentelemetry; + +import java.util.function.Supplier; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import io.quarkus.it.opentelemetry.model.Hit; +import io.quarkus.it.opentelemetry.model.db2.Db2Hit; +import io.quarkus.it.opentelemetry.model.mariadb.MariaDbHit; +import io.quarkus.it.opentelemetry.model.oracle.OracleHit; +import io.quarkus.it.opentelemetry.model.pg.PgHit; + +@ApplicationScoped +@Path("/") +public class PingPongResource { + + @Transactional + @POST + @Produces(MediaType.APPLICATION_JSON) + @Path("/hit/{tenant}") + public Hit createHit(@QueryParam("id") Long id, @PathParam("tenant") String tenant) { + switch (tenant) { + case "postgresql": + persist(PgHit::new, id); + return PgHit.findById(id); + case "oracle": + persist(OracleHit::new, id); + return OracleHit.findById(id); + case "mariadb": + persist(MariaDbHit::new, id); + return MariaDbHit.findById(id); + case "db2": + persist(Db2Hit::new, id); + return Db2Hit.findById(id); + default: + throw new IllegalArgumentException(); + } + } + + private void persist(Supplier hitSupplier, Long id) { + Hit hit = hitSupplier.get(); + hit.setId(id); + hit.setMessage("Hit message."); + hit.persist(); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java new file mode 100644 index 0000000000000..518360e01e370 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java @@ -0,0 +1,15 @@ +package io.quarkus.it.opentelemetry.model; + +public interface Hit { + + Long getId(); + + String getMessage(); + + void setId(Long id); + + void setMessage(String message); + + void persist(); + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java new file mode 100644 index 0000000000000..c0b6637ded50b --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.db2; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class Db2Hit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java new file mode 100644 index 0000000000000..c3d77e52cc23b --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.mariadb; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class MariaDbHit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java new file mode 100644 index 0000000000000..490c7fa5cf521 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.oracle; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class OracleHit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java new file mode 100644 index 0000000000000..a79d1b79c7c4a --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.pg; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class PgHit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html new file mode 100644 index 0000000000000..d3e7968fdf060 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html @@ -0,0 +1 @@ +Test diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties new file mode 100644 index 0000000000000..0002291d802dc --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties @@ -0,0 +1,52 @@ +# Setting these for tests explicitly. Not required in normal application +quarkus.application.name=opentelemetry-jdbc-instrumentation-it +quarkus.application.version=999-SNAPSHOT + +# Disable Dev Services as we don't want to start 3 databases at once (CI can't handle it within hang timeout) +quarkus.datasource.devservices.enabled=false +quarkus.devservices.enabled=false + +# JDBC instrumentation setting +driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver +model-base-dir=io.quarkus.it.opentelemetry.model. + +# Oracle data source +quarkus.hibernate-orm.oracle.datasource=oracle +quarkus.hibernate-orm.oracle.packages=${model-base-dir}oracle +quarkus.hibernate-orm.oracle.database.generation=none +quarkus.datasource.oracle.db-kind=oracle +quarkus.datasource.oracle.jdbc.driver=${driver} +quarkus.datasource.oracle.jdbc.max-size=1 + +# MariaDB data source +quarkus.hibernate-orm.mariadb.datasource=mariadb +quarkus.hibernate-orm.mariadb.packages=${model-base-dir}mariadb +quarkus.hibernate-orm.mariadb.database.generation=none +quarkus.datasource.mariadb.db-kind=mariadb +quarkus.datasource.mariadb.jdbc.driver=${driver} +quarkus.datasource.mariadb.jdbc.max-size=1 + +# PostgreSQL data source +quarkus.hibernate-orm.postgresql.datasource=postgresql +quarkus.hibernate-orm.postgresql.packages=${model-base-dir}pg +quarkus.hibernate-orm.postgresql.database.generation=none +quarkus.datasource.postgresql.db-kind=postgresql +quarkus.datasource.postgresql.jdbc.driver=${driver} +quarkus.datasource.postgresql.jdbc.max-size=1 + +# Db2 data source +quarkus.hibernate-orm.db2.datasource=db2 +quarkus.hibernate-orm.db2.packages=${model-base-dir}db2 +quarkus.hibernate-orm.db2.database.generation=none +quarkus.datasource.db2.db-kind=db2 +quarkus.datasource.db2.jdbc.driver=${driver} +quarkus.datasource.db2.jdbc.max-size=1 + +# Oracle test profile properties +%oracle-profile.quarkus.datasource."oracle".jdbc.url=jdbc:otel:oracle:thin:@localhost:1521/XE +%oracle-profile.quarkus.datasource."oracle".password=quarkus +%oracle-profile.quarkus.datasource."oracle".username=SYSTEM +%oracle-profile.quarkus.hibernate-orm."oracle".database.generation=drop-and-create +%oracle-profile.quarkus.hibernate-orm.mariadb.active=false +%oracle-profile.quarkus.hibernate-orm.postgresql.active=false +%oracle-profile.quarkus.hibernate-orm.db2.active=false diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java new file mode 100644 index 0000000000000..e2d9f170bdb67 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java @@ -0,0 +1,56 @@ +package io.quarkus.it.opentelemetry; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.Db2Container; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class Db2LifecycleManager implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(Db2LifecycleManager.class); + private static final String QUARKUS = "quarkus"; + private static final String DB2_IMAGE = System.getProperty("db2.image"); + private StartedDb2Container db2Container; + + @Override + public Map start() { + db2Container = new StartedDb2Container(); + LOGGER.info(db2Container.getLogs()); + + Map properties = new HashMap<>(); + properties.put("quarkus.datasource.db2.jdbc.url", + String.format("jdbc:otel:db2://%s:%s/%s", db2Container.getHost(), + db2Container.getFirstMappedPort(), QUARKUS)); + properties.put("quarkus.datasource.db2.password", QUARKUS); + properties.put("quarkus.datasource.db2.username", QUARKUS); + properties.put("quarkus.hibernate-orm.db2.database.generation", "drop-and-create"); + properties.put("quarkus.hibernate-orm.oracle.active", "false"); + properties.put("quarkus.hibernate-orm.postgresql.active", "false"); + properties.put("quarkus.hibernate-orm.mariadb.active", "false"); + + return properties; + } + + @Override + public void stop() { + db2Container.stop(); + } + + private static final class StartedDb2Container extends Db2Container { + + public StartedDb2Container() { + super(DockerImageName + .parse(DB2_IMAGE) + .asCompatibleSubstituteFor(DockerImageName.parse("ibmcom/db2"))); + withDatabaseName(QUARKUS); + withUsername(QUARKUS); + withPassword(QUARKUS); + addExposedPort(5000); + acceptLicense(); + start(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 0000000000000..75c39e53bc404 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,11 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@EnabledIfSystemProperty(named = "enable-db2", matches = "true") +@QuarkusIntegrationTest +public class Db2OpenTelemetryJdbcInstrumentationIT extends Db2OpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 0000000000000..32c0e20389814 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,19 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@EnabledIfSystemProperty(named = "enable-db2", matches = "true") +@QuarkusTest +@QuarkusTestResource(value = Db2LifecycleManager.class, restrictToAnnotatedClass = true) +public class Db2OpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testDb2SqlQueryTraced() { + testQueryTraced("db2", "Db2Hit"); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java new file mode 100644 index 0000000000000..b6c671839953a --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java @@ -0,0 +1,55 @@ +package io.quarkus.it.opentelemetry; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class MariaDbLifecycleManager implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(MariaDbLifecycleManager.class); + private static final String QUARKUS = "quarkus"; + private static final String MARIADB_IMAGE = System.getProperty("mariadb.image"); + private StartedMariaDBContainer mariaDbContainer; + + @Override + public Map start() { + mariaDbContainer = new StartedMariaDBContainer(); + LOGGER.info(mariaDbContainer.getLogs()); + + Map properties = new HashMap<>(); + properties.put("quarkus.datasource.mariadb.jdbc.url", + String.format("jdbc:otel:mariadb://%s:%s/%s", mariaDbContainer.getHost(), + mariaDbContainer.getFirstMappedPort(), QUARKUS)); + properties.put("quarkus.datasource.mariadb.password", QUARKUS); + properties.put("quarkus.datasource.mariadb.username", QUARKUS); + properties.put("quarkus.hibernate-orm.mariadb.database.generation", "drop-and-create"); + properties.put("quarkus.hibernate-orm.oracle.active", "false"); + properties.put("quarkus.hibernate-orm.postgresql.active", "false"); + properties.put("quarkus.hibernate-orm.db2.active", "false"); + + return properties; + } + + @Override + public void stop() { + mariaDbContainer.stop(); + } + + private static final class StartedMariaDBContainer extends MariaDBContainer { + + public StartedMariaDBContainer() { + super(DockerImageName + .parse(MARIADB_IMAGE) + .asCompatibleSubstituteFor(DockerImageName.parse(MariaDBContainer.NAME))); + withDatabaseName(QUARKUS); + withUsername(QUARKUS); + withPassword(QUARKUS); + addExposedPort(3306); + start(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 0000000000000..618f3c9060955 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class MariaDbOpenTelemetryJdbcInstrumentationIT extends MariaDbOpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 0000000000000..5034ac7e7c978 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,17 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(value = MariaDbLifecycleManager.class, restrictToAnnotatedClass = true) +public class MariaDbOpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testMariaDbQueryTraced() { + testQueryTraced("mariadb", "MariaDbHit"); + } + +} 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 new file mode 100644 index 0000000000000..37e4fccdbbef9 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,55 @@ +package io.quarkus.it.opentelemetry; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; + +import io.restassured.common.mapper.TypeRef; + +public abstract class OpenTelemetryJdbcInstrumentationTest { + + @BeforeEach + void reset() { + given().get("/reset").then().statusCode(HTTP_OK); + await().atMost(5, SECONDS).until(() -> getSpans().size() == 0); + } + + private List> getSpans() { + return get("/export").body().as(new TypeRef<>() { + }); + } + + protected void testQueryTraced(String dbKind, String expectedTable) { + given() + .queryParam("id", 1) + .when().post("/hit/" + dbKind) + .then() + .statusCode(200) + .body("message", Matchers.equalTo("Hit message.")); + + // Assert insert has been traced + boolean hitInserted = false; + for (Map spanData : getSpans()) { + if (spanData.get("attributes") instanceof Map) { + final Map attributes = (Map) spanData.get("attributes"); + var dbOperation = attributes.get("db.operation"); + var dbTable = attributes.get("db.sql.table"); + if ("INSERT".equals(dbOperation) && expectedTable.equals(dbTable)) { + hitInserted = true; + break; + } + } + } + assertTrue(hitInserted, "JDBC insert statement was not traced."); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 0000000000000..e7ccd488acefe --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class OracleOpenTelemetryJdbcInstrumentationIT extends OracleOpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 0000000000000..e7359e9c47d65 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,26 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(OracleOpenTelemetryJdbcInstrumentationTest.OracleTestProfile.class) +public class OracleOpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testOracleQueryTraced() { + testQueryTraced("oracle", "OracleHit"); + } + + public static class OracleTestProfile implements QuarkusTestProfile { + + @Override + public String getConfigProfile() { + return "oracle-profile"; + } + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java new file mode 100644 index 0000000000000..d02473db11df9 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java @@ -0,0 +1,55 @@ +package io.quarkus.it.opentelemetry; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class PostgreSqlLifecycleManager implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(PostgreSqlLifecycleManager.class); + private static final String QUARKUS = "quarkus"; + private static final String POSTGRES_IMAGE = System.getProperty("postgres.image"); + private StartedPostgresContainer postgresContainer; + + @Override + public Map start() { + postgresContainer = new StartedPostgresContainer(); + LOGGER.info(postgresContainer.getLogs()); + + Map properties = new HashMap<>(); + properties.put("quarkus.datasource.postgresql.jdbc.url", + String.format("jdbc:otel:postgresql://%s:%s/%s", postgresContainer.getHost(), + postgresContainer.getFirstMappedPort(), QUARKUS)); + properties.put("quarkus.datasource.postgresql.password", QUARKUS); + properties.put("quarkus.datasource.postgresql.username", QUARKUS); + properties.put("quarkus.hibernate-orm.postgresql.database.generation", "drop-and-create"); + properties.put("quarkus.hibernate-orm.oracle.active", "false"); + properties.put("quarkus.hibernate-orm.mariadb.active", "false"); + properties.put("quarkus.hibernate-orm.db2.active", "false"); + + return properties; + } + + @Override + public void stop() { + postgresContainer.stop(); + } + + private static final class StartedPostgresContainer extends PostgreSQLContainer { + + public StartedPostgresContainer() { + super(DockerImageName + .parse(POSTGRES_IMAGE) + .asCompatibleSubstituteFor(DockerImageName.parse(PostgreSQLContainer.IMAGE))); + withDatabaseName(QUARKUS); + withUsername(QUARKUS); + withPassword(QUARKUS); + addExposedPort(5432); + start(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 0000000000000..08e62277beea2 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class PostgresOpenTelemetryJdbcInstrumentationIT extends PostgresOpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 0000000000000..cde8956e413eb --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,17 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(value = PostgreSqlLifecycleManager.class, restrictToAnnotatedClass = true) +public class PostgresOpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testPostgreSqlQueryTraced() { + testQueryTraced("postgresql", "PgHit"); + } + +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index eb1dba664c1b6..6f555e0268809 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -311,6 +311,7 @@ micrometer-mp-metrics micrometer-prometheus opentelemetry + opentelemetry-jdbc-instrumentation opentelemetry-vertx opentelemetry-reactive opentelemetry-grpc From 24735ab06ed119534d4868c32c4fe13cfee4e364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Fri, 30 Dec 2022 15:21:47 +0100 Subject: [PATCH 2/2] Fix native mode build when `jdbc-oracle` is combined with `jdbc-db2` Fixes native build when `jdbc-oracle` is combined with `jdbc-db2`. This fix was kindly provided Severin Gehwolf and is added as second commit of this PR as 1. integration tests need the fix 2. no point of creating additional integration test module that combines jdbc-oracle and jdbc-db2 as we already have them here. --- .../runtime/graal/ObjIdSubstitutions.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java diff --git a/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java b/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java new file mode 100644 index 0000000000000..0f73387c82673 --- /dev/null +++ b/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java @@ -0,0 +1,44 @@ +package io.quarkus.jdbc.oracle.runtime.graal; + +import java.security.SecureRandom; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.TargetClass; + +/** + * Substitutions required when `jdbc-oracle` is combined with `jdbc-db2`. + */ +@TargetClass(className = "java.rmi.server.ObjID") +public final class ObjIdSubstitutions { + + @Alias + @InjectAccessors(SecureRandomAccessor.class) + private static SecureRandom secureRandom; + +} + +class SecureRandomAccessor { + private static volatile SecureRandom RANDOM; + + static SecureRandom get() { + SecureRandom result = RANDOM; + if (result == null) { + /* Lazy initialization on first access. */ + result = initializeOnce(); + } + return result; + } + + private static synchronized SecureRandom initializeOnce() { + SecureRandom result = RANDOM; + if (result != null) { + /* Double-checked locking is OK because INSTANCE is volatile. */ + return result; + } + + result = new SecureRandom(); + RANDOM = result; + return result; + } +} \ No newline at end of file