MDC and Thread Context
Mapped Diagnostic Context (MDC) associates contextual fields such as request IDs or user IDs with log events produced during an execution context. In Java, MDC is commonly backed by thread-local state, so context must be managed carefully when work moves between threads.
MDC provides key-value data that logging frameworks can include automatically in log records. For example, a web request can put `requestId=abc123` into MDC so every log statement executed in the same context can include that identifier without passing it through every method call.
With SLF4J, MDC is accessed through `org.slf4j.MDC`. Typical code calls `MDC.put()` before processing begins and `MDC.remove()` or `MDC.clear()` when processing ends. A logging pattern can then reference MDC fields, such as `%X{requestId}` in Logback-style configurations.
MDC context is commonly associated with the current thread. This makes it convenient for synchronous request processing, but it also creates a boundary when execution moves to another thread through an executor, `CompletableFuture`, or another asynchronous mechanism. The new thread does not automatically acquire arbitrary MDC state merely because the originating thread had it.
Thread pools make cleanup particularly important. Worker threads are reused, so leaving MDC values behind can cause a later task to emit logs containing another request's context. A robust design establishes the desired context at task boundaries and removes or restores it afterward.
For asynchronous systems, context propagation needs an explicit strategy. Depending on the stack, this may involve wrapping tasks, using framework-supported context propagation, or adopting observability mechanisms that propagate trace context. MDC is primarily a logging-context mechanism; it should not be treated as a general-purpose storage mechanism for application state.
Key points
- MDC adds key-value context to log events without passing those values through every method.
- SLF4J exposes MDC operations such as `put`, `get`, `remove`, and `clear`.
- MDC is commonly implemented using thread-local state, so thread boundaries matter.
- Thread-pool tasks must establish and clean up MDC explicitly when appropriate.
- MDC is for diagnostic/logging context, not general application state.
Interview questions
What is MDC, and why would you use it?
MDC stands for Mapped Diagnostic Context and provides key-value data associated with the current logging context. A common use is storing a request ID so log messages can be correlated without adding the ID to every logging call. Logging configuration can include MDC values in each formatted event. This makes request-level troubleshooting easier.
Why can MDC cause problems with thread pools?
MDC is commonly associated with thread-local state, while executor threads are reused between tasks. If a task sets an MDC value and does not remove or restore it, a later task using the same worker can inherit stale context. That can make logs incorrectly appear to belong to the previous request. Context should therefore be established and cleaned up at task boundaries.
Does MDC automatically propagate to another thread?
You should not assume that it does. MDC is commonly backed by thread-local state, so a separate worker thread has its own context. Asynchronous code therefore needs an explicit propagation mechanism when the diagnostic context should follow the task. The mechanism depends on the executor, framework, and observability stack being used.
How would you safely manage MDC around request processing?
At the request boundary, I would establish the fields needed for diagnostics, such as a request ID, before application processing begins. I would ensure cleanup occurs even when processing throws an exception, typically with a `finally` block or an equivalent framework lifecycle mechanism. For asynchronous work, I would explicitly capture and propagate only the context required by the task. I would also avoid treating MDC as a source of business-critical state.
What is the relationship between MDC and distributed tracing?
MDC is a local logging-context facility, while distributed tracing provides a context model intended to correlate operations across service and process boundaries. Trace and span identifiers can be placed into MDC so local logs contain useful tracing identifiers. However, putting an identifier in MDC does not itself propagate tracing context to another service. A tracing or context-propagation mechanism is responsible for crossing those boundaries.