Log4j2简介
日志框架slf4j、j.u.l、log4j、logback、log4j2的比较 和 log4j2配置文件详解,请参考上篇文章《Log4j2使用详解》
Log4j2实例
下面我们通过实例来看看Log4j2在Spring Boot中的应用
1、引入log4j2依赖
Spring Boot默认使用LogBack,如果我们要使用Log4j2,需要从spring-boot-starter-web中去掉spring-boot-starter-logging依赖,同时显式声明使用Log4j2的依赖jar包,具体如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 去掉默认配置 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入log4j2依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2、创建并配置log4j2-spring.xml
在src/main/resources文件夹下创建log4j2-spring.xml文件,并写入如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<properties>
<property name="logBase">~/</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout charset="UTF-8" pattern="[%d] [%-5p] [%t] [%c] - %m%n"/>
</Console>
<RollingFile name="RollingFile" fileName="${logBase}/logs/access.log" filePattern="${logBase}/logs/access.%d{yyyy-MM-dd}.log">
<PatternLayout pattern="[%d] [%-5p] [%t] [%c] - %m%n"/>
<TimeBasedTriggeringPolicy/>
</RollingFile>
<RollingFile name="ErrorFile" fileName="${logBase}/logs/error.log" filePattern="${logBase}/logs/error.%d{yyyy-MM-dd}.log">
<Filters>
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="[%d] [error] [%t] [%c] - %m%n"/>
<TimeBasedTriggeringPolicy/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
</Root>
</Loggers>
</configuration>
各节点的详细说明,详见《Log4j2使用详解》
logBase请根据项目的实际日志目录修改
3、打印日志
一般情况下,我们使用LoggerFactory去获取Logger对象,在BlogController中我们加入如下内容:
private final static Logger logger = LoggerFactory.getLogger(BlogController.class);
@GetMapping(value = "log")
public String printLog() {
logger.trace("trace log");
logger.debug("debug log");
logger.info("info log");
logger.warn("warn log");
logger.error("error log");
return "print log ok";
}
调用接口,可以看到控制台和日志目录下的logs/access.log文件中从trace log到error log都依次打印出来,而error.log中打印出了error log。
如果把log4j2-spring.xml文件中的<Root level="trace”>修改为<Root level="info”>,则日志从info级别开始打印,trace和debug日志不会打印。
多环境配置日志文件
Spring Boot默认加载log4j2-spring.xml文件,如果我们想像application.yml配置文件一样,不同的环境配置不同的log4j2文件,比如本地环境需要在控制台打印出来,测试环境和线上的日志目录不同等,这时候我们也可以根据环境的不同配置不同的日志文件。
1、创建log4j2-dev.xml、log4j2-test.xml和log4j2-prod.xml
log4j2-dev.xml:把log4j2-spring.xml文件内容copy到log4j2-dev.xml中
log4j2-test.xml:去掉控制台打印、修改logBase变量、修改root的日志level
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<properties>
<property name="logBase">~/test/</property>
</properties>
<Appenders>
<RollingFile name="RollingFile" fileName="${logBase}/logs/access.log" filePattern="${logBase}/logs/access.%d{yyyy-MM-dd}.log">
<PatternLayout pattern="[%d] [%-5p] [%t] [%c] - %m%n"/>
<TimeBasedTriggeringPolicy/>
</RollingFile>
<RollingFile name="ErrorFile" fileName="${logBase}/logs/error.log" filePattern="${logBase}/logs/error.%d{yyyy-MM-dd}.log">
<Filters>
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="[%d] [error] [%t] [%c] - %m%n"/>
<TimeBasedTriggeringPolicy/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
</Root>
</Loggers>
</configuration>
log4j2-prod.xml:修改logBase变量为~/prod/,其他同log4j2-test.xml
2、删除log4j2-spring.xml
3、多环境配置日志文件
application-dev.yml增加配置节点:
logging:
config: classpath:log4j2-dev.xml
application-test.yml增加配置节点:
logging:
config: classpath:log4j2-test.xml
application-prod.yml增加配置节点:
logging:
config: classpath:log4j2-prod.xml
4、修改环境变量,查看日志打印结果
IDEA中通过修改Active profiles来切换dev、test和prod环境,可以看到不同的环境打印出的日志
使用traceId跟踪请求全流程日志
线上我们一般采用多机部署,用kibana收集日志,但是在并发大的时候,使用日志定位问题比较麻烦,很难筛选出指定请求的全部相关日志。因此我们可以对日志打印做一个改造,使用traceId跟踪请求的全部路径。
1、MDC简介
MDC:Mapped Diagnostic Context,映射调试上下文,它是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。
2、创建AccessInterceptor
在com.tn666.demo目录下创建interceptor文件夹,在此文件夹下创建AccessInterceptor类文件:
public class AccessInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
return true;
}
}
preHandle方法会在Controller处理之前进行调用,我们在请求开始时,向MDC中写入了一个traceId,这里主要说一下traceId的记录,更多拦截器的内容,后续的文章中会详细介绍
3、创建MvcConfiguration
在com.tn666.demo.configuration文件夹下创建MvcConfiguration类文件:
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AccessInterceptor());
}
}
4、读取traceId
在 log4j 和 logback 的取值方式为:
%X{traceId}
在日志文件的PatternLayout中加入traceId的读取:
<PatternLayout pattern="[%d] [%X{traceId}] [%-5p] [%t] [%c] - %m%n"/>
5、日志打印
访问接口,可以看到traceId的打印
从打印出的日志可以看出,这是两次http请求,有两个traceId
6、下游服务使用相同traceId
查找问题时,有时候我们希望把一次请求串起来,包括调用第三方服务,这时候我们可以采用如下方法:
1)改造http调用工具
在发送http请求时,自动将traceId添加到header中
2)下游服务的拦截器修改
下游服务的拦截器,首先从header中获取traceId,写入MDC中,若header中没有traceId,再使用UUID
文章中的示例代码,可以在https://github.com/tunan66666/spring-boot-demo上查看
若您觉得还可以,请帮忙点个“赞”,谢谢