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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
