对于后端开发来说,排查问题是常有的事件。而排查问题时最罕用的就是看日志,看一次调用中通过了哪些零碎,是那个零碎出问题了。这就须要业务日志中关联调用链的TraceId信息,从而在利用呈现问题时,可能通过调用链的TraceId疾速关联到业务日志,及时定位剖析、解决问题。
之前从事的公司都有这种链路中间件,当初阿里团体的eagleeye 鹰眼零碎也是的,接入应用就行了,然而原理齐全不分明。不理解TraceId怎么生成的,怎么在零碎之间传递。所以明天先来实现一个最简略的TraceId
解决方案
- 本人生成 traceId 并 put 到 MDC 外面
MDC
MDC(Mapped Diagnostic Context)是一个映射,用于存储运行上下文的特定线程的上下文数据。因而,如果应用log4j进行日志记录,则每个线程都能够领有本人的MDC,该MDC对整个线程是全局的。属于该线程的任何代码都能够轻松拜访线程的MDC中存在的值。申请时,将TraceId放在header里,服务方从header里读取进去,并在日志上打印即可
如何将TraceId放到MDC中
1.日志文件logback-spring.xml
配置
打印黑白日志,须要在日志格局中加上[%X{TRACE_ID}]
,变量名TRACE_ID是本人定义的
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,能够使“${}”来应用变量。 -->
<property name="log.path" value="log" />
<!-- 黑白日志 -->
<!-- 黑白日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 黑白日志格局,留神加上TRACE_ID -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} [%X{TRACE_ID}] %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--输入到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发应用,只配置最底级别,控制台输入的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
2.新增拦截器
拦挡所有申请,从header里取出traceId而后放到MDC中,这样该工程所有地位都能读取到。如果header里没有,则本人生成一个,生成规定可参考阿里云帮忙文档: TraceId 和 SpanId 生成规定
@Component
public class LogInterceptor implements HandlerInterceptor {
private String TRACE_ID = "TRACE_ID";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果有下层调用就用下层的ID
String traceId = request.getHeader(TRACE_ID);
if (traceId == null) {
traceId = UUID.randomUUID().toString();
}
MDC.put(TRACE_ID, traceId);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
// Do nothing because of no business
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
//调用完结后删除
MDC.remove(TRACE_ID);
}
}
3.注册拦截器
将上一步的日志拦截器注册,并拦挡所有门路的HTTP申请,让其失效
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor)
.addPathPatterns("/**");
}
}
4.测试成果
到这一步就能够试试日志打印TraceId的成果了,轻易写一个接口,并打印日志。如下图曾经胜利打印出Traceid了
其余计划
下面演示的是最简陋的形式,公司外部个别都会再造一遍轮子,让各个利用接入应用的。前面再写几个开源的TraceId生成框架,更深刻的去理解全链路治理
发表回复