一、log4j2简介
Apache Log4j 2是对Log4j的升级,是最优秀的java日志框架。
二、log4j2特征
性能提升:Log4j2包含基于LMAX Disruptor库的下一代异步记录器。在多线程场景中,异步记录器的吞吐量比log4j和logback高18倍,延迟低。
自动重新加载配置:与logback一样,log4j2可以在修改时自动重新加载其配置。与logback不同,它会在重新配置时不会丢失日志事件。
高级过滤:与logback一样,log4j2支持基于log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。此外,过滤器还可以与记录器关联。与logback不同,logback可以在任何这些情况下使用通用的Filter类。
插件架构:log4j2使用插件模式配置组件。因此,无需编写代码来创建和配置Appender,layout,pattern Converter等。在配置了的情况下,log4j2自动识别插件并使用它们。
无垃圾机制:在稳定日志记录期间,log4j2在独立应用程序中是无垃圾的,在web应用程序中是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应性能。
总之:SLF4j+Log4j2的组合,是市场上最强大的日志功能实现方式。
三、slf4j集成log4j2
1. slf4j集成log4j2步骤共4步
- 导入slf4j日志门面
- 导入log4j2的适配器
- 导入log4j2日志门面
- 导入log4j2日志实现
<!--slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!--log4j2适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
<!--log4j2日志实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
2. 集成原理
slf4j门面调用的是log4j2的门面,再由log4j2的门面调用log4j2的实现。
四、log4j2配置文件
log4j2是参考logback创作出来的,所以配置文件也是使用xml。log4j2同样是默认加载类路径(resources)下的log4j2.xml文件中的配置。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--配置appender -->
<Appenders>
<Console name="consoleAppender" target="SYSTEM_ERR"></Console>
</Appenders>
<!--配置logger -->
<Loggers>
<!--配置rootlogger -->
<Root level="info">
<AppenderRef ref="consoleAppender"></AppenderRef>
</Root>
</Loggers>
</configuration>
五、日志文件输出
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--全局配置 -->
<properties>
<property name="logDir">F:\\project\\springboot_log</property>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"></property>
</properties>
<!--配置appender -->
<Appenders>
<!--控制台输出 -->
<Console name="consoleAppender" target="SYSTEM_ERR"></Console>
<!--配置文件输出 -->
<File name="fileAppender" fileName="${logDir}\\log4j2.log">
<!--配置文件输出格式 -->
<PatternLayout pattern="${pattern}"></PatternLayout>
</File>
</Appenders>
<!--配置logger -->
<Loggers>
<!--配置rootlogger -->
<Root level="info">
<AppenderRef ref="consoleAppender"></AppenderRef>
<AppenderRef ref="fileAppender"></AppenderRef>
</Root>
</Loggers>
</configuration>
六、日志文件的拆分和压缩
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--全局配置 -->
<properties>
<property name="logDir">F:\\project\\springboot_log</property>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"></property>
</properties>
<!--配置appender -->
<Appenders>
<!--按照指定规则来拆分日志文件
fileName:日志文件名字
filePattern:日志文件拆分后文件的明明规则
$${date:yyyy-MM-dd}:根据日期当天,创建一个文件夹
rolllog-%d{yyyy-MM-dd-HH-mm}-%i.log:为文件命名的规则:%i表示序号,从0开始,目的是为了让每一份文件名字不会重复
-->
<RollingFile name="rollingFile" fileName="${logDir}\\rolllog.log"
filePattern="${logDir}/$${date:yyyy-MM-dd}/rolllog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!-- 日志消息格式-->
<PatternLayout pattern="${pattern}"></PatternLayout>
<Policies>
<!--在系统启动时,触发拆分规则,产生一个日志文件-->
<OnStartupTriggeringPolicy></OnStartupTriggeringPolicy>
<!--按照文件的大小进行拆分 -->
<SizeBasedTriggeringPolicy size="1KB"></SizeBasedTriggeringPolicy>
<!--按照时间节点进行拆分,拆分规则就是 filePattern-->
<TimeBasedTriggeringPolicy></TimeBasedTriggeringPolicy>
</Policies>
<!--在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则 -->
<DefaultRolloverStrategy max="30"></DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<!--配置logger -->
<Loggers>
<!--配置rootlogger -->
<Root level="info">
<AppenderRef ref="rollingFile"></AppenderRef>
</Root>
</Loggers>
</configuration>
七、异步日志
log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应前面的Appender组件和Logger组件。
1. AsyncAppender方式
AsyncAppender方式是通过引用别的Appender来实现,当有日志事件到达时,会开启另外一个线程来处理它们。需要注意的是,如果在Appender的时候出现异常,对应用来说是无法感知的。
AysncAppender应该在它引用的Appender之后配置,默认使用java.util.concurrent.ArrayBlockingQueue实现而不需要其它外部的类库。当使用此Appender的时候,在多线程的环境下需要注意,阻塞队列容易受到锁争用的影响,这可能会对性能产生影响。这时候,我们应该考虑使用无锁的异步记录器(AsyncLogger).
2. AsyncLogger方式(最推荐混合异步)
AsyncLogger才是log4j2实现异步最重要的功能体现,也是官方推荐的异步方式。
它可以使得调用Logger.log返回的更快。可以有两种选择:全局异步和混合异步。
全局异步:所有的日志都异步的记录,在配置文件上不用做任何改动,只需要在jvm启动的时候增加一个参数即可实现。
混合异步:可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。虽然Log4j2提供一套异常处理机制,可以覆盖大部分状态,但是还是有一小部分的特殊情况是无法完全处理的,比如我们如果是记录审计日志(特殊情况之一),那么官方就推荐使用同步日志的方式,二对于其他的一些仅仅是记录一个程序日志的地方,使用异步日志将大幅提升性能,减少应用本身的影响。
混合异步的方式需要通过修改配置文件来实现,使用AsyncLogger标记配置
八、AsyncAppender方式异步日志
1. 实现步骤
1. 添加异步日志的依赖
<!--异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.3</version>
</dependency>
2. 在Appender标签中,对于异步进行配置,使用Async标签
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--配置appender -->
<Appenders>
<!--控制台输出 -->
<Console name="consoleAppender" target="SYSTEM_OUT"></Console>
<!-- 配置异步-->
<Async name="myAsync">
<!-- 将控制台输出异步的操作-->
<AppenderRef ref="consoleAppender"></AppenderRef>
</Async>
</Appenders>
<!--配置logger -->
<Loggers>
<!--配置rootlogger -->
<Root level="info">
<AppenderRef ref="myAsync"></AppenderRef>
</Root>
</Loggers>
</configuration>
3. rootlogger引用Async,如上图
九、AsyncLogger-全局异步日志
全局异步:所有的日志都是异步的日志记录,在配置文件上不用做任何的改动。只需要在类路径resources下添加一个properties属性文件,做异步配置即可。
文件要求是:文件名必须为log4j2.component.properties
文件内容为键值对:Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
十、AsyncLogger-混合异步日志(推荐使用)
混合异步:可以在应用中同时使用同步日志和异步日志,这使得日志的配置和输出会更加的灵活。
需求:假设我们有自定义的logger -- com.atredhorse,让自定义的Logger是异步的。让rootLogger是同步的,也即其他包下是同步的。
<!--配置logger -->
<Loggers>
<!--自定义logger,让自定义的logger为异步logger-->
<!-- includeLocation="false" 表示取出日志记录中的行号信息,这个行号信息非常的影响日志记录的效率,
生产中都不加这个行号
additivity="false":表示不继承rootlogger-->
<AsyncLogger name="com.atredhorse" level="debug"
includeLocation="false" additivity="false">
<AppenderRef ref="consoleAppender"></AppenderRef>
</AsyncLogger>
<!--配置rootlogger -->
<Root level="info">
<AppenderRef ref="consoleAppender"></AppenderRef>
</Root>
</Loggers>
总之:如果使用异步日志,AsyncAppender、AsyncLogger不要同时出现,没有这个需求,效果也不会叠加。如果同时出现,那么效率会以AsyncAppender为主。
AsyncLogger的全局异步和混合异步也不要同时出现,没有这个需求,效果也不会叠加。