Apache Log4j2
是对Log4j
的升级版,参考了logback
的一些优秀的设计,并且修复了一些问题,因此带
来了一些重大的提升主要有:
jvm gc
。目前市面上最主流的日志门面就是SLF4J
,虽然Log4j2
也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将Log4j2
看作是日志的实现加粗样式,Slf4j + Log4j2
应该是未来的大势所趋。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>log4j2_demo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--使用slf4j 作为日志门面--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--使用 log4j2 的适配器进行绑定--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.9.1</version> </dependency> <!--log4j2日志门面--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.11.1</version> </dependency> <!--log4j2 日志实现--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.11.1</version> </dependency> <!--异步日志依赖--> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.4</version> </dependency> <!--junit 单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <plugins> <!-- 设置编译版本为1.8 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; public class Log4j2Test { // 定义日志记录器对象 public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class); // 快速入门 @Test public void testQuick()throws Exception{ // 日志消息输出 LOGGER.fatal("fatal"); LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("inf"); LOGGER.debug("debug"); LOGGER.trace("trace"); } }
当我们运行时,出现以下情况是因为少了个配置文件,解决方法是在resource目录下新建一个log4j2.xml
log4j.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- status="warn" 日志框架本身的输出日志级别 monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒 --> <Configuration status="debug" monitorInterval="5"> <!-- 集中配置属性进行管理 使用时通过:${name} --> <properties> <property name="LOG_HOME">/logs</property> </properties> <!--日志处理--> <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_HOME}/myfile.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /> </File> <!--<Async name="Async">--> <!--<AppenderRef ref="file"/>--> <!--</Async>--> <!--使用随机读写刘的日志文件输出 appender,性能提高--> <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /> </RandomAccessFile> <!--按照一定规则拆分的日志文件的 appender--> <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="/logs/$${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.SSS}] [%-5level] %l %c{36} - %msg%n" /> <Policies> <!--在系统启动时,出发拆分规则,生产一个新的日志文件--> <OnStartupTriggeringPolicy /> <!--按照文件大小拆分,10MB --> <SizeBasedTriggeringPolicy size="10 MB" /> <!--按照时间节点拆分,规则根据filePattern定义的--> <TimeBasedTriggeringPolicy /> </Policies> <!--在同一个目录下,文件的个数限定为 30 个,超过进行覆盖--> <DefaultRolloverStrategy max="30" /> </RollingFile> </Appenders> <!--logger 定义--> <Loggers> <!--自定义异步 logger 对象 includeLocation="false" 关闭日志记录的行号信息 additivity="false" 不在继承 rootlogger 对象 --> <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="Console"/> </AsyncLogger> <!--使用 rootLogger 配置 日志级别 level="trace"--> <Root level="trace"> <!--指定日志使用的处理器--> <AppenderRef ref="Console" /> <!--使用异步 appender--> <AppenderRef ref="Async" /> </Root> </Loggers> </Configuration>
log4j2
默认加载classpath
下的 log4j2.xml
文件中的配置。
<?xml version="1.0" encoding="UTF-8"?> <!-- status="warn" 日志框架本身的输出日志级别 monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒 --> <Configuration status="debug" monitorInterval="5"> <!-- 集中配置属性进行管理 使用时通过:${name} --> <properties> <property name="LOG_HOME">/logs</property> </properties> <!--日志处理--> <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_HOME}/myfile.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /> </File> <!--<Async name="Async">--> <!--<AppenderRef ref="file"/>--> <!--</Async>--> <!--使用随机读写流的日志文件输出 appender,性能提高--> <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /> </RandomAccessFile> <!--按照一定规则拆分的日志文件的 appender--> <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="/logs/$${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.SSS}] [%-5level] %l %c{36} - %msg%n" /> <Policies> <!--在系统启动时,触发拆分规则,生产一个新的日志文件--> <OnStartupTriggeringPolicy /> <!--按照文件大小拆分,10MB --> <SizeBasedTriggeringPolicy size="10 MB" /> <!--按照时间节点拆分,规则根据filePattern定义的--> <TimeBasedTriggeringPolicy /> </Policies> <!--在同一个目录下,文件的个数限定为 30 个,超过进行覆盖--> <DefaultRolloverStrategy max="30" /> </RollingFile> </Appenders> <!--logger 定义--> <Loggers> <!--自定义异步 logger 对象 includeLocation="false" 关闭日志记录的行号信息 additivity="false" 不在继承 rootlogger 对象 --> <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="Console"/> </AsyncLogger> <!--使用 rootLogger 配置 日志级别 level="trace"--> <Root level="trace"> <!--指定日志使用的处理器--> <AppenderRef ref="Console" /> <!--使用异步 appender--> <AppenderRef ref="Async" /> </Root> </Loggers> </Configuration>
异步日志
log4j2
最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用log4j2
的异步日志。
同步日志
异步日志
Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender
,一个是通过AsyncLogger
,分别对应
前面我们说的Appender组件和Logger组件。
注意:配置异步日志需要添加依赖
<!--异步日志依赖--> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.4</version> </dependency>
1. AsyncAppender方式
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn"> <properties> <property name="LOG_HOME">D:/logs</property> </properties> <Appenders> <File name="file" fileName="${LOG_HOME}/myfile.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> </File> <Async name="Async"> <AppenderRef ref="file"/> </Async> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Async"/> </Root> </Loggers> </Configuration>
2. AsyncLogger方式
AsyncLogger
才是log4j2
的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log
返回的更快。你可以有两种选择:全局异步和混合异步。
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerCon textSelector
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <properties> <property name="LOG_HOME">D:/logs</property> </properties> <Appenders> <File name="file" fileName="${LOG_HOME}/myfile.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> </File> <Async name="Async"> <AppenderRef ref="file"/> </Async> </Appenders> <Loggers> <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="file"/> </AsyncLogger> <Root level="info" includeLocation="true"> <AppenderRef ref="file"/> </Root> </Loggers> </Configuration>
如上配置: com.itheima 日志是异步的,root日志是同步的。
Log4j2
最牛的地方在于异步输出日志时的性能表现,Log4j2
在多线程的环境下吞吐量与Log4j
和
Logback
的比较如下图。下图比较中Log4j2有三种模式:1)全局使用异步模式;2)部分Logger采用异
步模式;3)异步Appender 。可以看出在前两种模式下,Log4j2的性能较之Log4j和Logback有很大的
优势。
无垃圾记录
垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,花费大量精力来控制这些暂停。
许多日志库(包括以前版本的Log4j)在稳态日志记录期间分配临时对象,如日志事件对象,字符串,
字符数组,字节数组等。这会对垃圾收集器造成压力并增加GC暂停发生的频率。
从版本2.6开始,默认情况下Log4j
以“无垃圾”模式运行,其中重用对象和缓冲区,并且尽可能不分配临
时对象。还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal
字段。
Log4j 2.6
中的无垃圾日志记录部分通过重用ThreadLocal
字段中的对象来实现,部分通过在将文本转换
为字节
时重用缓冲区来实现。
使用Log4j 2.5:内存分配速率809 MB /秒,141个无效集合。
Log4j 2.6没有分配临时对象:0(零)垃圾回收。
有两个单独的系统属性可用于手动控制Log4j用于避免创建临时对象的机制:
log4j2.enableThreadlocals
- 如果“true”(非Web应用程序的默认值)对象存储在ThreadLocal
字段中并重新使用,否则将为每个日志事件创建新对象。
log4j2.enableDirectEncoders
- 如果将“true”(默认)日志事件转换为文本,则将此文本转换为字节而不创建临时对象。注意: 由于共享缓冲区上的同步,在此模式下多线程应用程序的同步日志记录性能可能更差。如果您的应用程序是多线程的并且日志记录性能很重要,请考虑使用异步记录器。