Funsaized
Funsaized

Reputation: 2130

Spring Boot & Slf4j 2 Http request metadata logging interceptor?

I would like to configure my logging setup such that incoming requests are tagged with an id (i.e UUID) that is used to trace requests and processes throughout the application (i.e controller classes, service classes, etc) as well as other relevant information in the request (i.e hostname, ipaddress...).

Referencing the slf4j 2 documentation, I see mention of ThreadContext. For every request made to my spring-boot application, I would like to do something similar to this :

ThreadContext.put("id", UUID.randomUUID().toString());
ThreadContext.put("ipAddress", this.request.getRemoteAddr());
...

and call these keys in the PatternLayout for my debugger as follows: %X{key}. This would ideally produce something similar to the following:

[INFO] request #UUID1 #IP1: start.
[INFO] request #UUID1 #IP1: do something controller class 
[INFO] request #UUID2 #IP2: start.
[INFO] request #UUID1 #IP1: do something service class
[INFO] request #UUID2 #IP2: do something controller class 
[INFO] request #UUID2 #IP2: do something service class 
[INFO] request #UUID1 #IP1: end.
[INFO] request #UUID2 #IP2: end.

Although I'm unsure how to begin actually implementing this. Any insight would be appreciated!

Upvotes: 1

Views: 3414

Answers (3)

Funsaized
Funsaized

Reputation: 2130

Using a class implementing the Filter class, I was able to get desired behavior by using the following code as well as calling the keys in my Log4j 2 PatternLayout:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.TimeZone;
import java.util.UUID;

@Component
public class RequestResponseLoggingFilter implements Filter {
    private FilterConfig filterConfig;
    private static String TZ_NAME = "timezoneOffset";

    private static final Logger LOGGER = LogManager.getLogger(RequestBody.class);


@Override
public void init(FilterConfig filterConfig) throws ServletException {
    this.filterConfig = filterConfig;
}

/**
 * Sample filter that populates the MDC on every request.
 */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)servletRequest;
    HttpServletResponse response = (HttpServletResponse)servletResponse;

    String requestId = request.getHeader("requestId");

    if (requestId == null) {
        requestId = UUID.randomUUID().toString();
    }

    ThreadContext.put("requestId", requestId);
    ThreadContext.put("ipAddress", request.getRemoteAddr());
    HttpSession session = request.getSession(false);
    TimeZone timeZone = null;
    ThreadContext.put("hostname", servletRequest.getServerName());
    ThreadContext.put("locale", servletRequest.getLocale().getDisplayName());
    if (timeZone == null) {
        timeZone = TimeZone.getDefault();
    }
    ThreadContext.put("timezone", timeZone.getDisplayName());
    LOGGER.info(
            "Logging Request {} : {}", request.getMethod(),
            request.getRequestURI());
    filterChain.doFilter(servletRequest, servletResponse);
    LOGGER.info(
            "Logging Response {} : {}",
            response.getStatus(), response.getContentType());
    ThreadContext.clearMap();
}

@Override
public void destroy() {
}
}

Upvotes: 0

sanimalp
sanimalp

Reputation: 819

Spring Cloud Sleuth will do all this for you.. simply include it in your classpath, and add the attributes you care about to your logging output. It also automatically instruments controllers and clients to pass the header attributes on.

Upvotes: 1

Anil Bachola
Anil Bachola

Reputation: 289

You should be looking at MDC (mapped diagnostic context) with slf4j https://ivanursul.com/slf4j-mdc

Create a Servlet Filter, which sets the required params in the MDC, they will be available through out the request and they get logged automatically.

Upvotes: 3

Related Questions