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 :)
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 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.
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.
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?
You have three different problems to solve:
I would suggest this approaches
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
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();
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With