Springboot-MDC+logback实现日志追踪
一、MDC介绍
MDC(Mapped Diagnostic Contexts)映射诊断上下文,该特征是logback提供的一种方便在多线程条件下的记录日志的功能。
某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池中复用已有的线程。在一个用户的会话存续期间,可能有多个线程处理过该用户的请求。这使得比较难以区分不同用户所对应的日志。当需要追踪某个用户在系统中的相关日志记录时,就会变得很麻烦。
MDC正是用于解决上述问题的,MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
二、MDC实现日志跟踪
一个系统提供服务,提供给其他系统来调用,当其他系统调用的时候,请求头带上唯一的请求标识(requestId),把这个requestId输出到日志中,这样两个系统直接就会形成一个执行链,用requestId串联起来,当出现错误时,可以在调用方查询对应的请求日志,也可以在服务方查询请求日志,定位问题很方便,输出日志的地方很多。可以使用MDC配合logback中的pattern。
三、HandlerInterceptor拦截器
相关介绍:
SpringBoot使用过滤器、拦截器、切面(AOP),及其之间的区别和执行顺序 - 劈天造陆 - 博客园
HandlerInterceptor_dekulugu的博客-CSDN博客_handlerinterceptor
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TokenInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//添加MDC日志
String appkey = request.getHeader("appkey");
MDC.put("LOG_TRACK_ID", appkey);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//移除MDC日志
MDC.remove("LOG_TRACK_ID");
}
}
四、logback文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="logLevel" value="INFO"></property>
<property name="logPath" value="./logs"></property>
<property name="maxHistory" value="31"/>
<property name="maxFileSize" value="100MB"/>
<property name="totalSizeCap" value="200GB"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] [%X{logTrackId}] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="FILE_INFO" class="ch.qos.logback.classic.sift.SiftingAppender">
<!--discriminator鉴别器,根据logTrackId这个key对应的value鉴别日志事件,然后委托给具体appender写日志-->
<discriminator>
<key>logTrackId</key>
<defaultValue>default</defaultValue>
</discriminator>
<sift>
<!--具体的写日志appender,每一个logTrackId创建一个文件-->
<appender name="FILE-${logTrackId}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--直接指定文件路径-->
<append>true</append>
<encoder charset="UTF-8">
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] [%X{logTrackId}] %-5level %logger{35} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--定义文件滚动时的文件名的格式-->
<fileNamePattern>${logPath}/%d{yyyy-MM-dd}/${logTrackId}-%i.log</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
<maxFileSize>${maxFileSize}</maxFileSize>
<totalSizeCap>${totalSizeCap}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
</sift>
</appender>
<!-- 异步输出 -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE_INFO"/>
</appender>
<root level="${logLevel}">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE_INFO"/>
</root>
</configuration>
相关学习链接:
logback新认识(二):logback之MDC日志跟踪、日志自定义效果_IT-CRUD-CSDN博客