javaslf4jcompletable-futuremdc

How to retain slf4j MDC logging context in CompletableFuture?


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");
}

Solution

  • 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.