diff --git a/extensions/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/deployment/pom.xml index c042883b380157..4b5932e3dc77ed 100644 --- a/extensions/opentelemetry/deployment/pom.xml +++ b/extensions/opentelemetry/deployment/pom.xml @@ -45,6 +45,14 @@ io.quarkus quarkus-grpc-common-deployment + + io.quarkus + quarkus-resteasy-reactive-spi-deployment + + + io.quarkus + quarkus-resteasy-common-spi + 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 16bb2819c906fc..36b7648e37aa15 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 @@ -26,6 +26,9 @@ import io.quarkus.opentelemetry.runtime.tracing.intrumentation.grpc.GrpcTracingServerInterceptor; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.reactivemessaging.ReactiveMessagingTracingDecorator; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.restclient.OpenTelemetryClientFilter; +import io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy.OpenTelemetryServerFilter; +import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem; +import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; import io.quarkus.runtime.LaunchMode; import io.quarkus.vertx.core.deployment.VertxOptionsConsumerBuildItem; import io.vertx.core.VertxOptions; @@ -112,4 +115,26 @@ VertxOptionsConsumerBuildItem vertxTracingOptions(InstrumentationRecorder record LIBRARY_AFTER); } + // RESTEasy and Vert.x web + @BuildStep + void registerResteasyClassicAndOrResteasyReactiveProvider( + Capabilities capabilities, + BuildProducer resteasyJaxrsProviderBuildItemBuildProducer, + BuildProducer containerRequestFilterBuildItemBuildProducer) { + boolean isResteasyClassicAvailable = capabilities.isPresent(Capability.RESTEASY); + boolean isResteasyReactiveAvailable = capabilities.isPresent(Capability.RESTEASY_REACTIVE); + + if (!isResteasyClassicAvailable && !isResteasyReactiveAvailable) { + // if RestEasy is not available then no need to continue + return; + } + + if (isResteasyClassicAvailable) { + resteasyJaxrsProviderBuildItemBuildProducer + .produce(new ResteasyJaxrsProviderBuildItem(OpenTelemetryServerFilter.class.getName())); + } else { + containerRequestFilterBuildItemBuildProducer + .produce(new ContainerRequestFilterBuildItem.Builder(OpenTelemetryServerFilter.class.getName()).build()); + } + } } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java index 1e2f344aa40981..00107904f049fd 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java @@ -3,6 +3,7 @@ import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.SERVER; import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; +import static io.quarkus.opentelemetry.deployment.common.TestUtil.assertStringAttribute; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,7 +23,9 @@ import io.opentelemetry.extension.annotations.WithSpan; import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestUtil; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; @@ -31,6 +34,7 @@ public class OpenTelemetryHttpCDILegacyTest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .setArchiveProducer( () -> ShrinkWrap.create(JavaArchive.class) + .addClass(TestUtil.class) .addClass(HelloResource.class) .addClass(HelloBean.class) .addClass(TestSpanExporter.class)); @@ -55,6 +59,10 @@ void telemetry() { SpanData server = getSpanByKindAndParentId(spans, SERVER, "0000000000000000"); assertEquals("/hello", server.getName()); assertEquals(SERVER, server.getKind()); + // verify that OpenTelemetryServerFilter took place + assertStringAttribute(server, SemanticAttributes.CODE_NAMESPACE, + "io.quarkus.opentelemetry.deployment.OpenTelemetryHttpCDILegacyTest$HelloResource"); + assertStringAttribute(server, SemanticAttributes.CODE_FUNCTION, "hello"); SpanData internal = getSpanByKindAndParentId(spans, INTERNAL, server.getSpanId()); assertEquals("HelloBean.hello", internal.getName()); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java index c235f703747511..addd1335ef8c31 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java @@ -3,6 +3,7 @@ import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.SERVER; import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; +import static io.quarkus.opentelemetry.deployment.common.TestUtil.assertStringAttribute; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,7 +23,9 @@ import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestUtil; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; @@ -31,6 +34,7 @@ public class OpenTelemetryHttpCDITest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .setArchiveProducer( () -> ShrinkWrap.create(JavaArchive.class) + .addClass(TestUtil.class) .addClass(HelloResource.class) .addClass(HelloBean.class) .addClass(TestSpanExporter.class)); @@ -54,6 +58,10 @@ void telemetry() { final SpanData server = getSpanByKindAndParentId(spans, SERVER, "0000000000000000"); assertEquals("/hello", server.getName()); + // verify that OpenTelemetryServerFilter took place + assertStringAttribute(server, SemanticAttributes.CODE_NAMESPACE, + "io.quarkus.opentelemetry.deployment.OpenTelemetryHttpCDITest$HelloResource"); + assertStringAttribute(server, SemanticAttributes.CODE_FUNCTION, "hello"); final SpanData internalFromBean = getSpanByKindAndParentId(spans, INTERNAL, server.getSpanId()); assertEquals("HelloBean.hello", internalFromBean.getName()); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestUtil.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestUtil.java index bec058689979a8..c2c4911f50cc79 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestUtil.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/TestUtil.java @@ -1,15 +1,19 @@ package io.quarkus.opentelemetry.deployment.common; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.IdGenerator; import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.quarkus.arc.Unremovable; @@ -60,4 +64,8 @@ public static TextMapPropagator[] getTextMapPropagators(OpenTelemetry openTeleme privatePropagatorsField.setAccessible(true); return (TextMapPropagator[]) privatePropagatorsField.get(textMapPropagator); } + + public static void assertStringAttribute(SpanData spanData, AttributeKey attributeKey, String expectedValue) { + assertEquals(expectedValue, spanData.getAttributes().get(attributeKey), "Attribute Key Named:" + attributeKey.getKey()); + } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/resteasy/OpenTelemetryServerFilter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/resteasy/OpenTelemetryServerFilter.java new file mode 100644 index 00000000000000..e7eeb4396963d0 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/resteasy/OpenTelemetryServerFilter.java @@ -0,0 +1,31 @@ +package io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; + +/** + * Handles both RESTEasy Classic and RESTEasy Reactive (via Vert.x) + */ +@Provider +public class OpenTelemetryServerFilter implements ContainerRequestFilter { + + @Context + ResourceInfo resourceInfo; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + Span localRootSpan = LocalRootSpan.current(); + + localRootSpan.setAttribute(SemanticAttributes.CODE_NAMESPACE, resourceInfo.getResourceClass().getName()); + localRootSpan.setAttribute(SemanticAttributes.CODE_FUNCTION, resourceInfo.getResourceMethod().getName()); + } +}