MDC — Mapped Diagnostic Context in Java Logging
Fast recall
Quick revision
Core answer
CompletableFuture, @Async, or reactive execution, so propagate context deliberately and restore or clear it on reusable threads.Interview-ready guide
Interview answer
MDC is a diagnostic key-value context exposed by SLF4J and supported by logging providers. A servlet filter can put a request ID into MDC before the chain, and a configured pattern such as %X{requestId} or a structured encoder can add it to later events. Setting a value alone does not force every output to print it. Traditional MDC is associated with the current thread, so an executor task, CompletableFuture, or reactive pipeline does not automatically inherit it. I use the framework's propagation mechanism for those boundaries. On a pooled thread I restore the previous context or clear my value in finally, otherwise the next request can receive the wrong ID.
MDC — Mapped Diagnostic Context in Java Logging
Direct Answer
SLF4J MDC exposes key-value diagnostic context normally associated with the current thread. Put a request or trace ID into the context and configure the provider's pattern or encoder to include it. MDC does not automatically follow work into thread pools, CompletableFuture, @Async, or reactive execution, so propagate context deliberately and restore or clear it on reusable threads.
Deep Dive & Architecture
MDC lets code attach context such as requestId to logging events without repeating that value in every call. The active provider holds the context and an output pattern such as %X{requestId} or a structured encoder decides whether it appears.
Traditional MDC is thread-associated. A servlet filter can set the request ID before the chain and restore or clear it in finally. A pooled thread is reused, so a leaked value can label the next request incorrectly.
Async work needs explicit support. Copying the context map in a task decorator can work for an executor; reactive frameworks have their own context and bridging mechanisms. Preserve any previous MDC map as well as clearing newly added keys so nested scopes remain correct.
Code Example
import org.slf4j.MDC;
import java.util.UUID;
public class RequestIdFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
MDC.put("requestId", UUID.randomUUID().toString());
try {
chain.doFilter(req, res); // all downstream logs carry requestId
} finally {
MDC.clear(); // CRITICAL: pooled threads reuse -> clear to avoid leaks
}
}
}
<pattern>%d %-5level [%X{requestId}] %logger - %msg%n</pattern>
Common Mistakes & Pitfalls
- Not clearing MDC: leftover values leak into the next pooled request's logs — always
MDC.clear()infinally. - Assuming it crosses threads: MDC is thread-local; async tasks/new threads lose it unless you copy it over.
- Storing large objects: keep MDC to small identifiers, not whole payloads.
Check your understanding
Answer these without looking back at the article.
- What is SLF4J and why is it used instead of logging directly with a framework?
- How do you create and use a Logger in Java with SLF4J?
- How do you configure logging in a Spring Boot application?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.