跨线程(线程池)日志链路追踪
不跨线程的业务日志链路我们可以通过日志框架如Logback 使用MDC对象放入一个traceId,再在日志配置文件配置即可 但如跨线程的话traceid无法传递(MDC 底层使用的 TreadLocal)
对于线上业务存在大量的跨线程业务操作,实际线上日志输出海量且相当复杂,故研究跨线程的日志链路追踪,形成业务日志链路记录,方便高效的排查线上问题
在此有一种方案为重写线程池,将线程池重新封装,在线程执行将前一个线程的日志traceId传递给将要执行的线程日志MDC
对于直接new创建线程的情况不考略【实际应用中应该避免这种用法】
在此基础上我们可以通过复写线程池ThreadPoolExecutor 来实现traceid的传递
import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; public class ThreadMdcUtil { public static void setTraceIdIfAbsent() { if (MDC.get(MDCGenerator.REQUEST_ID_HEADER_NAME) == null) { MDC.put(MDCGenerator.REQUEST_ID_HEADER_NAME, UUID.randomUUID().toString()); } } 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(); } }; } }
jdk线程池
/** * @author lyh */ public class ThreadPoolExecutorMdcWrapper extends ThreadPoolExecutor { public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); } public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); } public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } @Override public void execute(Runnable task) { super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap())); } @Override public <T> Future<T> submit(Runnable task, T result) { return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()), result); } @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())); } }
spring ThreadPoolExecutor
public class ThreadPoolTaskExecutorMdcWrapper extends ThreadPoolTaskExecutor { @Override public void execute(Runnable task) { super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap())); } @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())); } }