Skip to content

Commit 2d78bf0

Browse files
committed
Add test coverage for the fix quarkusio/quarkus#48057
1 parent 1cf9fa7 commit 2d78bf0

File tree

8 files changed

+283
-0
lines changed

8 files changed

+283
-0
lines changed

http/vertx/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
<groupId>io.quarkus</groupId>
2424
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
2525
</dependency>
26+
<dependency>
27+
<groupId>io.quarkus</groupId>
28+
<artifactId>quarkus-smallrye-health</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>io.quarkus</groupId>
32+
<artifactId>quarkus-smallrye-context-propagation</artifactId>
33+
</dependency>
2634
</dependencies>
2735
<profiles>
2836
<profile>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import java.util.List;
4+
5+
import jakarta.enterprise.context.ApplicationScoped;
6+
import jakarta.inject.Inject;
7+
import jakarta.ws.rs.GET;
8+
import jakarta.ws.rs.Path;
9+
import jakarta.ws.rs.core.Response;
10+
11+
import org.jboss.logmanager.MDC;
12+
13+
import io.smallrye.health.SmallRyeHealthReporter;
14+
15+
@Path("/external-health")
16+
@ApplicationScoped
17+
public class ExternalHealthEndpoint {
18+
19+
public static final String LOGGER_NAME = "test-logger";
20+
public static final String MDC_KEY = "endpoint.context";
21+
public static final String MDC_VALUE_PREFIX = "value-from-endpoint-";
22+
23+
@Inject
24+
SmallRyeHealthReporter healthReporter;
25+
26+
@Inject
27+
InMemoryLogHandler inMemoryLogHandler;
28+
29+
@GET
30+
public Response triggerHealthChecks() {
31+
InMemoryLogHandler.reset();
32+
MDC.put(MDC_KEY, MDC_VALUE_PREFIX + "ExternalEndpoint");
33+
// access the MDC context before calling the health reporter to cause the problem
34+
MDC.clear();
35+
// call the health reporter
36+
return Response.ok(healthReporter.getHealth().getPayload()).build();
37+
38+
}
39+
40+
@GET
41+
@Path("/log-records")
42+
public List<String> logRecords() {
43+
return inMemoryLogHandler.logRecords();
44+
}
45+
46+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.concurrent.CopyOnWriteArrayList;
6+
import java.util.logging.ErrorManager;
7+
import java.util.logging.Formatter;
8+
import java.util.logging.LogRecord;
9+
10+
import org.jboss.logmanager.ExtHandler;
11+
import org.jboss.logmanager.formatters.PatternFormatter;
12+
13+
public class InMemoryLogHandler extends ExtHandler {
14+
private static final PatternFormatter FORMATTER = new PatternFormatter(
15+
"%d{HH:mm:ss} %-5p endpoint_context=%X{endpoint.context} [%c{2.}] (%t) %s%n");
16+
17+
private static final List<String> recordList = new CopyOnWriteArrayList<>();
18+
19+
public List<String> logRecords() {
20+
return Collections.unmodifiableList(recordList);
21+
}
22+
23+
public static void reset() {
24+
recordList.clear();
25+
}
26+
27+
@Override
28+
public void publish(LogRecord record) {
29+
String loggerName = record.getLoggerName();
30+
if (loggerName != null && loggerName.equals(ExternalHealthEndpoint.LOGGER_NAME)) {
31+
final String formatted;
32+
final Formatter formatter = getFormatter();
33+
try {
34+
formatted = formatter.format(record);
35+
} catch (Exception ex) {
36+
reportError("Formatting error", ex, ErrorManager.FORMAT_FAILURE);
37+
return;
38+
}
39+
if (!formatted.isEmpty()) {
40+
recordList.add(formatted);
41+
}
42+
}
43+
}
44+
45+
@Override
46+
public Formatter getFormatter() {
47+
return FORMATTER;
48+
}
49+
50+
@Override
51+
public void flush() {
52+
}
53+
54+
@Override
55+
public void close() throws SecurityException {
56+
}
57+
58+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
import jakarta.enterprise.event.Observes;
5+
import jakarta.enterprise.inject.Produces;
6+
import jakarta.inject.Singleton;
7+
8+
import io.quarkus.bootstrap.logging.InitialConfigurator;
9+
import io.quarkus.runtime.ShutdownEvent;
10+
import io.quarkus.runtime.StartupEvent;
11+
12+
@ApplicationScoped
13+
public class InMemoryLogHandlerProducer {
14+
15+
public volatile boolean initialized = false;
16+
17+
@Produces
18+
@Singleton
19+
public InMemoryLogHandler inMemoryLogHandler() {
20+
return new InMemoryLogHandler();
21+
}
22+
23+
public boolean isInitialized() {
24+
return initialized;
25+
}
26+
27+
void onStart(@Observes StartupEvent ev, InMemoryLogHandler inMemoryLogHandler) {
28+
InitialConfigurator.DELAYED_HANDLER.addHandler(inMemoryLogHandler);
29+
initialized = true;
30+
}
31+
32+
void onStop(@Observes ShutdownEvent ev, InMemoryLogHandler inMemoryLogHandler) {
33+
initialized = false;
34+
InitialConfigurator.DELAYED_HANDLER.removeHandler(inMemoryLogHandler);
35+
}
36+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
5+
import org.eclipse.microprofile.health.HealthCheck;
6+
import org.eclipse.microprofile.health.HealthCheckResponse;
7+
import org.eclipse.microprofile.health.Liveness;
8+
import org.jboss.logging.Logger;
9+
import org.jboss.logmanager.MDC;
10+
11+
@Liveness
12+
@ApplicationScoped
13+
public class MdcPropagationHealthCheck1 implements HealthCheck {
14+
15+
private static final Logger LOG = Logger.getLogger(ExternalHealthEndpoint.LOGGER_NAME);
16+
17+
@Override
18+
public HealthCheckResponse call() {
19+
MDC.put(ExternalHealthEndpoint.MDC_KEY, ExternalHealthEndpoint.MDC_VALUE_PREFIX + getClass().getSimpleName());
20+
Object mdcValue = MDC.get(ExternalHealthEndpoint.MDC_KEY);
21+
LOG.info("MDC in health check: " + mdcValue);
22+
LOG.error("Test log with MDC");
23+
return HealthCheckResponse.up("mdc-propagation-check");
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
5+
import org.eclipse.microprofile.health.HealthCheck;
6+
import org.eclipse.microprofile.health.HealthCheckResponse;
7+
import org.eclipse.microprofile.health.Liveness;
8+
import org.jboss.logging.Logger;
9+
import org.jboss.logmanager.MDC;
10+
11+
@Liveness
12+
@ApplicationScoped
13+
public class MdcPropagationHealthCheck2 implements HealthCheck {
14+
15+
private static final Logger LOG = Logger.getLogger(ExternalHealthEndpoint.LOGGER_NAME);
16+
17+
@Override
18+
public HealthCheckResponse call() {
19+
MDC.put(ExternalHealthEndpoint.MDC_KEY, ExternalHealthEndpoint.MDC_VALUE_PREFIX + getClass().getSimpleName());
20+
Object mdcValue = MDC.get(ExternalHealthEndpoint.MDC_KEY);
21+
LOG.info("MDC in health check: " + mdcValue);
22+
LOG.error("Test log with MDC");
23+
return HealthCheckResponse.up("mdc-propagation-check");
24+
}
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import io.quarkus.runtime.annotations.RegisterForReflection;
4+
5+
@RegisterForReflection(targets = {
6+
ExternalHealthEndpoint.class,
7+
}, classNames = {
8+
"org.eclipse.parsson.JsonStringImpl",
9+
})
10+
public class MdcTestNativeConfiguration {
11+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package io.quarkus.ts.vertx;
2+
3+
import static io.restassured.RestAssured.given;
4+
import static org.awaitility.Awaitility.await;
5+
import static org.hamcrest.MatcherAssert.assertThat;
6+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
7+
import static org.hamcrest.Matchers.is;
8+
9+
import java.util.List;
10+
import java.util.concurrent.TimeUnit;
11+
12+
import org.junit.jupiter.api.BeforeEach;
13+
import org.junit.jupiter.api.RepeatedTest;
14+
import org.junit.jupiter.api.Tag;
15+
16+
import io.quarkus.test.scenarios.QuarkusScenario;
17+
18+
@Tag("https://github.com/quarkusio/quarkus/discussions/47481")
19+
@QuarkusScenario
20+
public class MdcContextPropagationIT {
21+
22+
@BeforeEach
23+
public void clearLogs() {
24+
InMemoryLogHandler.reset();
25+
}
26+
27+
@RepeatedTest(5)
28+
void testMdcIsPropagatedFromExternalEndpointToHealthCheck() {
29+
given()
30+
.when()
31+
.get("/external-health")
32+
.then()
33+
.statusCode(200);
34+
35+
await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> {
36+
List<String> records = given()
37+
.when()
38+
.get("/external-health/log-records")
39+
.then()
40+
.statusCode(200)
41+
.extract()
42+
.body()
43+
.jsonPath()
44+
.getList(".", String.class);
45+
46+
assertThat("Should have at least 2 log records", records.size(), greaterThanOrEqualTo(2));
47+
48+
boolean hasHealthCheck1Log = records.stream()
49+
.anyMatch(record -> record.contains("endpoint_context=value-from-endpoint-MdcPropagationHealthCheck1")
50+
&& record.contains("Test log with MDC"));
51+
52+
boolean hasHealthCheck2Log = records.stream()
53+
.anyMatch(record -> record.contains("endpoint_context=value-from-endpoint-MdcPropagationHealthCheck2")
54+
&& record.contains("Test log with MDC"));
55+
56+
assertThat("Should have log from MdcPropagationHealthCheck1 with correct MDC",
57+
hasHealthCheck1Log, is(true));
58+
assertThat("Should have log from MdcPropagationHealthCheck2 with correct MDC",
59+
hasHealthCheck2Log, is(true));
60+
61+
records.forEach(record -> {
62+
if (record.contains("MdcPropagationHealthCheck1")) {
63+
assertThat("HC1 log should not contain HC2 context",
64+
record.contains("MdcPropagationHealthCheck2"), is(false));
65+
}
66+
if (record.contains("MdcPropagationHealthCheck2")) {
67+
assertThat("HC2 log should not contain HC1 context",
68+
record.contains("MdcPropagationHealthCheck1"), is(false));
69+
}
70+
});
71+
});
72+
}
73+
74+
}

0 commit comments

Comments
 (0)