• SpringBoot+slf4j线程池全链路调用日志跟踪 二


    关注作者公众号【互联网专栏】获取本项目源码

    本项目源码已在多个项目中实践

    接着上一篇文章,项目中使用了线程池,那么子线程中日志就会丢失traceId,下面讲解如何实现子线程中的traceId日志跟踪。

    解决思路

    子线程在打印日志的过程中traceId将丢失,解决方式为重写线程池,将主线程的traceId继续传递到子线程中。当然,对于直接new创建线程的情况不考略【实际应用中应该避免这种用法】。

    继承ThreadPoolExecutor,重写执行任务的方法

    public final class OverrideThreadPoolExecutor extends ThreadPoolExecutor {
    
        @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()));
        }
    }
    

    封装ThreadMdcUtil工具类

    以封装Callable为例:

    • 判断当前线程对应MDC的Map是否存在,如果存在则设置子线程的ContextMap为当前线程的;
    • 如果不存在,则重新生成traceId;
    • 执行run方法
    public final class ThreadMdcUtil {
    
        public static void setTraceIdIfAbsent() {
            if (MDC.get(TraceConstant.MDC_TRACE) == null || MDC.get(TraceConstant.MDC_TRACE).length() == 0) {
                String tid = UUID.randomUUID().toString().replace("-", "");
                MDC.put(TraceConstant.MDC_TRACE, tid);
            }
        }
       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();
                }
            };
        }
    }
    

    测试子线程中traceId的传递,本项目中ExecutorService已经重写了线程池

    @RestController
    @RequestMapping("trace")
    @Slf4j
    @AllArgsConstructor
    public class TestTraceController {
    
        private final ExecutorService executorService;
    
        @GetMapping("traceLog")
        public String traceLog() {
            log.info("---接口调用了---");
            traceService();
            asyncTrace();
            return "success";
        }
    
        private void traceService(){
            log.error("## 执行traceService方法");
        }
    
        private void asyncTrace(){
            CompletableFuture.runAsync(()->{
                log.info("执行线程池中的方法asyncTrace,未重写了线程池");
            }, executorService);
        }
    
    }
    

    未重写ThreadPoolExecutor效果如下:

    重写ThreadPoolExecutor效果如下:

  • 相关阅读:
    机器码call和jmp地址的计算
    linux下系统对于sigsegv错误时的处理
    elf文件中的.plt .rel.dyn .rel.plt .got .got.plt的关系
    docker随谈
    nginx性能优化技巧
    ubuntu安装php常见错误集锦
    ubuntu php5.6源码安装
    关于mysqld_safe
    ubuntu mysql5.7源码安装
    linux下nginx模块开发入门
  • 原文地址:https://www.cnblogs.com/kevin-ying/p/14483180.html
Copyright © 2020-2023  润新知