为基于弹簧的Web应用程序中的每个请求分配一个唯一的ID

2022-09-01 21:39:45

我创建了一个基于弹簧的Java Web应用程序,

对于每个请求,将创建一个“控制器类”实例来处理该请求。

在业务逻辑中,我想使用自动分配给每个请求的唯一 ID 进行一些日志记录,以便我可以跟踪程序的确切操作。

日志可能是这样的(同时2个请求):

[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.

从日志中,我可以意识到:req #XXX:begin-step1-step2-end req #YYY:begin-step1-end

我希望可以在代码的任何地方轻松调用日志记录,因此我不想在每个java函数中添加“requestId”参数,

如果能够以静态方式调用日志记录工具,那就太完美了:

LOG.doLog("did step 1");

任何想法,我该怎么做?谢谢:)


答案 1

您也可以尝试使用 Log4j 的 MDC 类。MDC 基于每个线程进行管理。如果您使用的是 ServletRequestListner,则可以在 requestInitialized 中设置唯一 Id。

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(); 
 }
}

现在,如果您执行日志,则代码中的任何位置都可以进行调试,警告或错误。您放入MDC的任何内容都将被打印出来。您需要配置 log4j.properties。请注意 %X{RequestId}。这是指在上面的请求初始化()中插入的键名。

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

我还发现此链接很有帮助->Log4j的NDC和MDC设施之间有什么区别?


答案 2

您需要解决三个不同的问题:

  1. 为每个请求生成一个唯一的 ID
  2. 存储 ID 并在代码中的任何位置访问它
  3. 自动记录 ID

我会建议这种方法

  1. 使用 Servlet 过滤器或 ServletRequestListener(如 M. Deinum 建议)或 Spring Handler Interceptor 以一般方式拦截请求,在那里您可以创建一个唯一的 ID,可能带有 UUID

  2. 您可以将 id 另存为请求的属性,在这种情况下,id 将仅在控制器层中传播,而不是在服务中传播。因此,您可以使用 ThreadLocal 变量或要求 Spring 对 RequestContextHolder 执行魔术来解决问题:RequestContextHolder 将允许您在服务层中访问该特定线程的请求以及请求属性。RequestContextHolder 使用 ThreadLocal 变量来存储请求。您可以通过以下方式访问请求:

    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    // Extract the request
    HttpServletRequest request = attr.getRequest();
    
  3. 有一篇有趣的文章2018年替代),如果您使用的是log4j,则关于记录器的模式布局的自定义。但是,您只需创建日志记录系统接口的代理,然后手动将id附加到每个记录的字符串中即可。


推荐