共计 3105 个字符,预计需要花费 8 分钟才能阅读完成。
对于后端开发来说,排查问题是常有的事件。而排查问题时最罕用的就是看日志,看一次调用中通过了哪些零碎,是那个零碎出问题了。这就须要业务日志中关联调用链的 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 生成框架,更深刻的去理解全链路治理