Using SLF4J MDC for Request-Level Log Tracing in Asynchronous Java Applications

This article demonstrates how to employ SLF4J's MDC to attach a request identifier to log entries, enabling efficient filtering of all logs belonging to a single request even across asynchronous threads and thread pools, and shows an advanced decorator‑based solution for proper MDC propagation.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Using SLF4J MDC for Request-Level Log Tracing in Asynchronous Java Applications

1. Main Content

The SLF4J logging framework provides an MDC (Mapped Diagnostic Context) utility, which can be used to tag log messages with a request identifier, making it easy to locate all logs related to a specific request.

Basic usage involves calling MDC.put() at the entry point of a request to store a unique ID, and MDC.remove() at the exit to clear it. With a proper log4j2.xml configuration, the ID appears in the log output within curly braces.

public class Main {
    private static final String KEY = "requestId";
    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        // entry: set request ID
        MDC.put(KEY, UUID.randomUUID().toString());
        // log in main thread
        logger.debug("log in main thread 1");
        logger.debug("log in main thread 2");
        logger.debug("log in main thread 3");
        // exit: remove request ID
        MDC.remove(KEY);
    }
}

Running the program prints logs that contain the requestId, allowing you to filter logs with a simple grep requestId=xxx *.log command.

2. Advanced

When a request spawns asynchronous threads, the MDC value does not automatically propagate because MDC relies on ThreadLocal storage, which is thread‑specific. To make MDC work in async contexts, a decorator pattern can be applied.

public class MDCRunnable implements Runnable {
    private final Runnable runnable;
    private final Map<String, String> map;

    public MDCRunnable(Runnable runnable) {
        this.runnable = runnable;
        // capture current thread's MDC map
        this.map = MDC.getCopyOfContextMap();
    }

    @Override
    public void run() {
        // copy captured MDC values to the async thread
        for (Map.Entry<String, String> entry : map.entrySet()) {
            MDC.put(entry.getKey(), entry.getValue());
        }
        runnable.run();
        // clean up
        for (Map.Entry<String, String> entry : map.entrySet()) {
            MDC.remove(entry.getKey());
        }
    }
}

By wrapping any Runnable (or tasks submitted to an ExecutorService) with MDCRunnable, the requestId is correctly propagated to the new thread.

public class Main {
    private static final String KEY = "requestId";
    private static final Logger logger = LoggerFactory.getLogger(Main.class);
    private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();

    public static void main(String[] args) {
        MDC.put(KEY, UUID.randomUUID().toString());
        logger.debug("log in main thread");
        // async thread with MDC propagation
        new Thread(new MDCRunnable(() -> logger.debug("log in other thread"))).start();
        // thread pool with MDC propagation
        EXECUTOR.execute(new MDCRunnable(() -> logger.debug("log in other thread pool")));
        EXECUTOR.shutdown();
        MDC.remove(KEY);
    }
}

Executing the program now yields log entries from the main thread, the explicit thread, and the thread pool, all containing the same requestId.

3. Summary

The article shows how to use SLF4J's MDC to tag logs with a request identifier, enabling quick filtering of all logs belonging to a single request, and demonstrates a decorator‑based approach to make MDC work in asynchronous threads and thread pools.

Integrating MDC via AOP or similar techniques across the system can dramatically reduce debugging time during development and speed up log analysis in production environments.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaAsynchronousloggingThreadLocalslf4jmdc
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.