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 f263832b8d810..4238f8b615937 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 @@ -28,7 +28,6 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.exporter.otlp.internal.OtlpSpanExporterProvider; import io.opentelemetry.instrumentation.annotations.AddingSpanAttributes; -import io.opentelemetry.instrumentation.annotations.SpanAttribute; import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; @@ -47,6 +46,8 @@ import io.quarkus.arc.processor.InterceptorBindingRegistrar; import io.quarkus.arc.processor.Transformation; import io.quarkus.datasource.common.runtime.DataSourceUtil; +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; @@ -92,7 +93,6 @@ public boolean test(AnnotationInstance annotationInstance) { private static final DotName WITH_SPAN_INTERCEPTOR = DotName.createSimple(WithSpanInterceptor.class.getName()); private static final DotName ADD_SPAN_ATTRIBUTES_INTERCEPTOR = DotName .createSimple(AddingSpanAttributesInterceptor.class.getName()); - private static final DotName SPAN_ATTRIBUTE = DotName.createSimple(SpanAttribute.class.getName()); @BuildStep AdditionalBeanBuildItem ensureProducerIsRetained() { @@ -263,10 +263,14 @@ void createOpenTelemetry( @BuildStep @Record(ExecutionTime.RUNTIME_INIT) - void setupVertx(InstrumentationRecorder recorder, - BeanContainerBuildItem beanContainerBuildItem) { - - recorder.setupVertxTracer(beanContainerBuildItem.getValue()); + void setupVertx(InstrumentationRecorder recorder, BeanContainerBuildItem beanContainerBuildItem, + Capabilities capabilities) { + boolean sqlClientAvailable = capabilities.isPresent(Capability.REACTIVE_DB2_CLIENT) + || capabilities.isPresent(Capability.REACTIVE_MSSQL_CLIENT) + || capabilities.isPresent(Capability.REACTIVE_MYSQL_CLIENT) + || capabilities.isPresent(Capability.REACTIVE_ORACLE_CLIENT) + || capabilities.isPresent(Capability.REACTIVE_PG_CLIENT); + recorder.setupVertxTracer(beanContainerBuildItem.getValue(), sqlClientAvailable); } @BuildStep diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/instrumentation/InstrumentationProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/instrumentation/InstrumentationProcessor.java index d8aa5e59bd0cb..ff6e4dda31e2e 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/instrumentation/InstrumentationProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/instrumentation/InstrumentationProcessor.java @@ -20,6 +20,7 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.AdditionalIndexedClassesBuildItem; import io.quarkus.opentelemetry.deployment.tracing.TracerEnabled; +import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.InstrumentationRecorder; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.grpc.GrpcTracingClientInterceptor; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.grpc.GrpcTracingServerInterceptor; @@ -71,17 +72,21 @@ public boolean getAsBoolean() { } @BuildStep(onlyIf = GrpcExtensionAvailable.class) - void grpcTracers(BuildProducer additionalBeans) { - additionalBeans.produce(new AdditionalBeanBuildItem(GrpcTracingServerInterceptor.class)); - additionalBeans.produce(new AdditionalBeanBuildItem(GrpcTracingClientInterceptor.class)); + void grpcTracers(BuildProducer additionalBeans, OTelBuildConfig config) { + if (config.instrument().grpc()) { + additionalBeans.produce(new AdditionalBeanBuildItem(GrpcTracingServerInterceptor.class)); + additionalBeans.produce(new AdditionalBeanBuildItem(GrpcTracingClientInterceptor.class)); + } } @BuildStep void registerRestClientClassicProvider( Capabilities capabilities, BuildProducer additionalIndexed, - BuildProducer additionalBeans) { - if (capabilities.isPresent(Capability.REST_CLIENT) && capabilities.isMissing(Capability.REST_CLIENT_REACTIVE)) { + BuildProducer additionalBeans, + OTelBuildConfig config) { + if (capabilities.isPresent(Capability.REST_CLIENT) && capabilities.isMissing(Capability.REST_CLIENT_REACTIVE) + && config.instrument().restClientClassic()) { additionalIndexed.produce(new AdditionalIndexedClassesBuildItem(OpenTelemetryClientFilter.class.getName())); additionalBeans.produce(new AdditionalBeanBuildItem(OpenTelemetryClientFilter.class)); } @@ -90,8 +95,9 @@ void registerRestClientClassicProvider( @BuildStep void registerReactiveMessagingMessageDecorator( Capabilities capabilities, - BuildProducer additionalBeans) { - if (capabilities.isPresent(Capability.SMALLRYE_REACTIVE_MESSAGING)) { + BuildProducer additionalBeans, + OTelBuildConfig config) { + if (capabilities.isPresent(Capability.SMALLRYE_REACTIVE_MESSAGING) && config.instrument().reactiveMessaging()) { additionalBeans.produce(new AdditionalBeanBuildItem(ReactiveMessagingTracingOutgoingDecorator.class)); additionalBeans.produce(new AdditionalBeanBuildItem(ReactiveMessagingTracingIncomingDecorator.class)); additionalBeans.produce(new AdditionalBeanBuildItem(ReactiveMessagingTracingEmitterDecorator.class)); @@ -115,35 +121,27 @@ VertxOptionsConsumerBuildItem vertxTracingOptions( // RESTEasy and Vert.x web @BuildStep - void registerResteasyClassicAndOrResteasyReactiveProvider( + void registerResteasyClassicAndOrResteasyReactiveProvider(OTelBuildConfig config, Capabilities capabilities, BuildProducer resteasyJaxrsProviderBuildItemBuildProducer) { - - boolean isResteasyClassicAvailable = capabilities.isPresent(Capability.RESTEASY); - - if (!isResteasyClassicAvailable) { - // if RestEasy is not available then no need to continue - return; + if (capabilities.isPresent(Capability.RESTEASY) && config.instrument().resteasyClassic()) { + resteasyJaxrsProviderBuildItemBuildProducer + .produce(new ResteasyJaxrsProviderBuildItem(OpenTelemetryClassicServerFilter.class.getName())); } - - resteasyJaxrsProviderBuildItemBuildProducer - .produce(new ResteasyJaxrsProviderBuildItem(OpenTelemetryClassicServerFilter.class.getName())); } @BuildStep void resteasyReactiveIntegration( Capabilities capabilities, BuildProducer containerRequestFilterBuildItemBuildProducer, - BuildProducer preExceptionMapperHandlerBuildItemBuildProducer) { - - if (!capabilities.isPresent(Capability.RESTEASY_REACTIVE)) { - // if RESTEasy Reactive is not available then no need to continue - return; + BuildProducer preExceptionMapperHandlerBuildItemBuildProducer, + OTelBuildConfig config) { + if (capabilities.isPresent(Capability.RESTEASY_REACTIVE) && config.instrument().resteasyReactive()) { + containerRequestFilterBuildItemBuildProducer + .produce(new CustomContainerRequestFilterBuildItem(OpenTelemetryReactiveServerFilter.class.getName())); + preExceptionMapperHandlerBuildItemBuildProducer + .produce(new PreExceptionMapperHandlerBuildItem(new AttachExceptionHandler())); } - containerRequestFilterBuildItemBuildProducer - .produce(new CustomContainerRequestFilterBuildItem(OpenTelemetryReactiveServerFilter.class.getName())); - preExceptionMapperHandlerBuildItemBuildProducer - .produce(new PreExceptionMapperHandlerBuildItem(new AttachExceptionHandler())); } } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestSpanExporter.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestSpanExporter.java index 5f934bac68810..533e3ca62cd5b 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestSpanExporter.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestSpanExporter.java @@ -51,7 +51,8 @@ public List getFinishedSpanItems(int spanCount) { } public void assertSpanCount(int spanCount) { - await().atMost(30, SECONDS).untilAsserted(() -> assertEquals(spanCount, finishedSpanItems.size())); + await().atMost(30, SECONDS).untilAsserted( + () -> assertEquals(spanCount, finishedSpanItems.size(), "Spans: " + finishedSpanItems.toString())); } public void reset() { diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java new file mode 100644 index 0000000000000..5c6bb07a37f23 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java @@ -0,0 +1,100 @@ +package io.quarkus.opentelemetry.deployment.instrumentation; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; +import java.util.List; + +import jakarta.inject.Inject; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.grpc.GrpcClient; +import io.quarkus.grpc.GrpcService; +import io.quarkus.opentelemetry.deployment.Greeter; +import io.quarkus.opentelemetry.deployment.GreeterBean; +import io.quarkus.opentelemetry.deployment.GreeterClient; +import io.quarkus.opentelemetry.deployment.GreeterGrpc; +import io.quarkus.opentelemetry.deployment.HelloProto; +import io.quarkus.opentelemetry.deployment.HelloReply; +import io.quarkus.opentelemetry.deployment.HelloReplyOrBuilder; +import io.quarkus.opentelemetry.deployment.HelloRequest; +import io.quarkus.opentelemetry.deployment.HelloRequestOrBuilder; +import io.quarkus.opentelemetry.deployment.MutinyGreeterGrpc; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.mutiny.Uni; + +public class GrpcOpenInstrumentationDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withApplicationRoot(root -> root + .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) + .addClasses(HelloService.class) + .addClasses(GreeterGrpc.class, MutinyGreeterGrpc.class, + Greeter.class, GreeterBean.class, GreeterClient.class, + HelloProto.class, HelloRequest.class, HelloRequestOrBuilder.class, + HelloReply.class, HelloReplyOrBuilder.class) + .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + .withConfigurationResource("application-default.properties") + .overrideConfigKey("quarkus.grpc.clients.hello.host", "localhost") + .overrideConfigKey("quarkus.grpc.clients.hello.port", "9001") + .overrideConfigKey("quarkus.otel.instrument.grpc", "false"); + + @Inject + TestSpanExporter spanExporter; + + @GrpcClient + Greeter hello; + + @AfterEach + void tearDown() { + spanExporter.reset(); + } + + @Test + void testTratestTracingDisabled() { + String response = hello.sayHello( + HelloRequest.newBuilder().setName("ping").build()) + .map(HelloReply::getMessage) + .await().atMost(Duration.ofSeconds(5)); + assertEquals("Hello ping", response); + + List spans = spanExporter.getFinishedSpanItems(1); + assertEquals(1, spans.size()); + + SpanData internal = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); + assertEquals("span.internal", internal.getName()); + assertEquals("value", internal.getAttributes().get(stringKey("grpc.internal"))); + } + + @GrpcService + public static class HelloService implements Greeter { + + @Inject + Tracer tracer; + + @Override + public Uni sayHello(HelloRequest request) { + Span span = tracer.spanBuilder("span.internal") + .setSpanKind(INTERNAL) + .setAttribute("grpc.internal", "value") + .startSpan(); + span.end(); + return Uni.createFrom().item(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()); + } + } + +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxEventBusInstrumentationDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxEventBusInstrumentationDisabledTest.java new file mode 100644 index 0000000000000..8956b57879970 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxEventBusInstrumentationDisabledTest.java @@ -0,0 +1,92 @@ +package io.quarkus.opentelemetry.deployment.instrumentation; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; +import static java.net.HttpURLConnection.HTTP_OK; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; +import io.quarkus.opentelemetry.deployment.common.TestUtil; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.vertx.ConsumeEvent; +import io.restassured.RestAssured; +import io.vertx.core.eventbus.EventBus; +import io.vertx.ext.web.Router; + +public class VertxEventBusInstrumentationDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .withApplicationRoot(root -> root + .addClasses(Events.class, TestUtil.class, TestSpanExporter.class, TestSpanExporterProvider.class) + .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + .overrideConfigKey("quarkus.otel.traces.exporter", "test-span-exporter") + .overrideConfigKey("quarkus.otel.metrics.exporter", "none") + .overrideConfigKey("quarkus.otel.logs.exporter", "none") + .overrideConfigKey("quarkus.otel.bsp.schedule.delay", "200") + .overrideConfigKey("quarkus.otel.instrument.vertx-event-bus", "false"); + + @Inject + TestSpanExporter spanExporter; + + @AfterEach + void tearDown() { + spanExporter.reset(); + } + + @Test + void testTracingDisabled() throws Exception { + + RestAssured.when().get("/hello/event") + .then() + .statusCode(HTTP_OK) + .body(equalTo("BAR")); + + // http request and dummy + List spans = spanExporter.getFinishedSpanItems(2); + assertEquals(2, spans.size()); + + SpanData internal = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); + assertEquals("io.quarkus.vertx.opentelemetry", internal.getName()); + assertEquals("dummy", internal.getAttributes().get(stringKey("test.message"))); + } + + @Singleton + public static class Events { + + @Inject + Tracer tracer; + + @ConsumeEvent("foo") + String echo(String foo) { + tracer.spanBuilder("io.quarkus.vertx.opentelemetry").startSpan() + .setAttribute("test.message", "dummy") + .end(); + return foo.toUpperCase(); + } + + void registerRoutes(@Observes Router router, EventBus eventBus) { + router.get("/hello/event").handler(rc -> { + eventBus.request("foo", "bar").onComplete(r -> rc.end(r.result().body().toString())); + }); + } + } + +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxHttpInstrumentationDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxHttpInstrumentationDisabledTest.java new file mode 100644 index 0000000000000..eb8cc3eb05375 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxHttpInstrumentationDisabledTest.java @@ -0,0 +1,85 @@ +package io.quarkus.opentelemetry.deployment.instrumentation; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; +import static java.net.HttpURLConnection.HTTP_OK; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; +import io.quarkus.opentelemetry.deployment.common.TestUtil; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.vertx.core.eventbus.EventBus; +import io.vertx.ext.web.Router; + +public class VertxHttpInstrumentationDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .withApplicationRoot(root -> root + .addClasses(Events.class, TestUtil.class, TestSpanExporter.class, + TestSpanExporterProvider.class) + .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + .overrideConfigKey("quarkus.otel.traces.exporter", "test-span-exporter") + .overrideConfigKey("quarkus.otel.metrics.exporter", "none") + .overrideConfigKey("quarkus.otel.logs.exporter", "none") + .overrideConfigKey("quarkus.otel.bsp.schedule.delay", "200") + .overrideConfigKey("quarkus.otel.instrument.vertx-http", "false"); + + @Inject + TestSpanExporter spanExporter; + + @AfterEach + void tearDown() { + spanExporter.reset(); + } + + @Test + void testTracingDisabled() throws Exception { + RestAssured.when().get("/hello/foo") + .then() + .statusCode(HTTP_OK) + .body(equalTo("oof")); + + List spans = spanExporter.getFinishedSpanItems(1); + assertEquals(1, spans.size()); + + SpanData internal = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); + assertEquals("io.quarkus.vertx.opentelemetry", internal.getName()); + assertEquals("dummy", internal.getAttributes().get(stringKey("test.message"))); + } + + @Singleton + public static class Events { + + @Inject + Tracer tracer; + + void registerRoutes(@Observes Router router, EventBus eventBus) { + router.get("/hello/foo").handler(rc -> { + tracer.spanBuilder("io.quarkus.vertx.opentelemetry").startSpan() + .setAttribute("test.message", "dummy") + .end(); + rc.end("oof"); + }); + } + } + +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/InstrumentBuildTimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/InstrumentBuildTimeConfig.java new file mode 100644 index 0000000000000..09ecb1532018b --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/InstrumentBuildTimeConfig.java @@ -0,0 +1,41 @@ +package io.quarkus.opentelemetry.runtime.config.build; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.smallrye.config.WithDefault; + +@ConfigGroup +public interface InstrumentBuildTimeConfig { + + /** + * Enables instrumentation for gRPC. + */ + @WithDefault("true") + boolean grpc(); + + /** + * Enables instrumentation for SmallRye Reactive Messaging. + */ + @WithDefault("true") + boolean reactiveMessaging(); + + /** + * Enables instrumentation for JAX-RS Rest Client backed by RESTEasy Classic. + */ + @WithDefault("true") + boolean restClientClassic(); + + /** + * Enables instrumentation for RESTEasy Reactive. + */ + @WithDefault("true") + boolean resteasyReactive(); + + /** + * Enables instrumentation for RESTEasy Classic. + */ + @WithDefault("true") + boolean resteasyClassic(); + + // NOTE: agroal, graphql and scheduler have their own config properties + +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java index e7d2620b9c8de..679cf07f40d4c 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java @@ -18,6 +18,7 @@ @ConfigMapping(prefix = "quarkus.otel") @ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) public interface OTelBuildConfig { + String INSTRUMENTATION_NAME = "io.quarkus.opentelemetry"; /** @@ -61,4 +62,9 @@ public interface OTelBuildConfig { */ @WithDefault(TRACE_CONTEXT + "," + BAGGAGE) List propagators(); + + /** + * Enable/disable instrumentation for specific technologies. + */ + InstrumentBuildTimeConfig instrument(); } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/InstrumentRuntimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/InstrumentRuntimeConfig.java new file mode 100644 index 0000000000000..f5c5cdddd104b --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/InstrumentRuntimeConfig.java @@ -0,0 +1,27 @@ +package io.quarkus.opentelemetry.runtime.config.runtime; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.smallrye.config.WithDefault; + +@ConfigGroup +public interface InstrumentRuntimeConfig { + + /** + * Enables instrumentation for Vert.x HTTP. + */ + @WithDefault("true") + boolean vertxHttp(); + + /** + * Enables instrumentation for Vert.x Event Bus. + */ + @WithDefault("true") + boolean vertxEventBus(); + + /** + * Enables instrumentation for Vert.x SQL Client. + */ + @WithDefault("true") + boolean vertxSqlClient(); + +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java index d38e1fc83fe7a..f428629d4f957 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java @@ -71,4 +71,9 @@ public interface OTelRuntimeConfig { @WithName("experimental.shutdown-wait-time") @WithDefault("1s") Duration experimentalShutdownWaitTime(); + + /** + * Enable/disable instrumentation for specific technologies. + */ + InstrumentRuntimeConfig instrument(); } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/InstrumentationRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/InstrumentationRecorder.java index 9613b7a13eebc..105eb7f7a1881 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/InstrumentationRecorder.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/InstrumentationRecorder.java @@ -1,16 +1,20 @@ package io.quarkus.opentelemetry.runtime.tracing.intrumentation; +import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import io.opentelemetry.api.OpenTelemetry; import io.quarkus.arc.runtime.BeanContainer; +import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.EventBusInstrumenterVertxTracer; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.HttpInstrumenterVertxTracer; +import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.InstrumenterVertxTracer; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.OpenTelemetryVertxMetricsFactory; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.OpenTelemetryVertxTracer; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.OpenTelemetryVertxTracingFactory; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.SqlClientInstrumenterVertxTracer; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; import io.vertx.core.VertxOptions; import io.vertx.core.metrics.MetricsOptions; @@ -21,6 +25,12 @@ public class InstrumentationRecorder { public static final OpenTelemetryVertxTracingFactory FACTORY = new OpenTelemetryVertxTracingFactory(); + private final RuntimeValue config; + + public InstrumentationRecorder(RuntimeValue config) { + this.config = config; + } + /* RUNTIME INIT */ public Consumer getVertxTracingOptions() { TracingOptions tracingOptions = new TracingOptions() @@ -29,13 +39,19 @@ public Consumer getVertxTracingOptions() { } /* RUNTIME INIT */ - public void setupVertxTracer(BeanContainer beanContainer) { + public void setupVertxTracer(BeanContainer beanContainer, boolean sqlClientAvailable) { OpenTelemetry openTelemetry = beanContainer.beanInstance(OpenTelemetry.class); - OpenTelemetryVertxTracer openTelemetryVertxTracer = new OpenTelemetryVertxTracer(List.of( - new HttpInstrumenterVertxTracer(openTelemetry), - new EventBusInstrumenterVertxTracer(openTelemetry), - // TODO - Selectively register this in the recorder if the SQL Client is available. - new SqlClientInstrumenterVertxTracer(openTelemetry))); + List> tracers = new ArrayList<>(3); + if (config.getValue().instrument().vertxHttp()) { + tracers.add(new HttpInstrumenterVertxTracer(openTelemetry)); + } + if (config.getValue().instrument().vertxEventBus()) { + tracers.add(new EventBusInstrumenterVertxTracer(openTelemetry)); + } + if (sqlClientAvailable && config.getValue().instrument().vertxSqlClient()) { + tracers.add(new SqlClientInstrumenterVertxTracer(openTelemetry)); + } + OpenTelemetryVertxTracer openTelemetryVertxTracer = new OpenTelemetryVertxTracer(tracers); FACTORY.getVertxTracerDelegator().setDelegate(openTelemetryVertxTracer); } diff --git a/integration-tests/opentelemetry-scheduler/src/test/java/io/quarkus/it/opentelemetry/scheduler/OpenTelemetrySchedulerTest.java b/integration-tests/opentelemetry-scheduler/src/test/java/io/quarkus/it/opentelemetry/scheduler/OpenTelemetrySchedulerTest.java index 28652a8ddd4c5..ed3aba8edc23e 100644 --- a/integration-tests/opentelemetry-scheduler/src/test/java/io/quarkus/it/opentelemetry/scheduler/OpenTelemetrySchedulerTest.java +++ b/integration-tests/opentelemetry-scheduler/src/test/java/io/quarkus/it/opentelemetry/scheduler/OpenTelemetrySchedulerTest.java @@ -2,6 +2,7 @@ import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -9,8 +10,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; @@ -33,7 +36,7 @@ public void schedulerSpanTest() { assertCounter("/scheduler/count/job-definition", 1, Duration.ofSeconds(3)); // ------- SPAN ASSERTS ------- - List> spans = getSpans(); + List> spans = getSpans("myCounter", "myJobDefinition"); assertJobSpan(spans, "myCounter", DURATION_IN_NANOSECONDS); // identity assertJobSpan(spans, "myJobDefinition", DURATION_IN_NANOSECONDS); // identity @@ -62,9 +65,20 @@ private void assertCounter(String counterPath, int expectedCount, Duration timeo } - private List> getSpans() { - return get("/export").body().as(new TypeRef<>() { + private List> getSpans(String... expectedNames) { + AtomicReference>> ret = new AtomicReference<>(Collections.emptyList()); + await().atMost(15, SECONDS).until(() -> { + List> spans = get("/export").body().as(new TypeRef<>() { + }); + for (String name : expectedNames) { + if (spans.stream().filter(map -> map.get("name").equals(name)).findAny().isEmpty()) { + return false; + } + } + ret.set(spans); + return true; }); + return ret.get(); } private void assertJobSpan(List> spans, String expectedName, long expectedDuration) { @@ -82,6 +96,7 @@ private void assertJobSpan(List> spans, String expectedName, "' is not longer than 100ms, actual duration: " + delta + " (ns)"); } + @SuppressWarnings("unchecked") private void assertErrorJobSpan(List> spans, String expectedName, long expectedDuration, String expectedErrorMessage) { assertJobSpan(spans, expectedName, expectedDuration);