When executing async CompletableFuture
, the parent threadcontext and moreover the org.slf4j.MDC
context is lost.
This is bad as I'm using some kind of "fish tagging" to track logs from one request among multiple logfiles.
MDC.put("fishid", randomId())
Question: how can I retain that id during the tasks of CompletableFutures
in general?
List<CompletableFuture<UpdateHotelAllotmentsRsp>> futures =
tasks.stream()
.map(task -> CompletableFuture.supplyAsync(
() -> businesslogic(task))
.collect(Collectors.toList());
List results = futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
public void businesslogic(Task task) {
LOGGER.info("mdc fishtag context is lost here");
}
The most readable way I solved this problem was as below -
---------------Thread utils class--------------------
public static Runnable withMdc(Runnable runnable) {
Map<String, String> mdc = MDC.getCopyOfContextMap();
return () -> {
MDC.setContextMap(mdc);
runnable.run();
};
}
public static <U> Supplier<U> withMdc(Supplier<U> supplier) {
Map<String, String> mdc = MDC.getCopyOfContextMap();
return (Supplier) () -> {
MDC.setContextMap(mdc);
return supplier.get();
};
}
---------------Usage--------------
CompletableFuture.supplyAsync(withMdc(() -> someSupplier()))
.thenRunAsync(withMdc(() -> someRunnable())
....
WithMdc in ThreadUtils would have to be overloaded to include other functional interfaces which are accepted by CompletableFuture
Please note that the withMdc() method is statically imported to improve readability.