Springboot使用MDC实现日志追踪
Springboot使用MDC实现日志追踪
·
MDC介绍
1、简介:
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。
2、一般时候,我们为了日志追踪,会在日志中拼接参数,代码具有侵入性,也容易忘记。MDC结合log4j,程序会在适当时候当我们拼接好参数。
MDC使用
1.拦截器中使用MDC put 键值对。使得所有请求,从一开始就标记上特定标识。
2.如果是微服务之间的调用,则需要上层服务在header中添加标识同请求一起传输过来。下层服务直接使用上层服务的标识,就可以将日志串联起来。
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果有上层调用就用上层的ID
String traceId = request.getHeader(Constants.TRACE_ID);
if (traceId == null) {
traceId = UUID.randomUUID().toString();
}
MDC.put(Constants.TRACE_ID, traceId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
MDC.clear();
}
}
重点:修改logback日志格式
<property name="pattern">[TRACEID:%X{traceId}] %d{HH:mm:ss.SSS} %-5level %class{-1}.%M()/%L - %msg%xEx%n</property>
也可以只在配置文件中添加配置
logging.pattern.console="%d %.-1p traceId:[%X{traceId}] --- [%t] %logger{16} : %replace(%m%wEx){'[\r\n]+', '\t'}%nopex%n"
MDC的坑
1.主线程中,如果使用了线程池,会导致线程池中丢失MDC信息;
解决办法:需要我们自己重写线程池,在调用线程跳动run之前,获取到主线程的MDC信息,重新put到子线程中的。
public class MDCLogThreadPoolExecutor extends ThreadPoolExecutor {
public MDCLogThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable command) {
super.execute(MDCLogThreadPoolExecutor.executeRunable(command, MDC.getCopyOfContextMap()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(MDCLogThreadPoolExecutor.executeRunable(task, MDC.getCopyOfContextMap()));
}
@Override
public <T> Future<T> submit(Callable<T> callable) {
return super.submit(MDCLogThreadPoolExecutor.submitCallable(callable,MDC.getCopyOfContextMap()));
}
public static Runnable executeRunable(Runnable runnable ,Map<String,String> mdcContext){
return new Runnable() {
@Override
public void run() {
if (mdcContext == null) {
MDC.clear();
} else {
MDC.setContextMap(mdcContext);
}
try {
runnable.run();
} finally {
MDC.clear();
}
}
};
}
private static <T> Callable<T> submitCallable( Callable<T> callable, Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
try {
return callable.call();
} finally {
MDC.clear();
}
};
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)