Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assign a unique id to every request in a spring-based web application

Tags:

java

spring

I created a spring-based Java web application,

for each request, an instance of 'controller class' will be created to handle the request.

in the business logic, I want to do some logging with a UNIQUE ID automatically assigned to each request so that I can track what the program exactly did.

the log may be like this(2 requests at same time):

[INFO] request #XXX: begin.
[INFO] request #XXX: did step 1
[INFO] request #YYY: begin.
[INFO] request #XXX: did step 2
[INFO] request #YYY: did step 1
[INFO] request #XXX: end.
[INFO] request #YYY: end.

from the log, I can realize: req #XXX: begin-step1-step2-end req #YYY: begin-step1-end

I hope the logging can be called easily everywhere in the code, so I don't want to add a parameter of "requestId" to every java function,

It's perfect if the logging tool can be called in a static way:

LOG.doLog("did step 1");

any idea of how can I do this? thank you :)

like image 526
user2257598 Avatar asked Sep 16 '13 08:09

user2257598


People also ask

What is correlation ID in spring boot?

A correlation ID is a unique ID that gets carried across all the microservices that are executed when carrying out a customer request. A correlation ID allows us to trace the chain of events that occur as a call goes through a series of microservice calls. to see more go to 8 Service routing with Spring Cloud Gateway.

How do you implement a correlation ID in Java?

How to add correlation ID? You'd add it as a header to the http requests which is normally called X-Correlation-ID. In most cases your point of entry would be some webserver like Apache Httpd or Nginx - so those would generate and populate the header.

How do I create a spring boot filter?

There are three ways to add your filter, Annotate your filter with one of the Spring stereotypes such as @Component. Register a @Bean with Filter type in Spring @Configuration. Register a @Bean with FilterRegistrationBean type in Spring @Configuration.


3 Answers

You can also try using MDC class of Log4j. The MDC is managed on a per thread basis. If you are using a ServletRequestListner then you can set the unique Id in the requestInitialized.

import org.apache.log4j.MDC;
import java.util.UUID;

public class TestRequestListener implements ServletRequestListener {    
protected static final Logger LOGGER = LoggerFactory.getLogger(TestRequestListener.class);

 public void requestInitialized(ServletRequestEvent arg0) {
    LOGGER.debug("++++++++++++ REQUEST INITIALIZED +++++++++++++++++");

    MDC.put("RequestId", UUID.randomUUID());

 }

 public void requestDestroyed(ServletRequestEvent arg0) {
    LOGGER.debug("-------------REQUEST DESTROYED ------------");
    MDC.clear(); 
 }
}

Now anywhere in the code if you do a log either debug, warn or error. Whatever you had put in the MDC will be printed out. You need to configure you log4j.properties. Notice the %X{RequestId}. This referes to the key name which is inserted in the requestInitialized() above.

log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSS} %p %C %X{RequestId} - %m%n

I also found this link to be helpful -> What is the difference between Log4j's NDC and MDC facilities?

like image 167
Sharadr Avatar answered Oct 21 '22 17:10

Sharadr


You have three different problems to solve:

  1. Generate an unique id for each request
  2. Store the id and access it everywhere in the code
  3. Log the id automatically

I would suggest this approaches

  1. Use a Servlet filter or a ServletRequestListener (as suggested by M. Deinum) or a Spring Handler Interceptor to intercept the request in a general way, there you can create a unique id, maybe with an UUID

  2. You can save the id as an attribute of the request, in this case the id would propagate just in the controller layer, not in the services. So you can solve the problem using a ThreadLocal variable or asking Spring to do the magic with the RequestContextHolder: the RequestContextHolder will allow you to access the request of that specific thread, and the request attributes as well, in the service layer. The RequestContextHolder use ThreadLocal variable to store the request. You can access the request in this way:

    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    // Extract the request
    HttpServletRequest request = attr.getRequest();
    
  3. There is an interesting article (2018 alternative), if you are using log4j, on the customization of the pattern layout of the logger. However, youncan simply create a proxy of your logging system interface and append manually the id to every logged string.

like image 26
MPavesi Avatar answered Oct 21 '22 16:10

MPavesi


You can also use the "Fish Tagging" in Log4j 2. It is the same idea like MDC and NDC (thread-basis) as described in https://logging.apache.org/log4j/2.x/manual/thread-context.html

Here you can use either the Thread Context Stack or the Thread Context Map. An example for the Map looks like this:

//put a unique id to the map
ThreadContext.put("id", UUID.randomUUID().toString()

//clear map
ThreadContext.clearMap();

And for the pattern in log4j2.xml you can also use the %X{KEY} tag.

To put a new id to the map (for example for every incoming request) you can do that in an ServletRequestListener implementation how Sharadr described it.

like image 5
K.E. Avatar answered Oct 21 '22 18:10

K.E.