Understanding MDC (Mapped Diagnostic Context) in Microservices Architecture

Arvind Kumar
3 min readJan 30, 2025

--

In a microservices-based architecture, logging plays a crucial role in monitoring, debugging, and troubleshooting applications. However, with multiple services handling various parts of a request, tracking a specific request’s flow becomes challenging. This is where MDC (Mapped Diagnostic Context) comes into play, enabling structured and context-aware logging.

This article explores what MDC is, how it helps in microservices, common issues, alternatives, and best practices for its usage.

What is MDC?

Mapped Diagnostic Context (MDC) is a mechanism provided by SLF4J and Logback that allows developers to store contextual information in logs. It works by associating contextual data (like user ID, request ID, or session ID) with the current thread, making it accessible to all log statements within that thread.

MDC is commonly used in multi-threaded applications and distributed systems where tracking logs based on a request or user action is necessary.

Example: Setting and retrieving values in MDC:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class MdcExample {
private static final Logger logger = LoggerFactory.getLogger(MdcExample.class);
public static void main(String[] args) {
MDC.put("userId", "12345");
logger.info("User action performed.");
MDC.clear();
}
}

Log Output:

INFO [main] [userId=12345] - User action performed.

How MDC Helps in Microservices

1. Request Tracing Across Multiple Services

In microservices, an incoming request may travel through multiple services. MDC allows attaching a unique trace ID or correlation ID to each request, helping developers track its journey across logs.

Example: Setting Trace ID in a Spring Boot Filter

@Component
public class MdcFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
try {
filterChain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}

2. Debugging and Log Enrichment

MDC allows logs to be enriched with additional details like user ID, session ID, and API name, making debugging easier.

Example log format with MDC:

INFO [http-nio-8080-exec-2] [traceId=abc123 userId=5678] - User logged in.

3. Security and Compliance Logging

In regulated environments, logging user activity is crucial. MDC ensures logs capture necessary identifiers for audit purposes.

Common Issues with MDC

1. MDC Context Leakage in Thread Pools

Since thread pools reuse threads, MDC values set in one request may leak into another request if not properly cleared.

Solution: Always call MDC.clear() in finally blocks after processing each request.

2. MDC Not Propagating to Async Threads

MDC data does not automatically transfer to new threads created in @Async or CompletableFuture.

Solution: Manually propagate MDC using MDC.getCopyOfContextMap().

CompletableFuture.runAsync(() -> {
MDC.setContextMap(parentMdcContext);
logger.info("Processing async request");
MDC.clear();
});

3. MDC and Reactive Programming (Spring WebFlux)

Since WebFlux is non-blocking, MDC is not available across reactive contexts.

Solution: Use Reactor Context instead:

Mono<Void> logWithContext(Mono<Void> mono) {
return mono.contextWrite(ctx -> ctx.put("userId", "12345"))
.doOnSuccess(unused -> log.info("User action in reactive flow."));
}

Alternatives to MDC

1. Logging Framework Support (Logback/Log4j2 Context Map)

Some logging frameworks provide built-in context management that handles MDC-like behavior but avoids thread-local issues.

2. Distributed Tracing (Zipkin, Jaeger, OpenTelemetry)

For better observability, use OpenTelemetry or Zipkin to propagate trace IDs across microservices instead of relying solely on MDC.

3. Explicit Logging Parameters

Instead of using MDC, pass contextual information explicitly:

logger.info("User {} performed action on order {}", userId, orderId);

Final Thoughts on MDC Usage

MDC is powerful for structured logging and tracing in monolithic and early-stage microservices applications.

However, in modern cloud-native applications, prefer distributed tracing (OpenTelemetry, Jaeger, Zipkin) for better cross-service correlation.

Always ensure MDC values are cleared properly to prevent leakage in thread pools.

By implementing MDC correctly, you can enhance observability, debugging, and security in your microservices-based system.

🚀 Did this article help you? Share your thoughts and experiences with MDC in the comments below!

--

--

Arvind Kumar
Arvind Kumar

Written by Arvind Kumar

Staff Engineer @Chegg || Passionate about technology || https://youtube.com/@codefarm0

Responses (1)