背景:
TraceId是我在项目上自定义的,作为链路追踪来使用。至于为什么没有用市面上skywalking此类现成的链路追踪,是因为目前这个项目是boot单体,也不是分布式,不想引入过多比较复杂的依赖项,造成不必要的维护麻烦。使用traceId只是来准确定位某个请求的日志信息,自己实现的话比较轻量化,也能够满足项目的开发和维护需求。
当请求访问时,被过滤器拦截到,生成一个唯一的traceId存放在MDC中(分布式项目可以通过请求头获取上游服务传过来的traceId),MDC是ThreadLocal类型线程安全的。日志xml配置写上traceId会自动读取MDC。
logback日志框架
由于之前项目是使用的logback日志框架,如果只在xml日志文件中配置traceId的话,当报错时候输出的堆栈信息内不会保存traceId。到时候线上排查问题,无法通过grep命令筛选出来报错的堆栈信息,也就不能快速定位代码中的报错位置。
堆栈信息不携带traceId
xml配置文件中的配置
![](https://img-blog.csdnimg.cn/img_convert/6303bb26ab904a6769817de46e787e24.png)
堆栈信息携带traceId
logback想要在堆栈信息中添加traceId需要继承ThrowableProxyConverter,重写里面的方法。
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.IThrowableProxy;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
public class CompressedStackTraceConverter extends ThrowableProxyConverter {
public CompressedStackTraceConverter() {
}
@Override
protected String throwableProxyToString(IThrowableProxy tp) {
String original = super.throwableProxyToString(tp);
String traceId = MDC.get("traceId");
if (StringUtils.isBlank(traceId)) {
traceId = "";
}
traceId = "\t[ably-stack]\t[" + traceId + "]";
String osName = System.getProperties().get("os.name").toString().toLowerCase(Locale.ROOT);
return osName.startsWith("win") ? original.replaceAll("\r\n\t", traceId + "\r\n\t") : original.replaceAll("\n\t", traceId + "\n\t");
}
}
此时的xml配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<conversionRule conversionWord="ex"
converterClass="com.example.springbootdemo.configuration.httpglobal.CompressedStackTraceConverter"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern xml:space="preserve">
%d{yyyy-MM-dd HH:mm:ss.SSS}|-|%lsn|-|%thread|-|%X{traceType}|-|%X{traceId}|-|%logger{80}:%L|-|%msg|-|%ex|-|%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
这样logback框架就可以打印堆栈信息的时候携带traceId了
log4j日志框架
目前这个新项目使用的log4j这个框架,这里的traceId也是放在MDC中。
堆栈信息不携带traceId
![](https://img-blog.csdnimg.cn/img_convert/7518e1aea220832a3e79f9adfc5179b1.png)
堆栈信息携带traceId
当我配置好traceId后,开始配置堆栈信息中的traceId,发现ThrowableProxyConverter是logback包下面的,无法使用它来添加。于是我在网上找是否log4j有对应的像logback这种接口可以操作堆栈信息的。无果!(也有可能我没找到)。
后来偶然查到%throwable可以支持更改单个堆栈跟踪元素,我就想可不可以使用它来添加traceId。通过了解%throwable的常用方法,看到了配合{suffix(pattern)}使用,可以在每个堆栈帧的末尾添加模式输出。于是抱着试一试的态度就打印traceId,结果是可以的。
![](https://img-blog.csdnimg.cn/img_convert/e84d7cdb805122494c8297a1f3e3b1eb.png)
对应的traceId在堆栈信息以及主日志中都打印了出来
![](https://img-blog.csdnimg.cn/img_convert/63b0d79697f163b810abc08d26aaba81.png)