Log4j2介绍
Log4j2是Log4j的升级版,参考Logback的优秀设计并修复了一些问题。主要优点有:
- 异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。
- 性能提升,log4j2相较于log4j和logback都具有很明显的性能提升。
- 自动重载配置,参考了logback的设计,会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
- 无垃圾机制,log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvmgc。
Log4j2的简单使用
Log4j2和Slf4j一样,既可以当门户也可以当日志实现。但是通常都是用Slf4j当门户,Log4j2当日志实现。先单独使用Log4j2进行日志打印
<dependencies>
<!--log门户-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.19.0</version>
</dependency>
<!--log日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
public class TestLog4j2 {
public final static Logger logger = LogManager.getLogger(TestLog4j2.class);
@Test
public void testLog() throws Exception {
logger.fatal("fatal");
logger.error("error");
logger.warn("wring");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
20:05:28.830 [main] FATAL com.zmt.TestLog4j2 - fatal
20:05:28.831 [main] ERROR com.zmt.TestLog4j2 - error
可以看出,Log4j2的默认级别为error。
接下来使用Slf4j与Log4j2混合使用。
只需要在pom文件中添加slf4j-api与log4j-slf4j-impl依赖就好。其余什么都不用改变。
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
Log4j2的配置文件
log4j2默认加载classpath路径下的log4j2.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!--
status:日志框架本身的输出日志级别(输出框架信息)
monitorInterval:自动加载配置文件的间隔时间,在生产环境中不需要重启项目
-->
<configuration status="warn" monitorInterval="5">
<properties>
<property name="log_dir" value="D:/logs"></property>
</properties>
<!--
日志输出格式:
%-5level 日志级别
%d{yyyy-MM-dd HH:mm:ss}日期
%c类的完整名称
%M为method
%L为行号
%thread线程名称
%m或者%msg为信息
%n换行
-->
<Appenders>
<!--控制台输出 appender-->
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
</Console>
<!--日志文件输出 appender-->
<File name="file" fileName="${log_dir}/myfile.log">
<Patternlayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] %l %c{36} - %m%n"/>
</File>
<!--使用随机读写流的日志文件输出appender,性能提高-->
<RandomAccessFile name="accessFile" fileName="${log_dir}/myAcclog.log">
<Patternlayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] %l %c{36} - %m%n"/>
</RandomAccessFile>
<!--按照一定规则拆分的日志文件的 appender-->
<RollingFile name="rollingFile" fileName="${log_dir}/myrollog.log"
filePattern="${log_dir}/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!--日志级别过滤器-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
<!--日志消息格式-->
<Patternlayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [-5level] %l %c{36} - %msg%n"/>
<Policies>
<!--在系统启动时,出发拆分规则,生产一个新的日志文件-->
<OnStartupTriggeringPolicy/>
<!--按照文件大小拆分,10MB-->
<SizeBasedTriggeringPolicy size="10 MB"/>
<!--按照时间节点拆分,规则根据filePattern定义的-->
<TimeBasedTriggeringPolicy/>
</Policies>
<!--在同一个目录下,文件的个数限定为 30 个,超过进行覆盖-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<!--logger 定义-->
<Loggers>
<!--使用 rootLogger 配置 日志级别 level="trace"-->
<Root Level="trace">
<!--指定日志使用的处理器-->
<AppenderRef ref="Console"/>
<AppenderRef ref="file"/>
</Root>
</Loggers>
</configuration>
Log4j2异步日志
异步日志分为三种:
- AsyncAppender:异步Appender
- 全局AsyncLogger:全局异步Logger
- 混合AsyncLogger:异步与同步混合使用的Logger
其中异步Appender的性能最差,基本不使用。
全局异步Logger性能最好,但是有时并不是所有的业务都需要异步记录日志,因此也不常用。
通常使用混合AsyncLogger。
异步Appender简单实现
在xml配置文件中Appenders标签下添加配置
<Appenders>
<!--控制台输出 appender-->
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
</Console>
<!--日志文件输出 appender-->
<File name="file" fileName="${log_dir}/myfile.log">
<Patternlayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] %l %c{36} - %m%n"/>
</File>
<!--配置异步实现-->
<Async name="async">
<AppenderRef ref="file"></AppenderRef>
</Async>
</Appenders>
<!--logger 定义-->
<Loggers>
<!--使用 rootLogger 配置 日志级别 level="trace"-->
<Root Level="trace">
<!--指定日志使用的处理器-->
<AppenderRef ref="Console"/>
<AppenderRef ref="async"/>
</Root>
</Loggers>
public class TestLog4j2 {
public final static Logger logger = LogManager.getLogger(TestLog4j2.class);
@Test
public void testLog() throws Exception {
logger.fatal("fatal");
logger.error("error");
logger.warn("wring");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
观察日志文件
全局异步Logger简单实现
只需要在resource目录下创建文件名为log4j.component.properties添加如下配置
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
log4j2.xml配置文件不需要做任何改动。
混和异步Logger简单实现
如果使用混合异步Logger,需要将全局异步Logger的配置文件注释或删除,并且需要添加依赖。
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
在log4j.xml文件中进行修改
<!--logger 定义-->
<Loggers>
<!--自定义Logger-->
<!--
includeLocation:设为false作用是关闭日志行号记录,打开的话会影响性能
additivity:不再继承rootLogger
-->
<AsyncLogger name="com.zmt" level="debug" includeLocation="false" additivity="false">
<AppenderRef ref="Console"/>
</AsyncLogger>
<!--使用 rootLogger 配置 日志级别 level="trace"-->
<Root Level="trace">
<!--指定日志使用的处理器-->
<AppenderRef ref="Console"/>
<AppenderRef ref="async"/>
</Root>
</Loggers>
观察控制台输出
可以看到,即使日志格式中添加了%L但仍不会再控制台中打印行号
注意:
- 如果使用异步日志AysncAppender、AsyncLogger与全局日志不要同时出现,性能会和最差的一致
- 设置includeLocation为false是必要的,打印位置信息会降低异步日志的性能,比同步日志还慢。
Log4j2的无垃圾模式
其他日志系统在记录日志时会分配临时对象,如日志事件对象、字符串、字符数组等,当临时对象过多时会触发JVM的gc操作(该操作会暂停程序的运行)。而Log4j2从2.6版本开始,默认情况下以无垃圾模式运行,主要原理是重用对象和缓冲区,尽可能的不分配临时对象。