目录
- 1. log4j2
- 1.1 log4j2介绍
- 1.2 Log4j2入门
- 1.2.1 log4j2(日志门面 + 日志框架)使用
- 1.2.2 slf4j + log4j2使用
- 1.3 Log4j2配置
- 1.4 Log4j2异步日志
- 1.4.1 全局异步AsyncLogger
- 1.4.2 混合异步AsyncLogger
- 1.4.3 AsyncAppender
1. log4j2
1.1 log4j2介绍
Apache Log4j2是Log4j的升级版,参考了logback一些优秀的设计,并且修复了logback的一些问题,而且性能上也有了重大提升,主要有:
- log4j2对Appender提供了一些异常处理机制
- 参考了logback的设计,提供自动刷新参数配置,可以自动重载配置而不用重启应用
- log4j2利用缓冲区和重用对象,几乎不会产生临时对象,从而实现无垃圾机制
1.2 Log4j2入门
虽然Log4j2既是日志门面,也是日志框架。但还是推荐使用流行的日志门面slf4j + 功能强大且性能优越的日志框架log4j2
1.2.1 log4j2(日志门面 + 日志框架)使用
在pom.xml添加依赖
<!-- log4j2日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.18.0</version>
</dependency>
<!-- log4j2日志框架 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.18.0</version>
</dependency>
程序如下:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2Test {
public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
public static void main(String[] args) {
// 默认只打印error及以上级别的日志
LOGGER.fatal("fatal");
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
运行程序,结果如下:
17:51:14.737 [main] FATAL Log4j2Test - fatal
17:51:14.744 [main] ERROR Log4j2Test - error
默认只打印error及以上级别的日志。后面可以通过log4j2.xml日志配置文件进行日志级别和日志输出格式的配置
1.2.2 slf4j + log4j2使用
在pom.xml添加依赖
<!-- log4j2日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.18.0</version>
</dependency>
<!-- log4j2日志框架 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.18.0</version>
</dependency>
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!--log4j2的适配器,为slf4j绑定日志框架 -->
<!-- 依赖org.slf4j:slf4j-api:1.7.25 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.18.0</version>
</dependency>
程序如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Test {
public final static Logger LOGGER = LoggerFactory.getLogger(Log4j2Test.class);
public static void main(String[] args) {
// 默认只打印error级别的日志
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
运行程序,结果如下:
18:07:44.628 [main] ERROR Log4j2Test - error
默认只打印error级别的日志。后面可以通过log4j2.xml日志配置文件进行日志级别和日志输出格式的配置
1.3 Log4j2配置
在classpath,比如resources目录下,新建log4j2.xml日志配置文件。内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- status="warn": 日志框架本身的输出日志级别, 默认是warn。日志框架本身的日志输出到控制台 -->
<!-- monitorInterval="5":自动加载log4j2.xml文件的时间间隔, -->
<Configuration status="warn" monitorInterval="5">
<!-- 定义属性,供其它标签引用。引用方式${name} -->
<properties>
<property name="LOG_HOME">C:/Users/dell/Desktop/java11Test/logs</property>
</properties>
<!-- 日志处理器 -->
<Appenders>
<!-- 控制台输出appender。可选值:SYSTEM_OUT、SYSTEM_ERR -->
<Console name="Console" target="SYSTEM_OUT">
<!--
%d{yyyy-MM-dd HH:mm:ss.SSS}: 日志日期及其格式
%t/%thread: 日志执行的线程
%-5level: 日志级别输出占5个字符,左对其
%c{36}: 日志执行的类的全限定名, 深度为36
%M: 日志执行的方法名
%L: 日志执行的具体行号
%l: 日志执行的位置信息
%m/%msg: 日志的消息
%n: 换行符
-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
</Console>
<!-- 日志文件输出appender -->
<File name="File" fileName="${LOG_HOME}/myFile.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
</File>
<!-- 使用随机读写流的日志文件输出appender,性能高 -->
<RandomAccessFile name="RandomAccessFile" fileName="${LOG_HOME}/myAccessFile.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
</RandomAccessFile>
<!-- 按照一定规则拆分日志文件的appender -->
<!-- filePattern: 拆分后的文件名。拆分规则如下:
以天为单位,拆分成文件夹
一天内按照分钟为单位生成一个日志文件。和policy的TimeBasedTriggeringPolicy对应
日志文件到达指定大小,再按照指定大小进行拆分,文件序号从1开始递增。和policy的SizeBasedTriggeringPolicy对应
-->
<RollingFile name="RollingFile" fileName="${LOG_HOME}/myRollingFile.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myRollingFile-%d{yyyy-MM-dd-HH-mm}-%i.log">
<!-- 日志级别过滤器。高于等于debug级别的(比如info)进行日志记录,低于debug级别的进行拦截 -->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
<!-- 日志消息拆分的规则 -->
<Policies>
<!-- 在系统启动时,进行拆分,生成一个新的日志文件进行新日志消息的记录。原来的日志文件按照日志记录时间(非启动时间)归档成一个新的归档日志文件 -->
<OnStartupTriggeringPolicy />
<!-- 按照文件大小进行拆分 -->
<SizeBasedTriggeringPolicy size="10 MB" />
<!-- 按照时间间隔拆分,规则根据filePattern定义的 -->
<TimeBasedTriggeringPolicy />
</Policies>
<!-- 在同一分钟下,文件的个数限定为30个,超过则进行滚动覆盖(文件序号最大为30) -->
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<Loggers>
<!-- 配置日志级别 -->
<Root level="INFO">
<!-- 指定日志输出appender。不指定File、RandomAccessFile、RollingFile,也会创建空日志文件 -->
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
1.4 Log4j2异步日志
同步日志处理流程图如下:
异步日志处理流程图如下:
log4j2的异步日志带来了很大的性能提升。为了实现异步日志需要在pom.xml添加如下依赖:
<!-- org.apache.logging.log4j:log4j:2.18.0依赖com.lmax:disruptor:3.4.4 -->
<!-- 异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
提供了两种异步日志实现方法,一个是AsyncLogger(对应Logger组件,推荐使用),一个是AsyncAppender(对应Appender组件)
log4j2的全局异步AsyncLogger性能最好,第二个是混合异步AsyncLogger,性能最差的是AsyncAppender(和同步日志相比没有什么性能提升。和logback性能一样)
如果使用异步日志,全局异步AsyncLogger、混合异步AsyncLogger、AsyncAppender,不要同时使用。否则会使用性能较低的一种异步方式
1.4.1 全局异步AsyncLogger
所有appender的所有日志级别的日志都异步的记录,不需要改动log4j2.xml配置文件。只需要在classpath,比如resources目录下,新建log4j2.component.properties配置文件。内容如下:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
1.4.2 混合异步AsyncLogger
可以同时使用同步日志和异步日志,这使得日志的配置方式更加灵活
log4j2.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">C:/Users/dell/Desktop/java11Test/logs</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
</Console>
</Appenders>
<Loggers>
<!-- 自定义异步logger对象, name必须是LoggerFactory.getLogger(Log4j2Test.class)对应 -->
<!-- 如果设置includeLocation=true, 开启日志记录行号信息会急剧降低异步日志的性能,比同步日志还要慢 -->
<!-- 这里设置includeLocation=false,则PatternLayout的%l将不会打印出来 -->
<!-- additivity="false": 不再继承RootLogger对象. 这里建议强制设置成false -->
<AsyncLogger name="Log4j2Test" level="WARN" includeLocation="false" additivity="false">
<AppenderRef ref="Console"/>
</AsyncLogger>
<!-- RootLogger是同步的。除Log4j2Test外,其它名称的logger使用RootLogger进行输出 -->
<Root level="INFO">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
输出如下:
2022-07-10 15:55:23.037 [main] [ERROR] --- error
2022-07-10 15:55:23.039 [main] [WARN ] --- warn
可以看到打印了WARN级别的日志,且没有打印日志执行的位置信息
1.4.3 AsyncAppender
log4j2.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">C:/Users/dell/Desktop/java11Test/logs</property>
</properties>
<Appenders>
<File name="File" fileName="${LOG_HOME}/myFile.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %l --- %msg%n" />
</File>
<!-- AsyncAppender引用appender -->
<Async name="Async">
<AppenderRef ref="File"/>
</Async>
</Appenders>
<Loggers>
<Root level="INFO">
<!-- 直接引用AsyncAppender -->
<AppenderRef ref="Async" />
</Root>
</Loggers>
</Configuration>