一、背景
在排查问题时,如果只根据关键字搜索,可能不精准,比如根据userId搜索,但是这个userId访问的记录也很多,很难定位出问题的是哪一次的;比如根据其他关键字搜索如orderId,可能很多用户都访问了这个模块,很难定位是哪个用户出问题了;而把多个条件整合在一块,日志不规范比如不是所有日志都打印了这两个参数,则排查方向很不明确。
这时需要追踪整个调用链路,除了一些三方工具,追踪的方法也可以自定义实现:
(1)后端添加一个过滤器处理traceId,对每个请求创建一个唯一的id(这里命名为traceId),
(第一次)判断为空则生成一个再存起来。至于存在哪里?如果存在request对象中,和业务
耦合太深了,每个entry point入口(controler接口)都要加上request, 且要传给业务服务。
所以有了MDC,MDC是一个在 Java 项目中用于日志跟踪的工具,它允许你在多线程环境下关联和传递特定的上下文信息。
(2)每次打印日志也打印这个traceId,如果硬编码,代码风格会很难看
仍然可以通过MDC来解决这个问题。
(3)在filter中通过HttpServletResponse塞到header中返回给前端,这样就可以在控制台拿到这个
traceId,去追踪后端日志了。
二、MDC
1、简介
MDC(Mapped Diagnostic Context)是一个线程本地的、可维护的、可传递的上下文环境。在 Java 中,MDC 主要用于在应用程序的不同组件之间传递日志上下文信息,例如用户会话 ID、请求 ID、用户身份信息等。MDC 让你可以将这些信息关联到特定的日志事件中,以便后续的日志处理器(如日志输出器)能够在日志中显示或处理这些信息。
这样,通过 MDC,可以在日志中记录和传递特定的上下文信息,从而更好地理解和分析应用程序的日志。
2、作用
(1)跟踪日志上下文信息: MDC 允许你在日志中添加额外的上下文信息,帮助理解日志事件发生的背景和环境。
(2)诊断和调试: 在多线程环境中,使用 MDC 可以将特定的上下文信息关联到日志中,有助于排查问题和调试程序。
(3)日志过滤和路由: MDC 中的上下文信息可以被日志处理器用来过滤、路由或分类日志事件,例如基于用户会话 ID 将日志事件路由到特定的日志文件或系统。
3、原理
MDC 的实现原理通常基于线程本地变量(ThreadLocal)。详情参考【多线程】ThreadLocal 详解每个线程都有自己的 MDC,线程在处理请求时可以将上下文信息设置到 MDC 中,这些信息会和该线程相关联。当日志事件发生时,日志框架会从 MDC 中获取相应的上下文信息,并将其包含在日志中。
三、使用方法
在 Java 中,常见的日志框架(如 Logback、Log4j2)都支持 MDC。
1、配置日志框架
2、设置上下文信息
在代码中,当需要记录日志时,可以通过代码将相关的上下文信息设置到 MDC 中。例如,在请求开始时,你可以设置用户会话 ID:
import org.slf4j.MDC;// 设置用户会话 ID 到 MDC
MDC.put("sessionId", sessionId);
3、记录日志
当需要记录日志时,日志框架会自动将 MDC 中的上下文信息包含到日志中。你可以在日志消息中使用占位符来引用 MDC 中的上下文信息,具体使用方式取决于日志框架和日志输出格式。
只要代码中有 MDC.put操作,日志配置文件都可以直接使用这个key。
4、清除上下文信息
当请求处理结束后,记得清除 MDC 中的上下文信息,以免影响后续请求的日志记录:
// 清除 MDC 中的上下文信息
MDC.clear();