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/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 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