在Log4j2中实现只有在traceId有值时才输出
文章目录
记录如何在Log4j2中使用traceId实现链路追踪时只有在请求header中有trace-id
时才显示前端发送过来的trace-id
值。
前置工作
完整代码参见spring-mybatis-demo。
假设使用的程序框架为Spring Boot
并通过Maven
管理依赖,需要预先添加如下代码配置:
-
pom.xml
中排除Spring Boot
自带的日志框架并添加log4j2
依赖1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>${spring.boot.version}</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.boot.version}</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <version>${spring.boot.version}</version> </dependency>
-
添加
Filter
用于从请求header中获取前端传递的trace-id
并传入MDC中1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
@Order(0) @Component @WebFilter(filterName = "traceIdFilter", urlPatterns = "/*") public class TraceIdFilter implements Filter { public static final String TRACE_ID = "TRACE_ID"; public static final String TRACE_ID_HEADER = "trace-id"; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; String traceId = request.getHeader(TRACE_ID_HEADER); MDC.put(TRACE_ID, traceId); chain.doFilter(req, res); } @Override public void destroy() { MDC.clear(); } }
-
在主启动类中添加如下配置用于在线程间传递
MDC
值1 2 3 4 5 6 7 8 9 10 11 12
@SpringBootApplication public class TestApplication { // 用于在线程间传递mdc值 static { System.setProperty("log4j2.isThreadContextMapInheritable", "true"); } public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } }
-
log4j2.xml
配置如下1 2 3 4 5 6 7 8 9 10 11 12 13 14
<Configuration status="warn"> <Appenders> <!-- Console appender configuration --> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%yellow{[%d{yyyy-MM-dd HH:mm:ss.SSS}]} %clr{[%-5level]} %green{[TraceId-%X{TRACE_ID}]} %cyan{[%c]}- %msg%n" /> </Console> </Appenders> <Loggers> <!-- Root logger referring to console appender --> <Root level="info" additivity="false"> <AppenderRef ref="console" /> </Root> </Loggers> </Configuration>
其中的
%green{[TraceId-%X{TRACE_ID}]}
就是将获取到的trace-id
在控制台中按照TraceId-xxx
的格式以绿色打印出来,该段配置是我们进行链路最终的核心。其中
%X
专门用于获取MDC
中的值,在log4j2
官网对应的说明如下
测试&问题
测试代码如下
|
|
在Postman中发送类似如下请求,在header中将trace-id
的值设置为123456
在控制台可发现类似如下输出,可发现前面几条系统启动时输出的日志也会按照trace-id
格式进行输出,由于此时还没接收到前端发送的请求,故其值为空导致只显示一个TraceId-
前缀,看起来不美观且影响使用。
解决方案
终极目标是实现在有trace-id
时按照格式输出,没有时则正常输出,很明显要达成此效果需要判断trace-id
是否为空。
最开始Google
和Stackoverflow
搜索的结果建议使用ScriptPatternSelector,在官网给出的使用示例如下,看起来有一丢丢复杂,在有多个Appenders时不适合扩展。
|
|
上述方式需要设置两个Pattern
且在不同的Appender
中都需要动态配置,使用起来不是特别灵活。
有木有可能通过一个Pattern
同时兼容这两种情况呢?
经过网络搜索和查看log4j2
中关于Pattern Layout的说明之后,最终找到了解决方案,那就是%notEmpty
!
官方文档中关于%notEmpty
的说明如下
基于上述说明可尝试将%green{%notEmpty{[TraceId-%X{TRACE_ID}]}}
修改为%green{%notEmpty{[TraceId-%X{TRACE_ID}]}}
,最终完整的PatternLayout
如下
|
|
重新调用Postman
的输出结果如下,完美实现功能!
服务间调用
在微服务中通常会有多个模块协同工作,各个模块之间也可能会互相调用,在此种情况下也需要将trace-id
准确的传递与获取,确保单次请求调用链路中的trace-id
都一样才能体现链路追踪的意义。
Feign调用
当使用Feign
作为远程调用实现时,需在被调用方中添加如下配置代码才能确保trace-id
被正常传递与获取。
|
|