目录
一.分布式服务查看日志的痛点
分布式服务最显著的特征就是服务可能分散在不同的机器上,尤其是做了集群后,同样的服务也可能部署在不同的机器上,散落在各处的日志文件对于查询分析问题会非常麻烦。并且无法对于同一次请求整个调用链的日志串联起来。
二.解决方案
1.可以使用ELK等工具将日志集中输出,但对于已经上线的代码势必会修改现有代码的打印逻辑
2.可以使用类似Skywalking框架实现,但是第三方框架又太重
3.自定义实现调用链路串联,本文主要介绍这种方式。文末送上基于SpringBoot自动装配实现的链路追踪jar包
三.自定义链路追踪串联日志
1.通过Filter过滤器读取和设置traceId,并把traceId设置到response中,前端也可以查看
2.对OpenFeign扩展,通过RequestInterceptor接口扩展请求头,通过请求头传递traceId
3.将全局的traceId放入MDC中,MDC为slf4j-api包中提供的日志上下文工具全程Mapped Diagnostic Context,详细信息(Chapter 8: Mapped Diagnostic Context),本质是ThreadLocal。放入MDC中的key可以在日志中输出
public class TraceIdFilter implements Filter { public final static String TRACE_ID = "traceId"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String traceId = httpRequest.getHeader(TRACE_ID); if (!StringUtils.hasLength(traceId)) { traceId = UUID.randomUUID().toString(); } MDC.put(TRACE_ID, traceId); HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setHeader(TRACE_ID, traceId); chain.doFilter(request, response); } }
public class FeignRequestExtend implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { String traceId = MDC.get(TraceIdFilter.TRACE_ID); requestTemplate.header(TraceIdFilter.TRACE_ID, traceId); } }
4.由于MDC本质是ThreadLocal,所以使用后要清理现场,避免线程复用出现脏数据
public class TraceIdInterceptor implements HandlerInterceptor { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { MDC.clear(); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { MDC.clear(); } }
5.使用SpringBoot自动装配功能实现集成,编写自动配置类
@Configuration public class FeignExtendAutoConfiguration { @Bean public FeignRequestExtend createFeignRequestExtend(){ return new FeignRequestExtend(); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public TraceIdFilter createTraceIdFilter(){ return new TraceIdFilter(); } @Bean public TraceIdInterceptor createTraceIdInterceptor(){ return new TraceIdInterceptor(); } }
6.META-INF文件中编写spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.glzt.feignextend.configuration.FeignExtendAutoConfiguration
7.日志配置文件修改
四.实现效果
服务A:
服务B:
五.下一步行动
日志打印输出带有了traceId标记就可以根据时间+traceId串联业务日志了,但是日志还是散落在不同的服务器上,可以通过文件读取的方式把日志集中输出到一个文件中,甚至每个业务一个文件。如果把traceId返回到前端,那么查看日志完全可以通过界面输入traceId来查看整个业务调用日志。
六.JAR包下载链接
基于SpringBoot自动装配实现的对于OpenFeign扩展请求传递traceId,分布式服务日志查询串联标记-Java文档类资源-CSDN下载