In a world of complex and distributed application, logging plays a very crucial role in auditing and debugging. I have been working on spring boot rest applications for a good amount of time now. And with multiple instances running and increased traffic, I quickly understood that I need a better way to track the logs of specific API requests.
The solution was pretty easy and simple with MDC (Mapped Diagnostic Context). This is what MDC is for.
The idea here is to stamp each request with a unique id using SLF4J’s MDC. And then passing that same unique id in the response headers. Doing this will print the generated UUID in all your logs for that particular request.
Here’s how to implement this in your spring boot application:
Implement a Servlet Filter in Spring Boot
Implement a servlet filter and add a unique string in the MDC. Along with that, set the same unique ID in your response header. Here is how the implemented class will look like:
package com.oddblogger.springbootmdc.interceptor; import com.oddblogger.springbootmdc.constant.Constants; import java.io.IOException; import java.util.UUID; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.stereotype.Component; import org.springframework.web.util.ContentCachingResponseWrapper; @Component @Slf4j public class RequestFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { UUID uniqueId = UUID.randomUUID(); MDC.put("requestId", uniqueId.toString()); log.info("Request IP address is {}", servletRequest.getRemoteAddr()); log.info("Request content type is {}", servletRequest.getContentType()); HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper( httpServletResponse ); filterChain.doFilter(servletRequest, responseWrapper); responseWrapper.setHeader("requestId", uniqueId.toString()); responseWrapper.copyBodyToResponse(); log.info("Response header is set with uuid {}", responseWrapper.getHeader("requestId")); } }
Add logback.xml
To print this uuid string in your logs, you will have to update your logback.xml. Just add the variable your have set, requestId
in this case, to your message pattern. Here’s my logback file:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="Console" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern> %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %X{requestId} %file -> %M\(%line\) - %msg%n </pattern> </layout> </appender> <logger name="com.oddblogger" level="debug" additivity="false"> <appender-ref ref="Console"/> </logger> <root level="error"> <appender-ref ref="Console"/> </root> </configuration>
Result?
Now each of your request will have a unique request ID embedded in the response headers.
All you need to trace the logs of that request is to search that request ID in your logs.

MDC with Threads
When it comes to Threads in Java, things are rarely simple and straightforward. If you are using threads in your application, the request ID will not be printed in the logs for those threads. Because in threads, the MDC is not passed automatically. You will have to do that before starting your thread. For that you can implement a runnable class for copying the MDC before starting the thread. Here’s an example:
private static class YourRunnableClass implements Runnable { private final Runnable runnable; private final Map<String, String> context; public YourRunnableClass(Runnable runnable) { this.runnable = runnable; context = MDC.getCopyOfContextMap(); } @Override public void run() { try { if (!ObjectUtils.isEmpty(this.context)) { MDC.setContextMap(this.context); } runnable.run(); } finally { MDC.clear(); } } }
Note: If you don’t want anything else, you can just pass the requestId
in your new thread MDC.
Here is the link to the sample code: Sample App