diff --git a/extensions/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/deployment/pom.xml
index 88a08f63390cb..bb030a1ca3769 100644
--- a/extensions/opentelemetry/deployment/pom.xml
+++ b/extensions/opentelemetry/deployment/pom.xml
@@ -51,7 +51,7 @@
io.quarkus
- quarkus-resteasy-reactive-spi-deployment
+ quarkus-resteasy-reactive-server-spi-deployment
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 b73e6ab0cc1fc..1bac28f2b0fe3 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
@@ -25,9 +25,11 @@
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.AttachExceptionHandler;
import io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy.OpenTelemetryClassicServerFilter;
import io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy.OpenTelemetryReactiveServerFilter;
import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem;
+import io.quarkus.resteasy.reactive.server.spi.PreExceptionMapperHandlerBuildItem;
import io.quarkus.resteasy.reactive.spi.CustomContainerRequestFilterBuildItem;
import io.quarkus.vertx.core.deployment.VertxOptionsConsumerBuildItem;
import io.vertx.core.VertxOptions;
@@ -125,19 +127,20 @@ void registerResteasyClassicAndOrResteasyReactiveProvider(
}
@BuildStep
- void registerResteasyReactiveProvider(
+ void resteasyReactiveIntegration(
Capabilities capabilities,
- BuildProducer containerRequestFilterBuildItemBuildProducer) {
+ BuildProducer containerRequestFilterBuildItemBuildProducer,
+ BuildProducer preExceptionMapperHandlerBuildItemBuildProducer) {
- boolean isResteasyReactiveAvailable = capabilities.isPresent(Capability.RESTEASY_REACTIVE);
-
- if (!isResteasyReactiveAvailable) {
- // if RestEasy is not available then no need to continue
+ if (!capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
+ // if RESTEasy Reactive is not available then no need to continue
return;
}
containerRequestFilterBuildItemBuildProducer
.produce(new CustomContainerRequestFilterBuildItem(OpenTelemetryReactiveServerFilter.class.getName()));
+ preExceptionMapperHandlerBuildItemBuildProducer
+ .produce(new PreExceptionMapperHandlerBuildItem(new AttachExceptionHandler()));
}
}
diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/resteasy/AttachExceptionHandler.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/resteasy/AttachExceptionHandler.java
new file mode 100644
index 0000000000000..ea6173d3aa131
--- /dev/null
+++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/intrumentation/resteasy/AttachExceptionHandler.java
@@ -0,0 +1,16 @@
+package io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy;
+
+import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
+import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
+
+import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
+
+public class AttachExceptionHandler implements ServerRestHandler {
+ @Override
+ public void handle(ResteasyReactiveRequestContext requestContext) throws Exception {
+ Throwable throwable = requestContext.getThrowable();
+ if (throwable != null) { // should always be true
+ LocalRootSpan.current().recordException(throwable);
+ }
+ }
+}
diff --git a/integration-tests/opentelemetry-reactive/pom.xml b/integration-tests/opentelemetry-reactive/pom.xml
index 2ebb98c0aee06..c213fc02432ba 100644
--- a/integration-tests/opentelemetry-reactive/pom.xml
+++ b/integration-tests/opentelemetry-reactive/pom.xml
@@ -62,6 +62,11 @@
wiremock-jre8-standalone
test
+
+ org.assertj
+ assertj-core
+ test
+
diff --git a/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ExporterResource.java b/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ExporterResource.java
index 1a6743c956d6a..0d8f6cd3c4956 100644
--- a/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ExporterResource.java
+++ b/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ExporterResource.java
@@ -4,15 +4,16 @@
import java.util.stream.Collectors;
import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
import io.opentelemetry.sdk.trace.data.SpanData;
+import io.opentelemetry.sdk.trace.internal.data.ExceptionEventData;
@Path("")
public class ExporterResource {
@@ -35,6 +36,20 @@ public List export() {
.collect(Collectors.toList());
}
+ @GET
+ @Path("/exportExceptionMessages")
+ public List exportExceptionMessages() {
+ return inMemorySpanExporter.getFinishedSpanItems()
+ .stream()
+ .filter(sd -> !sd.getName().contains("export") && !sd.getName().contains("reset"))
+ .filter(sd -> !sd.getEvents().isEmpty())
+ .flatMap(sd -> sd.getEvents().stream())
+ .filter(e -> e instanceof ExceptionEventData)
+ .map(e -> (ExceptionEventData) e)
+ .map(e -> e.getException().getMessage())
+ .collect(Collectors.toList());
+ }
+
@ApplicationScoped
static class InMemorySpanExporterProducer {
@Produces
diff --git a/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ReactiveResource.java b/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ReactiveResource.java
index a08c9f3fbaffc..02f94b5493f59 100644
--- a/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ReactiveResource.java
+++ b/integration-tests/opentelemetry-reactive/src/main/java/io/quarkus/it/opentelemetry/reactive/ReactiveResource.java
@@ -51,4 +51,18 @@ public Uni helloPost(String body) {
return Uni.createFrom().item("Hello " + body).onItem().delayIt().by(Duration.ofSeconds(2))
.eventually((Runnable) span::end);
}
+
+ @Path("blockingException")
+ @GET
+ public String blockingException() {
+ throw new RuntimeException("dummy");
+ }
+
+ @Path("reactiveException")
+ @GET
+ public Uni reactiveException() {
+ return Uni.createFrom().item(() -> {
+ throw new RuntimeException("dummy2");
+ });
+ }
}
diff --git a/integration-tests/opentelemetry-reactive/src/main/resources/application.properties b/integration-tests/opentelemetry-reactive/src/main/resources/application.properties
index f0d7b887e6dae..7485ece03853e 100644
--- a/integration-tests/opentelemetry-reactive/src/main/resources/application.properties
+++ b/integration-tests/opentelemetry-reactive/src/main/resources/application.properties
@@ -1,4 +1,4 @@
-quarkus.rest-client.client.url=${test.url}
+quarkus.rest-client.client.url=${test.url
quarkus.otel.bsp.schedule.delay=100
-quarkus.otel.bsp.export.timeout=5s
\ No newline at end of file
+quarkus.otel.bsp.export.timeout=5s
diff --git a/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/OpenTelemetryReactiveTest.java b/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/OpenTelemetryReactiveTest.java
index 0f899d27bc4b8..26bf417e99ca0 100644
--- a/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/OpenTelemetryReactiveTest.java
+++ b/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/OpenTelemetryReactiveTest.java
@@ -5,12 +5,14 @@
import static io.opentelemetry.api.trace.SpanKind.SERVER;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL;
+import static io.quarkus.it.opentelemetry.reactive.Utils.getExceptionEventData;
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpanByKindAndParentId;
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpans;
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpansByKindAndParentId;
import static io.restassured.RestAssured.given;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -53,6 +55,35 @@ void get() {
assertEquals(spans.get(0).get("traceId"), spans.get(1).get("traceId"));
}
+ @Test
+ void blockingException() {
+ given()
+ .when()
+ .get("/reactive/blockingException")
+ .then()
+ .statusCode(500);
+
+ assertExceptionRecorded();
+ }
+
+ @Test
+ void reactiveException() {
+ given()
+ .when()
+ .get("/reactive/reactiveException")
+ .then()
+ .statusCode(500);
+
+ assertExceptionRecorded();
+ }
+
+ private static void assertExceptionRecorded() {
+ await().atMost(5, TimeUnit.SECONDS).until(() -> getExceptionEventData().size() == 1);
+ assertThat(getExceptionEventData()).singleElement().satisfies(s -> {
+ assertThat(s).contains("dummy");
+ });
+ }
+
@Test
void post() {
given()
diff --git a/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/Utils.java b/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/Utils.java
index f05a3d0fce3b6..56ef345d5fabf 100644
--- a/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/Utils.java
+++ b/integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/Utils.java
@@ -21,6 +21,11 @@ public static List