Skip to content

Commit c5cc65f

Browse files
authored
Merge pull request #34250 from Sgitario/34212
Propagate Smallrye Context when switching REST Client context
2 parents a72abac + 57e57b9 commit c5cc65f

File tree

6 files changed

+157
-46
lines changed

6 files changed

+157
-46
lines changed

independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSwitchToRequestContextRestHandler.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;
77
import org.jboss.resteasy.reactive.client.spi.ClientRestHandler;
88

9+
import io.smallrye.mutiny.infrastructure.Infrastructure;
910
import io.vertx.core.Context;
1011
import io.vertx.core.Handler;
1112
import io.vertx.core.Vertx;
@@ -29,10 +30,12 @@ public void handle(RestClientRequestContext requestContext) throws Exception {
2930
requestContext.resume(new Executor() {
3031
@Override
3132
public void execute(Runnable command) {
33+
// This is necessary to propagate the Smallrye context which is required for OpenTelemetry
34+
Runnable decorated = Infrastructure.decorate(command);
3235
captured.runOnContext(new Handler<Void>() {
3336
@Override
3437
public void handle(Void unused) {
35-
command.run();
38+
decorated.run();
3639
}
3740
});
3841
}

integration-tests/opentelemetry-reactive/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
<artifactId>awaitility</artifactId>
5858
<scope>test</scope>
5959
</dependency>
60+
<dependency>
61+
<groupId>com.github.tomakehurst</groupId>
62+
<artifactId>wiremock-jre8-standalone</artifactId>
63+
<scope>test</scope>
64+
</dependency>
6065

6166
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
6267
<dependency>

integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/OpenTelemetryReactiveClientTest.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_ROUTE;
88
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_STATUS_CODE;
99
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
10+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpanByKindAndParentId;
11+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpans;
1012
import static io.restassured.RestAssured.given;
11-
import static io.restassured.RestAssured.when;
1213
import static java.net.HttpURLConnection.HTTP_OK;
13-
import static java.util.stream.Collectors.toList;
1414
import static java.util.stream.Collectors.toSet;
1515
import static org.awaitility.Awaitility.await;
1616
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -28,7 +28,6 @@
2828

2929
import io.opentelemetry.api.trace.SpanKind;
3030
import io.quarkus.test.junit.QuarkusTest;
31-
import io.restassured.common.mapper.TypeRef;
3231
import io.smallrye.mutiny.Uni;
3332
import io.vertx.core.http.HttpMethod;
3433

@@ -113,23 +112,4 @@ void post() {
113112
assertEquals("helloPost", internal.get("name"));
114113
assertEquals(internal.get("parentSpanId"), server.get("spanId"));
115114
}
116-
117-
private static List<Map<String, Object>> getSpans() {
118-
return when().get("/export").body().as(new TypeRef<>() {
119-
});
120-
}
121-
122-
private static Map<String, Object> getSpanByKindAndParentId(List<Map<String, Object>> spans, SpanKind kind,
123-
Object parentSpanId) {
124-
List<Map<String, Object>> span = getSpansByKindAndParentId(spans, kind, parentSpanId);
125-
assertEquals(1, span.size());
126-
return span.get(0);
127-
}
128-
129-
private static List<Map<String, Object>> getSpansByKindAndParentId(List<Map<String, Object>> spans, SpanKind kind,
130-
Object parentSpanId) {
131-
return spans.stream()
132-
.filter(map -> map.get("kind").equals(kind.toString()))
133-
.filter(map -> map.get("parentSpanId").equals(parentSpanId)).collect(toList());
134-
}
135115
}

integration-tests/opentelemetry-reactive/src/test/java/io/quarkus/it/opentelemetry/reactive/OpenTelemetryReactiveTest.java

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
import static io.opentelemetry.api.trace.SpanKind.SERVER;
66
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
77
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL;
8+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpanByKindAndParentId;
9+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpans;
10+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpansByKindAndParentId;
811
import static io.restassured.RestAssured.given;
9-
import static io.restassured.RestAssured.when;
1012
import static java.net.HttpURLConnection.HTTP_OK;
11-
import static java.util.stream.Collectors.toList;
1213
import static java.util.stream.Collectors.toSet;
1314
import static org.awaitility.Awaitility.await;
1415
import static org.hamcrest.CoreMatchers.equalTo;
@@ -24,9 +25,7 @@
2425
import org.junit.jupiter.api.BeforeEach;
2526
import org.junit.jupiter.api.Test;
2627

27-
import io.opentelemetry.api.trace.SpanKind;
2828
import io.quarkus.test.junit.QuarkusTest;
29-
import io.restassured.common.mapper.TypeRef;
3029

3130
@QuarkusTest
3231
public class OpenTelemetryReactiveTest {
@@ -167,23 +166,4 @@ void multipleUsingCombine() {
167166
Map<String, Object> gokuInternal = getSpanByKindAndParentId(spans, INTERNAL, gokuServer.get("spanId"));
168167
assertEquals("helloGet", gokuInternal.get("name"));
169168
}
170-
171-
private static List<Map<String, Object>> getSpans() {
172-
return when().get("/export").body().as(new TypeRef<>() {
173-
});
174-
}
175-
176-
private static Map<String, Object> getSpanByKindAndParentId(List<Map<String, Object>> spans, SpanKind kind,
177-
Object parentSpanId) {
178-
List<Map<String, Object>> span = getSpansByKindAndParentId(spans, kind, parentSpanId);
179-
assertEquals(1, span.size());
180-
return span.get(0);
181-
}
182-
183-
private static List<Map<String, Object>> getSpansByKindAndParentId(List<Map<String, Object>> spans, SpanKind kind,
184-
Object parentSpanId) {
185-
return spans.stream()
186-
.filter(map -> map.get("kind").equals(kind.toString()))
187-
.filter(map -> map.get("parentSpanId").equals(parentSpanId)).collect(toList());
188-
}
189169
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package io.quarkus.it.opentelemetry.reactive;
2+
3+
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
4+
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
5+
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
6+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpanByKindAndParentId;
7+
import static io.quarkus.it.opentelemetry.reactive.Utils.getSpans;
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
import java.net.URI;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
import jakarta.annotation.PostConstruct;
15+
import jakarta.enterprise.context.ApplicationScoped;
16+
import jakarta.ws.rs.GET;
17+
import jakarta.ws.rs.Path;
18+
19+
import org.eclipse.microprofile.config.inject.ConfigProperty;
20+
import org.eclipse.microprofile.rest.client.RestClientBuilder;
21+
import org.junit.jupiter.api.Test;
22+
23+
import com.github.tomakehurst.wiremock.WireMockServer;
24+
import com.github.tomakehurst.wiremock.client.WireMock;
25+
26+
import io.opentelemetry.instrumentation.annotations.WithSpan;
27+
import io.quarkus.runtime.Startup;
28+
import io.quarkus.test.common.QuarkusTestResource;
29+
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
30+
import io.quarkus.test.junit.QuarkusTest;
31+
32+
@QuarkusTestResource(restrictToAnnotatedClass = true, value = OpenTelemetryWithSpanAtStartupTest.MyWireMockResource.class)
33+
@QuarkusTest
34+
public class OpenTelemetryWithSpanAtStartupTest {
35+
36+
private static final int WIREMOCK_PORT = 20001;
37+
private static final String STARTUP_BEAN_ENABLED_PROPERTY = "startup.bean.enabled";
38+
39+
@Test
40+
void testGeneratedSpansUsingRestClientReactive() {
41+
List<Map<String, Object>> spans = getSpans();
42+
assertEquals(2, spans.size());
43+
44+
// First span is the callWireMockClient method. It does not have a parent span.
45+
Map<String, Object> client = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000");
46+
assertEquals("StartupBean.callWireMockClient", client.get("name"));
47+
48+
// We should get one client span, from the internal method.
49+
Map<String, Object> server = getSpanByKindAndParentId(spans, CLIENT, client.get("spanId"));
50+
assertEquals("GET", server.get("name"));
51+
}
52+
53+
@Startup
54+
@ApplicationScoped
55+
public static class StartupBean {
56+
57+
@ConfigProperty(name = STARTUP_BEAN_ENABLED_PROPERTY, defaultValue = "false")
58+
boolean enabled;
59+
60+
@PostConstruct
61+
void onStart() {
62+
if (enabled) {
63+
callWireMockClient();
64+
}
65+
}
66+
67+
@WithSpan
68+
public void callWireMockClient() {
69+
RestClientBuilder.newBuilder()
70+
.baseUri(URI.create("http://localhost:" + WIREMOCK_PORT))
71+
.build(WireMockRestClient.class)
72+
.call();
73+
}
74+
}
75+
76+
@Path("/stub")
77+
public interface WireMockRestClient {
78+
79+
@GET
80+
void call();
81+
}
82+
83+
public static class MyWireMockResource implements QuarkusTestResourceLifecycleManager {
84+
85+
WireMockServer wireMockServer;
86+
87+
@Override
88+
public Map<String, String> start() {
89+
wireMockServer = new WireMockServer(WIREMOCK_PORT);
90+
wireMockServer.stubFor(
91+
WireMock.get(WireMock.urlMatching("/stub"))
92+
.willReturn(ok()));
93+
wireMockServer.start();
94+
95+
return Map.of(STARTUP_BEAN_ENABLED_PROPERTY, Boolean.TRUE.toString());
96+
}
97+
98+
@Override
99+
public synchronized void stop() {
100+
if (wireMockServer != null) {
101+
wireMockServer.stop();
102+
wireMockServer = null;
103+
}
104+
}
105+
}
106+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.quarkus.it.opentelemetry.reactive;
2+
3+
import static io.restassured.RestAssured.when;
4+
import static java.util.stream.Collectors.toList;
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
import io.opentelemetry.api.trace.SpanKind;
11+
import io.restassured.common.mapper.TypeRef;
12+
13+
public final class Utils {
14+
15+
private Utils() {
16+
17+
}
18+
19+
public static List<Map<String, Object>> getSpans() {
20+
return when().get("/export").body().as(new TypeRef<>() {
21+
});
22+
}
23+
24+
public static Map<String, Object> getSpanByKindAndParentId(List<Map<String, Object>> spans, SpanKind kind,
25+
Object parentSpanId) {
26+
List<Map<String, Object>> span = getSpansByKindAndParentId(spans, kind, parentSpanId);
27+
assertEquals(1, span.size());
28+
return span.get(0);
29+
}
30+
31+
public static List<Map<String, Object>> getSpansByKindAndParentId(List<Map<String, Object>> spans, SpanKind kind,
32+
Object parentSpanId) {
33+
return spans.stream()
34+
.filter(map -> map.get("kind").equals(kind.toString()))
35+
.filter(map -> map.get("parentSpanId").equals(parentSpanId)).collect(toList());
36+
}
37+
}

0 commit comments

Comments
 (0)