目录
Log4j2概述
Log4j2的同步和异步日志配置
Springboot整合Log4j2
a. 依赖b. 配置文件
c. log4j2.xml文件
d. Demo逻辑
e. 测试结果
整合TLog实现trace跟踪
整合TLog实现接口调用参数和耗时打印
参考
Log4j2概述
日志门面
门面模式(Facade Pattern),也称之为外观模式,其核心为:外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。
就像前面介绍的几种日志框架一样,每一种日志框架都有自己单独的API,要使用对应的框架就要使用其对应的API,这就大大的增加应用程序代码对于日志框架的耦合性。
为了解决这个问题,就是在日志框架和应用程序之间架设一个沟通的桥梁,对于应用程序来说,无论底层的日志框架如何变,都不需要有任何感知。只要门面服务做的足够好,随意换另外一个日志框架,应用程序不需要修改任意一行代码,就可以直接上线。
在软件开发领域有这样一句话:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。而门面模式就是对于这句话的典型实践。
为什么选用log4j2
相比与其他的日志系统,log4j2丢数据这种情况少;disruptor技术,在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生;
在这列举一下一些网上其他博文中对它们的性能评测:
- 可以看到在同步日志模式下, Logback的性能是最糟糕的.
- log4j2的性能无论在同步日志模式还是异步日志模式下都是最佳的.
Log4j2的同步和异步日志配置
Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger两种方式。
详细解释和原理参考文档:https://www.cnblogs.com/yeyang/p/7944906.html
所谓同步日志,即当输出日志时,必须等待日志输出语句执行完毕后,才能执行后面的业务逻辑语句。Log4j2的同步和异步日志配置
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="debug" name="MyApp" packages=""> <!--全局Filter--> <ThresholdFilter level="ALL"/> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/app-%d{yyyy-MM-dd HH}.log"> <!--Appender的Filter--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="500MB"/> </Policies> </RollingFile> </Appenders> <Loggers> <Logger name="com.meituan.Main" level="trace" additivity="false"> <!--Logger的Filter--> <ThresholdFilter level="debug"/> <appender-ref ref="RollingFile"/> </Logger> <Root level="debug"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
Log4j2中的异步日志实现方式有AsyncAppender和AsyncLogger两种。
其中,AsyncAppender采用了ArrayBlockingQueue来保存需要异步输出的日志事件;AsyncLogger则使用了Disruptor框架来实现高吞吐。
AsyncAppender模式
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn"> <Appenders> <RollingFile name="MyFile" fileName="logs/app.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <SizeBasedTriggeringPolicy size="500MB"/> </RollingFile> <Async name="Async"> <AppenderRef ref="MyFile"/> </Async> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Async"/> </Root> </Loggers> </Configuration>
AsyncLogger模式
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="debug" name="MyApp" packages=""> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/app-%d{yyyy-MM-dd HH}.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="500MB"/> </Policies> </RollingFile> <RollingFile name="RollingFile2" fileName="logs/app2.log" filePattern="logs/app2-%d{yyyy-MM-dd HH}.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="500MB"/> </Policies> </RollingFile> </Appenders> <Loggers> <AsyncLogger name="com.meituan.Main" level="trace" additivity="false"> <appender-ref ref="RollingFile"/> </AsyncLogger> <AsyncLogger name="RollingFile2" level="trace" additivity="false"> <appender-ref ref="RollingFile2"/> </AsyncLogger> <Root level="debug"> <AppenderRef ref="Console"/> <AppenderRef ref="RollingFile"/> </Root> </Loggers> </Configuration>
Springboot整合Log4j2
依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.14</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- spring boot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions><!-- 去掉springboot默认配置 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring boot test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<!-- 引入log4j2依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--使用Async异步日志需要的依赖-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
<!--工具类相关-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.30</version>
</dependency>
</dependencies>
配置文件
server:
port: 8080
servlet:
context-path: /
logging:
config: classpath:log4j2.xml
Log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--变量配置-->
<Properties>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 定义日志存储的路径 -->
<property name="FILE_PATH" value="logs"/>
<property name="PROJECT_NAME" value="SpringBoot4Log4j2"/>
<property name="log_level" value="info"/>
</Properties>
<appenders>
<console name="SYSTEM-OUT-APPENDER" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="${LOG_PATTERN}"/>
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<!-- <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>-->
</console>
<Console name="SYSTEM-ERR-APPENDER" target="SYSTEM_ERR">
<!--输出日志的格式-->
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
<PatternLayout pattern="${LOG_PATTERN}"/>
</File>
<RollingFile name="ERROR-APPENDER" fileName="${FILE_PATH}/common-error.log"
filePattern="${FILE_PATH}/common-error.log.%d{yyyy-MM-dd}" append="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies/>
<ThresholdFilter level="ERROR"/>
</RollingFile>
<RollingFile name="APP-DEFAULT-APPENDER" fileName="${FILE_PATH}/app-default.log"
filePattern="${FILE_PATH}/app-default.log.%d{yyyy-MM-dd}" append="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies/>
</RollingFile>
<RollingFile name="DIGEST-APPENDER" fileName="${FILE_PATH}/springboot-digest.log"
filePattern="${FILE_PATH}/springboot-digest.log.%d{yyyy-MM-dd}" append="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies/>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
<!-- 每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="SPRINGBOOT4LOG4J2-CONTROLLER-APPENDER" fileName="${FILE_PATH}/springboot-controller.log"
filePattern="${FILE_PATH}/springboot-controller.log.%d{yyyy-MM-dd_HH}" append="true">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<!-- <Policies>-->
<!-- <!–interval属性用来指定多久滚动一次,默认是1 hour–>-->
<!-- <TimeBasedTriggeringPolicy interval="1"/>-->
<!-- <SizeBasedTriggeringPolicy size="10MB"/>-->
<!-- </Policies>-->
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<Policies/>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
<!-- 每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="SPRINGBOOT4LOG4J2-SERVICE-APPENDER" fileName="${FILE_PATH}/springboot-service.log"
filePattern="${FILE_PATH}/springboot-service.log.%d{yyyy-MM-dd_HH}" append="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies/>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
<!-- 每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="SPRINGBOOT4LOG4J2-REPOSITORY-APPENDER" fileName="${FILE_PATH}/springboot-repository.log"
filePattern="${FILE_PATH}/springboot-repository.log.%d{yyyy-MM-dd_HH}" append="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies/>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
</appenders>
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<root level="${log_level}">
<appender-ref ref="ERROR-APPENDER"/>
<appender-ref ref="APP-DEFAULT-APPENDER"/>
</root>
<!--name:用来指定该Logger所适用的类或者类所在的包全路径, 或自定义的logger名称-->
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
<logger name="SYSTEM-OUT" level="${log_level}" additivity="false">
<AppenderRef ref="SYSTEM-OUT-APPENDER"/>
</logger>
<logger name="SYSTEM-ERR" level="${log_level}" additivity="false">
<AppenderRef ref="SYSTEM-ERR-APPENDER"/>
</logger>
<logger name="com.json.controller" level="${log_level}" additivity="false">
<AppenderRef ref="SPRINGBOOT4LOG4J2-CONTROLLER-APPENDER"/>
<appender-ref ref="ERROR-APPENDER"/>
</logger>
<Logger name="com.json.service" level="${log_level}" additivity="false">
<AppenderRef ref="SPRINGBOOT4LOG4J2-SERVICE-APPENDER"/>
<appender-ref ref="ERROR-APPENDER"/>
</Logger>
<Logger name="com.json.repository" level="${log_level}" additivity="false">
<AppenderRef ref="SPRINGBOOT4LOG4J2-REPOSITORY-APPENDER"/>
<appender-ref ref="ERROR-APPENDER"/>
</Logger>
<Logger name="DIGEST" level="${log_level}" additivity="false">
<AppenderRef ref="DIGEST-APPENDER"/>
<appender-ref ref="ERROR-APPENDER"/>
</Logger>
</loggers>
</configuration>
Demo逻辑
public class User implements Serializable {
private static final long serialVersionUID = -7351729135012380019L;
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
@Component
public class UserRepository {
private static final Logger logger = LoggerFactory.getLogger(UserRepository.class);
/**
* 返回用户信息
* @param name
* @return
*/
public User getUserByName(String name){
logger.info("查询{}用户信息", name);
return new User(name, 28);
}
}
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
@Autowired
private UserRepository userRepository;
/**
* 查询用户信息
* @param name
* @return
*/
public User queryUser(String name){
logger.info("查询{}用户信息", name);
return userRepository.getUserByName(name);
}
}
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
private static final Logger digest = LoggerFactory.getLogger("DIGEST");
@Autowired
private UserService userService;
@RequestMapping("/user")
public String getUser(String name) {
logger.info("UserController.class 收到{}用户请求", name);
try {
String res = userService.queryUser(name).toString();
digest.info("请求{}用户信息: {}", name, res);
return res;
} catch (Exception e) {
logger.info("请求{}用户信息错误", name, e);
return StringUtils.EMPTY;
}
}
}
@SpringBootApplication
public class Log4j2App {
public static void main(String[] args) {
SpringApplication.run(Log4j2App.class, args);
}
}
测试结果
整合TLog实现trace跟踪
TLog介绍和使用的官网文档:https://tlog.yomahub.com/pages/5b7bd2/
在上述Demo中做整合
pom添加TLog依赖。注意TLog要在Log4j2前导入
<!--TLog日志链路追踪. 要保证在log4j2依赖包前面导入--> <dependency> <groupId>com.yomahub</groupId> <artifactId>tlog-all-spring-boot-starter</artifactId> <version>1.5.0</version> <exclusions> <exclusion> <artifactId>commons-lang</artifactId> <groupId>commons-lang</groupId> </exclusion> <exclusion> <artifactId>fastjson</artifactId> <groupId>com.alibaba</groupId> </exclusion> </exclusions> </dependency>
log4j2.xml文件修改。
%TX{tl}
为TLog标签,%m
替换为%tm
<property name="LOG_PATTERN" value="%d [%TX{tl} %X{rpcId} - %X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %tm%n%throwable"/>
运行效果
整合TLog实现接口调用参数和耗时打印
在application.yml中添加配置
# TLog配置 tlog: # 自动打印接口调用参数和耗时 enable-invoke-time-print: true
测试结果