日志链路追踪

项目背景:项目中用到了异步操作,导致一些操作无法直接通过Tomcat自带的线程号去追踪整个链路。实现跨线程的traceID打印可以解决这个问题。其实这玩意就相当于每个请求的requestId

定义拦截器,设置、清除traceID


@Slf4j
@Component
public class LogInterceptor implements HandlerInterceptor {

    public LogInterceptor() {
        log.info("LogInterceptor is loaded");
    }

    public String TRACE_ID = "traceId";
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          //如果有上层调用就用上层的ID
          String traceId = request.getHeader(TRACE_ID);
          if (traceId == null) {
              traceId = UUID.fastUUID().toString();//TraceIdUtil.getTraceId()
          }
  
          MDC.put(TRACE_ID, traceId);
          return true;
      }
  
      @Override
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
              throws Exception {
      }
  
      @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
              throws Exception {
          //调用结束后删除
          MDC.remove(TRACE_ID);
      }
}

拦截器设置

@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("addInterceptors");
        // 添加拦截器,并指定拦截路径
        registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
    }
}

对原有的线程池配置进行修改,加上自定义的线程池装饰器。这么做是为了解决异步任务时,子线程的traceId 为空的问题??



/**
 * 线程池
 */

@Configuration
@EnableAsync
public class SyncConfiguration {

    public SyncConfiguration() {
        System.out.println("线程池is loading");
    }

    @Bean(name = "scorePoolTaskExecutor")
    public ThreadPoolTaskExecutor getScorePoolTaskExecutor() {

        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数
        taskExecutor.setCorePoolSize(1);
        //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        taskExecutor.setMaxPoolSize(1);
        //缓存队列
        taskExecutor.setQueueCapacity(1);
        //允许的空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
        taskExecutor.setKeepAliveSeconds(1);
        //异步方法内部线程名称
        taskExecutor.setThreadNamePrefix("Async-Service-");

        taskExecutor.setTaskDecorator(new ContextCopyingDecorator());//配置装饰器
        
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();

        return taskExecutor;
    }

}

自定义的线程池装饰器:解决子线程traceId丢失的问题


public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        try {
			RequestAttributes context = RequestContextHolder.currentRequestAttributes();  //1
			Map<String,String> previous = MDC.getCopyOfContextMap(); 					  //2
			return () -> {
			    try {
			    	RequestContextHolder.setRequestAttributes(context);	 //1
			    	MDC.setContextMap(previous);					     //2				
			        runnable.run();
			    } finally {
			        RequestContextHolder.resetRequestAttributes();		// 1
			        MDC.clear(); 										// 2
			    }
			};
		} catch (IllegalStateException e) {
			return runnable;
		}
    }
}

给日志的配置文件加上traceID

[TRACEID:%X{traceId}]
注意:如果在MDC中放的Key叫AAA,那么这个变量名就应该叫{AAA}

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>[TRACEID:%X{traceId}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值