log4j2配置文件解读
log4j2是对log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升
- 异常处理 : 在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制
- 性能提升 : log4j2相较于log4j和logback都具有很明显的性能提升
- 自动重载配置 : 参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用
- 无垃圾机制 : log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvm gc
1 log4j2日志门面+日志实现
<!-- Log4j2 门面API-->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.1</version>
</dependency>
<!-- Log4j2 日志实现 -->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
public class Log4j2Test {
// 定义日志记录器对象
// org.apache.logging.log4j.LogManager
// org.apache.logging.log4j.Logger
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");
}
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36} %L %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
2 slf4j+log4j2
目前市面上最主流的日志门面就是SLF4J,虽然Log4j2也是日志门面,因为它的日志实现功能非常强大,性能优越;所以大家一般还是将Log4j2看作是日志的实现,Slf4j + Log4j2应该是未来的大势所趋
<!--使用slf4j作为日志门面-->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!--使用log4j2的适配器进行绑定-->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.1</version>
</dependency>
<!-- log4j2日志实现 -->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
@Slf4j
public class Log4j2Test {
@Test
public void testQuick()throws Exception{
log.error("error");
log.warn("warn");
log.info("inf");
log.debug("debug");
log.trace("trace");
}
3 Log4j2配置
log4j2默认加载classpath下的 log4j2.xml 文件中的配置
控制台打印
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" monitorInterval="5">
<Appenders>
<!--控制台输出appender-->
<Console name="console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36} %L %m%n" />
</Console>
</Appenders>
<Loggers>
<!--使用rootLogger配置日志级别level="trace"-->
<Root level="trace">
<!--指定日志使用的处理器-->
<AppenderRef ref="console" />
</Root>
</Loggers>
</Configuration>
输出到文件中
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" monitorInterval="5">
<properties>
<property name="LOG_HOME">F:/logs</property>
</properties>
<Appenders>
<!--日志文件输出appender-->
<File name="file" fileName="${LOG_HOME}/log4j2.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l - %m%n"/>
</File>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="file"/>
</Root>
</Loggers>
</Configuration>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<properties>
<property name="LOG_HOME">F:/logs</property>
</properties>
<Appenders>
<!--使用随机读写流的日志文件输出appender提高性能-->
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/log4j2.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l - %m%n" />
</RandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="accessFile" />
</Root>
</Loggers>
</Configuration>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<properties>
<property name="LOG_HOME">/logs</property>
<property name="pattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n"/>
</properties>
<Appenders>
<!--按照一定规则拆分的日志文件的 appender-->
<RollingFile name="rollingFile" fileName="${LOG_HOME}/vx_video.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/vx_video-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!--日志级别过滤器-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
<!--日志消息格式-->
<PatternLayout pattern="${pattern}" />
<Policies>
<!--在系统启动时,触发拆分规则,产生一个新的日志文件-->
<OnStartupTriggeringPolicy />
<!--按照文件大小拆分(10MB) -->
<SizeBasedTriggeringPolicy size="10 MB" />
<!--按照时间节点拆分,规则根据filePattern定义的-->
<TimeBasedTriggeringPolicy />
</Policies>
<!--在同一个目录下,文件的个数限定为30个,超过进行覆盖-->
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="rollingFile"/>
</Root>
</Loggers>
</Configuration>
4 Log4j2异步日志
Log4j2最牛的地方在于异步输出日志时的性能表现;下图比较中Log4j2有三种模式:全局使用异步模式、部分Logger采用异步模式、异步Appender;可以看出在前两种模式下,Log4j2的性能较之Log4j和Logback有很大的优势
https://logging.apache.org/log4j/2.x/performance.html
Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender(了解即可),一个是通过AsyncLogger,分别对应Appender组件和Logger组件
注意:配置异步日志需要添加依赖
<!--使用slf4j作为日志门面-->
<!--使用log4j2的适配器进行绑定-->
<!-- log4j2日志实现 -->
<!--异步日志依赖-->
<!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
<!-- lombok -->
4.1 AsyncAppender方式(了解)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<properties>
<property name="LOG_HOME">F:/logs</property>
</properties>
<Appenders>
<File name="file" fileName="${LOG_HOME}/log4j2.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
<!---->
<Async name="fileAsync">
<AppenderRef ref="file"/>
</Async>
</Appenders>
<Loggers>
<Root level="info">
<!--使用异步appender-->
<AppenderRef ref="fileAsync"/>
</Root>
</Loggers>
</Configuration>
4.2 AsyncLogger方式(重点)
AsyncLogger才是log4j2 的重头戏,也是官方推荐的异步方式;它可以使得调用Logger.log返回的更快;你可以有两种选择:全局异步和混合异步
全局异步就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个log4j2.component.properties 配置
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
<properties>
<property name="LOG_HOME">F:/logs</property>
</properties>
<Appenders>
<Console name="console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36} %L %m%n" />
</Console>
<File name="file" fileName="${LOG_HOME}/log4j2.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="console"/>
<AppenderRef ref="file"/>
</Root>
</Loggers>
</Configuration>
2020-04-19 09:26:41,906 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@18b4aac2,component=AsyncLoggerRingBuffer
混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<properties>
<property name="LOG_HOME">F:/logs</property>
<!--注意行号-->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36} %L %m%n"/>
</properties>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="${pattern}"/>
</Console>
<File name="file" fileName="${LOG_HOME}/log4j2.log">
<PatternLayout>
<Pattern>${pattern}</Pattern>
</PatternLayout>
</File>
</Appenders>
<Loggers>
<!--自定义异步logger对象
includeLocation="false" : 关闭日志记录的行号信息
additivity="false" : 不在继承rootlogger对象
-->
<AsyncLogger name="com.nobug" level="trace" includeLocation="false" additivity="false">
<AppenderRef ref="file"/>
<!--<AppenderRef ref="console"/>-->
</AsyncLogger>
<Root level="trace" includeLocation="true">
</Root>
</Loggers>
</Configuration>
使用异步日志需要注意的问题:
- 如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现;性能会和 AsyncAppender一致,降至最低
- 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢
5 Log4j2的性能
无垃圾记录
https://logging.apache.org/log4j/2.x/manual/garbagefree.html
垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,花费大量精力来控制这些暂停。
许多日志库(包括以前版本的Log4j)在稳态日志记录期间分配临时对象,如日志事件对象,字符串, 字符数组,字节数组等。这会对垃圾收集器造成压力并增加GC暂停发生的频率。
从版本2.6开始,默认情况下Log4j以“无垃圾”模式运行,其中重用对象和缓冲区,并且尽可能不分配临 时对象。还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal字段。
Log4j 2.6中的无垃圾日志记录部分通过重用ThreadLocal字段中的对象来实现,部分通过在将文本转换 为字节时重用缓冲区来实现。