Skip to content

Commit

Permalink
Fix OpenTelemetry JDBC instrumentation in native mode and resilient test
Browse files Browse the repository at this point in the history
  • Loading branch information
michalvavrik committed Mar 18, 2023
1 parent bd51374 commit 1d0571f
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/native-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
{
"category": "Misc4",
"timeout": 120,
"test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, opentelemetry, webjars-locator",
"test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, opentelemetry, opentelemetry-jdbc-instrumentation, webjars-locator",
"os-name": "ubuntu-latest"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.sql.XADataSource;
Expand All @@ -31,6 +32,7 @@
import io.quarkus.agroal.runtime.TransactionIntegration;
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
import io.quarkus.agroal.spi.JdbcDriverBuildItem;
import io.quarkus.agroal.spi.OpenTelemetryInitBuildItem;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
Expand All @@ -51,10 +53,12 @@
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.RemovedResourceBuildItem;
import io.quarkus.deployment.builditem.SslNativeConfigBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.maven.dependency.ArtifactKey;
import io.quarkus.narayana.jta.deployment.NarayanaInitBuildItem;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem;
Expand All @@ -64,6 +68,7 @@ class AgroalProcessor {

private static final Logger log = Logger.getLogger(AgroalProcessor.class);

private static final String OPEN_TELEMETRY_DRIVER = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver";
private static final DotName DATA_SOURCE = DotName.createSimple(javax.sql.DataSource.class.getName());

@BuildStep
Expand Down Expand Up @@ -248,6 +253,7 @@ void generateDataSourceSupportBean(AgroalRecorder recorder,

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
@Consume(OpenTelemetryInitBuildItem.class)
@Consume(NarayanaInitBuildItem.class)
void generateDataSourceBeans(AgroalRecorder recorder,
DataSourcesRuntimeConfig dataSourcesRuntimeConfig,
Expand Down Expand Up @@ -383,4 +389,27 @@ HealthBuildItem addHealthCheck(Capabilities capabilities, DataSourcesBuildTimeCo
return null;
}
}

/**
* TODO: remove the step when https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8080 is closed
*/
@BuildStep
void adaptOpenTelemetryJdbcInstrumentationForNative(BuildProducer<RemovedResourceBuildItem> producer,
Capabilities capabilities) {
// remove 'JdbcSingletons' as it initialize OpenTelemetry at build time
// 'OpenTelemetryDriver' is removed as it is directly using 'JdbcSingletons'
// we also need to check for the driver presence at classpath as it is possible that both OpenTelemetry
// and Agroal extensions are used, but dependency 'opentelemetry-jdbc' is not present
if (capabilities.isPresent(OPENTELEMETRY_TRACER) && QuarkusClassLoader.isClassPresentAtRuntime(OPEN_TELEMETRY_DRIVER)) {
producer.produce(
new RemovedResourceBuildItem(ArtifactKey.fromString("io.opentelemetry.instrumentation:opentelemetry-jdbc"),
Set.of("META-INF/services/java.sql.Driver")));
producer.produce(
new RemovedResourceBuildItem(ArtifactKey.fromString("io.opentelemetry.instrumentation:opentelemetry-jdbc"),
Set.of("io/opentelemetry/instrumentation/jdbc/OpenTelemetryDriver")));
producer.produce(
new RemovedResourceBuildItem(ArtifactKey.fromString("io.opentelemetry.instrumentation:opentelemetry-jdbc"),
Set.of("io/opentelemetry/instrumentation.jdbc/internal/JdbcSingletons")));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import io.agroal.api.AgroalDataSourceMetrics;
import io.agroal.api.AgroalPoolInterceptor;
import io.agroal.api.configuration.AgroalDataSourceConfiguration;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.jdbc.datasource.OpenTelemetryDataSource;
import io.quarkus.arc.Arc;

/**
* The {@link AgroalDataSource} wrapper that activates OpenTelemetry JDBC instrumentation.
Expand All @@ -20,7 +21,7 @@ public class OpenTelemetryAgroalDataSource extends OpenTelemetryDataSource imple
private final AgroalDataSource delegate;

public OpenTelemetryAgroalDataSource(AgroalDataSource delegate) {
super(delegate, GlobalOpenTelemetry.get());
super(delegate, Arc.container().instance(OpenTelemetry.class).get());
this.delegate = delegate;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.quarkus.agroal.spi;

import io.quarkus.builder.item.EmptyBuildItem;

/**
* Marker build item that indicates that the OpenTelemetry extension has been initialized.
*/
public final class OpenTelemetryInitBuildItem extends EmptyBuildItem {
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
import io.quarkus.agroal.spi.OpenTelemetryInitBuildItem;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.InterceptorBindingRegistrarBuildItem;
Expand All @@ -36,6 +37,7 @@
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Produce;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
Expand Down Expand Up @@ -155,6 +157,7 @@ public void transform(TransformationContext context) {

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
@Produce(OpenTelemetryInitBuildItem.class)
void createOpenTelemetry(
OpenTelemetryRecorder recorder,
InstrumentationRecorder instrumentationRecorder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.Duration;
import java.util.List;

import jakarta.annotation.PostConstruct;
Expand All @@ -16,6 +17,8 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import org.awaitility.Awaitility;
import org.awaitility.core.ThrowingRunnable;
import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
Expand All @@ -41,6 +44,7 @@ public class OpenTelemetryDevServicesDatasourcesTest {
"quarkus.datasource.db-kind=h2\n" +
"quarkus.datasource.jdbc.telemetry=true\n" +
"quarkus.otel.traces.exporter=test-span-exporter\n" +
"quarkus.otel.bsp.export.timeout=1s\n" +
"quarkus.otel.bsp.schedule.delay=50\n"),
"application.properties"));

Expand All @@ -60,9 +64,12 @@ void devDatasource() {
}

private void verifyNumOfInsertedTraces(int expectedSpans, int insertCount) {
RestAssured.get("/hello/greetings-insert-count/" + expectedSpans).then()
ThrowingRunnable assertInsertCount = () -> RestAssured
.get("/hello/greetings-insert-count/" + expectedSpans)
.then()
.statusCode(200)
.body(Matchers.is(Integer.toString(insertCount)));
Awaitility.await().atMost(Duration.ofMinutes(1)).untilAsserted(assertInsertCount);
}

private void createGreeting(String greeting) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public abstract class OpenTelemetryJdbcInstrumentationTest {
@AfterEach
void reset() {
given().get("/reset").then().statusCode(HTTP_OK);
await().atMost(5, SECONDS).until(() -> {
await().atMost(30, SECONDS).until(() -> {
// make sure spans are cleared
List<Map<String, Object>> spans = getSpans();
if (spans.size() > 0) {
Expand All @@ -47,7 +47,7 @@ protected void testQueryTraced(String dbKind, String expectedTable) {
.statusCode(200)
.body("message", Matchers.equalTo("Hit message."));

Awaitility.await().during(Duration.ofSeconds(2)).until(() -> !getSpans().isEmpty());
Awaitility.await().atMost(Duration.ofSeconds(55)).until(() -> !getSpans().isEmpty());

// Assert insert has been traced
boolean hitInserted = false;
Expand Down

0 comments on commit 1d0571f

Please sign in to comment.