Logo
Published on

Add a unique ID in logs in Spring Boot for each Request

Authors
  • Name
    Twitter

Introduction

In the world of web applications, effective request monitoring and traceability are paramount for maintaining system integrity and diagnosing issues. Spring Boot, a popular framework for building Java applications, offers robust logging capabilities that can be further enriched by incorporating unique identifiers for each incoming request. This concise yet powerful practice enables developers to track and analyze individual request flows, simplifying debugging and enhancing overall system reliability. In this article, we explore the significance of adding unique IDs to logs in Spring Boot, offering insights into its implementation and the numerous benefits it brings to application development and maintenance.

Problem

Today generally we have multiple servers for each service. Now there are two ways to check logs first one is to get inside the individual server instance and check logs. There is another way in which logs are collected from all the servers and you can see them in one place. People generally use a second approach for searching and analyzing logs. Log Explorer in GCP is one such thing. Now with so many logs from multiple servers of a single service, it becomes difficult to trace the logs.

Moreover, even if we have a single server of a service then also due to concurrent requests/threads many logs are printed. A major issue faced is of tracing as logs are printed in an interleaved fashion. It becomes very difficult to follow the printed logs.

Solution

To fix the above issue we need some kind of identifier associated with each log entry. We can generate a unique ID and then assign that ID to all the logs for a particular request. So all the logs generated for a particular request will have a unique ID. With the help of this ID, we can easily trace through the logs. We can search for this ID in logs and get all the logs for a specific request. To implement this in Spring Boot we can use AOP.

Aspect-Oriented Programming (AOP) is a programming paradigm that allows developers to modularize cross-cutting concerns, such as logging, security, and transaction management, separately from the core business logic, promoting code reusability and maintainability. It achieves this by introducing “aspects” that can be applied across different parts of the codebase without altering the original code.

First of all, you have to create a request filter like below:

/**  
 * A filter that adds a key to the Mapped Diagnostic Context (MDC) to each request so you can print a unique id in the log messages of each request  
 **/  
@EqualsAndHashCode(callSuper = false)  
@Component  
@Slf4j  
public class Slf4jMDCFilter extends OncePerRequestFilter {  
  
    @Override  
    protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {  
        try {  
            MDC.put(MDC_UUID_TOKEN_KEY, UUID.randomUUID().toString());  
            chain.doFilter(request, response);  
        } catch (Exception ex) {  
            log.error(ERROR_FORMAT, "Exception occurred in filter while setting UUID for logs", ex);  
        } finally {  
            MDC.remove(MDC_UUID_TOKEN_KEY);  
        }  
    }  
  
    @Override  
    protected boolean isAsyncDispatch(final HttpServletRequest request) {  
        return false;  
    }  
  
    @Override  
    protected boolean shouldNotFilterErrorDispatch() {  
        return false;  
    }  
}

The above code runs once for every API request and adds the same unique ID to all the logs generated for a particular request. Next, you have to register the above AOP filter as a bean as shown below:

@Configuration  
@RequiredArgsConstructor  
public class BeanConfig {  
    private final Slf4jMDCFilter slf4jMDCFilter;  
  
    @Bean  
    public FilterRegistrationBean<Slf4jMDCFilter> servletRegistrationBean() {  
        final FilterRegistrationBean<Slf4jMDCFilter> filterRegistrationBean = new FilterRegistrationBean<>();  
        filterRegistrationBean.setFilter(slf4jMDCFilter);  
        filterRegistrationBean.setOrder(2);  
        return filterRegistrationBean;  
    }  
}

After that you need to add an appender in your log back file as shown below:

 <appender name="Console"  
              class="ch.qos.logback.core.ConsoleAppender">  
        <layout class="ch.qos.logback.classic.PatternLayout">  
            <Pattern>  
                %black(%d{ISO8601}) %X{Slf4jMDCFilter.UUID} %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable  
            </Pattern>  
        </layout>  
    </appender>

Summary

Implementing a unique ID in logs for each request in Spring Boot allows for better traceability and debugging by correlating logs across different components of the application, especially in concurrent scenarios. This unique identifier helps in tracking the flow of requests, identifying potential issues, and improving overall system observability. Please comment if you have any queries, thanks.