当前位置: 首页 > 科技观察

微服务实现简单的分布式日志跟踪

时间:2023-03-15 11:12:11 科技观察

最近想在项目中添加一个简单的分布式请求跟踪功能,从前端向网关发起请求,然后从网关调用SpringCloud的微服务。可以看到一个带有分布式ID的链接,通过请求的ID可以追溯整个链接,方便排查问题。现成的解决方案自然是使用SkyWalking、SpringCloudSleuth、Zipkin等组件,但想到主要目的是记录一个可以一直走各种服务的ID,方便日志查询,我不不想引入太多复杂的组件,最终决定通过MDC在日志中输出trace的ID,然后在Feign和RestTemplate中通过微服务中的requestID。主要包括几个步骤:前端生成请求ID加入请求头,带入网关,通过WebFilter拦截加入MDC,在日志中输出,带上请求IDFeign和RequestTemplate中的HTTPHeader,通过每个微服务微服务也通过WebFilter拦截并加入MDC,在日志中输出MDCMDC(MappedDiagnosticContext)是Log4j和Logback提供的方便多线程情况下日志记录的功能。MDC可以看作是一个绑定当前线程的哈希表,可以在其中添加键值对。MDC的关键操作:给MDC设置值:MDC.put(key,value);从MDC获取值:MDC.get(key);MDC中打印内容到日志:%X{key}AddTraceId工具类首先添加一个TraceIdUtils工具类,用于定义TRACE_ID的常量值以及设置和生成TRACE_ID的方法。下面的代码都是通过这个估计类来操作的。importorg.apache.commons.lang.RandomStringUtils;importorg.apache.commons.lang.StringUtils;importorg.slf4j.MDC;publicclassTraceIdUtils{publicstaticfinalStringTRACE_ID="traceId";privatestaticfinalintMAX_ID_LENGTH=10;/***生成traceId*/privatestaticStringgenTraceId(){returnRandomStringUtils.randomAlphanumeric(MAX_ID_LENGTH);}/***SettraceId*/publicstaticvoidsetTraceId(StringtraceId){//如果参数为空,生成一个新的IDtraceId=StringUtils.isBlank(traceId)?genTraceId():traceId;//设置traceId放在MDC中MDC.put(TRACE_ID,StringUtils.substring(traceId,-MAX_ID_LENGTH));}/***获取traceId*/publicstaticStringgetTraceId(){//获取StringtraceId=MDC.get(TRACE_ID);//如果traceId为空,则生成一个新的IDreturnStringUtils.isBlank(traceId)?genTraceId():traceId;}}添加一个TraceId过滤器,通过WebFilter添加一个GenericFilterBean,从请求头中获取TraceIdUtils.TRACE_ID对应的值,并在前端发起请求或者在微服务之间传递es,如果没有,TraceIdUtils.setTraceId会生成一个。importorg.springframework.core.annotation.Order;importorg.springframework.web.filter.GenericFilterBean;@WebFilter(urlPatterns="/*",filterName="traceIdFilter")@Order(1)publicclassTraceIdFilterextendsGenericFilterBean{@OverridepublicvoiddoqueFilter(Servestonse,RequestonseFilterChainfilterChain)throwsIOException,ServletException{//traceId初始化HttpServletRequestreq=(HttpServletRequest)request;Stringtrace.getHeader(TraceIdUtils.TRACE_ID);TraceIdUtils.setTraceId(traceId);//执行后续过滤器filterChain.doFilter;}}不要忘记在SpringBoot启动类中加上@ServletComponentScan注解,否则自定义的Filter不会生效。其中“com.yourtion.trace.filter”是TraceIdFilter所在的包名。@ServletComponentScan(basePackages="com.yourtion.trace.filter")@SpringBootApplicationpublicclassMyApplication{publicstaticvoidmain(String[]args){SpringApplication.run(MyApplication.class,args);}}在Feign上添加TraceId,因为@FeignClient的代理类在执行过程中,会用到使用Spring上下文的RequestInterceptor,所以自定义自己的拦截器,然后注入到Spring上下文中,这样就可以在请求的上下文中添加自定义的请求头了。importfeign.RequestInterceptor;importfeign.RequestTemplate;importorg.springframework.stereotype.Service;@ServicepublicclassFeignInterceptorimplementsRequestInterceptor{@Overridepublicvoidapply(RequestTemplatetemplate){template.header(TraceIdUtils.TRACE_ID,TraceIdUtils.getTraceId}IdonRest());通过RestTemplate发起请求。之前我们自己实现了RestTemplateConfig的配置类。这次我们在相关配置中加入:RestTemplaterestTemplate=builder.additionalInterceptors((request,body,execution)->{request.getHeaders().add(TraceIdUtils.TRACE_ID,TraceIdUtils.getTraceId());returnexecution.execute(request,正文);}).build();至此,链接上的TraceId已经添加完毕,剩下的就是在日志中打印出来了。修改Log4j2的布局格式修改日志的布局格式,打印出MDC中的traceId:)}%n"/>)}%n"/>至此,修改完成。

最新推荐
猜你喜欢