一、背景
开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用<代码>麋鹿> 代码来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿行其中导致很难筛选出指定请求的全部相关日志,以及下游线程/服务对应的日志。
二,解决思路
-
<李>每个请求都使用一个<代码>唯一标识> 代码来追踪全部的链路显示在日志中,并且不修改原有的打印方式(代码无* * *)李>
<李>使用Logback的MDC <代码> 代码>机制日志模板中加入<代码> traceId> 代码标识,取值方式为X <代码> % {traceId} 代码>
MDC(诊断上下文映射,映射调试上下文)是log4j和logback提供的一种方便在多线程条件下记录日志的功能.MDC可以看成是一个与当前线程绑定的地图,可以往其中添加键值对.MDC中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的MDC的内容。当需要记录日志时,只需要从争取民主变革运动中获取所需的信息即可.MDC的内容则由程序在适当的时候保存进去。对于一个网络应用来说,通常是在请求被处理的最开始保存这些数据。
李引用> >
三,方案实现
由于<代码> MDC> 代码内部使用的是<代码> ThreadLocal> 代码所以只有本线程才有效,子线程和下游的服务MDC <代码> 代码>里的值会丢失,所以方案主要的难点是解决值的传递问题。
3.1。修改日志模板
logback配置文件模板格式添加标识X <代码> % {traceId} 代码>
公共类TraceInterceptor实现HandlerInterceptor {
@Override
公共布尔preHandle (HttpServletRequest请求,HttpServletResponse响应对象处理程序){
字符串traceId=request.getHeader (CommonConstant.TRACE_ID_HEADER);
如果(StrUtil.isNotEmpty (traceId)) {
MDC.put (CommonConstant。LOG_TRACE_ID traceId);
}
返回true;
}
}代码>
<强>注册拦截器强>
<代码类="语言java ">公共类DefaultWebMvcConfig延伸WebMvcConfigurationSupport { @Override 保护无效addInterceptors (InterceptorRegistry注册表){//日志链路追踪拦截器 注册表。addInterceptor(新TraceInterceptor ()) .addPathPatterns (“/* *”); super.addInterceptors(注册表); } }代码>
3.4。下游服务增加假装拦截器
继续把当前服务的<代码> traceId> 代码值传递给下游服务
<代码类="语言java ">公共类FeignInterceptorConfig { @ bean 公共RequestInterceptor RequestInterceptor () { RequestInterceptor RequestInterceptor=模板→{//传递日志traceId 字符串traceId=MDC.get (CommonConstant.LOG_TRACE_ID); 如果(StrUtil.isNotEmpty (traceId)) { template.header (CommonConstant。TRACE_ID_HEADER traceId); } }; 返回requestInterceptor; } }代码>
3.5。扩展线程池
主要针对业务会使用线程池(异步,并行处理),并且<代码>春季> 代码自己也有<代码> @Async 代码>注解来使用线程池,所以需要扩展