本文最后更新于 1186 天前,其中的信息可能已经有所发展或是发生改变。
全局日志记录traceId
单体应用为了更方便的排查问题,使用过滤器+日志框架的MDC功能,实现每个请求产生的日志,都包含一个UUID。
定义过滤器
@Component
@Order(1)
public class TraceFilter extends OncePerRequestFilter {
public static final String TRACE_ID = "traceId";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
MDC.put(TRACE_ID, getTraceId());
filterChain.doFilter(request, response);
}
public static String getTraceId() {
return IdUtil.simpleUUID();
}
}
- 在以线程为维度的上下文中,放入traceId
logback定义日志文件的输出格式
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} | traceId=%X{traceId} | [%thread] %-5level %logger{50} - %msg%n" />
全局响应值里增加traceId
public ResponseDTO(Integer code,String msg,T data) {
this.code = code;
this.msg = msg;
this.data=data;
this.timestamp = new Date();
this.traceId = MDC.get(TraceFilter.TRACE_ID);
}
问题
如果项目里使用了异步、并发处理多任务,就会产生子线程。子线程需要继承父线程的MDC上下文才能使用traceId。
手动创建线程
这种不考虑,本来就是不推荐的写法。
使用线程池
继承ThreadPoolTaskExecutor
,重写提交任务的方法
public class ThreadPoolExecutorMdcWrapper extends ThreadPoolTaskExecutor {
@Override
public void execute(Runnable task) {
super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public void execute(Runnable task, long startTimeout) {
super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()), startTimeout);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
}
执行提交的任务前后都处理下,继承父线程的MDC上下文。
public class ThreadMdcUtil {
public static void setTraceIdIfAbsent() {
if (MDC.get(TraceFilter.TRACE_ID) == null) {
MDC.put(TraceFilter.TRACE_ID, TraceFilter.getTraceId());
}
}
public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try {
return callable.call();
} finally {
MDC.clear();
}
};
}
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try {
runnable.run();
} finally {
MDC.clear();
}
};
}
}