说起Java中的日志组件,jcl(Jakarta Commons Logging)、jul(Java Util Logging)、log4j、slf4j、logback、log4j2这些耳熟能详组件。不得不提到一个人,那就是Ceki Gülcü这位大神,是log4j、slf4j、logback的作者。他冷酷无情的几乎包揽了整个Java的日志组件,努力改进日志记录器组件,连sun官方的日志组件方案jul也不得不甘拜下风。有兴趣可以看看《Java日志系统历史从入门到崩溃》一文。
好了,我感觉log4j2诞生是要终结和一统这一切,其实slf4j已经做到了。那么选择log4j2的理由是什么?那只能因为性能改进。在多线程方案中,与Log4j 1.x和Logback相比,异步Logger的吞吐量高18倍,延迟降低了几个数量级。请参见Log4j2异步日志记录性能。
好了,在了解一点点背景和目标的后,我们本着“用就是了”的原则直奔主题。
注意,这里是在spring boot 框架下使用log4j2组件的常用配置。
引入log4j2依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
排除log4j 1.x
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.2.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
注意log4j、slf4j、jcl的桥接器
因为很多其他组件绑定了各种的日志组件,所以配置桥接器是必要的,把不同日志统一接入log4j2的实现上来。
这个spring-boot-starter-log4j2已经配置好了,可以去pom里看一下:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jul</artifactId>
<version>2.12.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.29</version>
<scope>compile</scope>
</dependency>
配置文件
配置文件的名称叫log4j2.xml
或者是log4j2-spring.xml
都可以,一般放置在resources下。
如果使用其他位置或名称,在application.yml中指定一下即可:
logging:
config: classpath:log4j2.xml
典型配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<Configuration monitorInterval="60">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--变量配置-->
<properties>
<!-- 定义日志存储的默认路径,可用-Dlog.path参数覆盖 -->
<property name="FILE_PATH" value="${sys:log.path:-/path/logs}"/>
<property name="FILE_NAME" value="app_name"/>
<!--输出日志的格式-->
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<property name="LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
</properties>
<Appenders>
<!-- 控制台日志,一般本地调试时开启 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!--文件日志,会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="Filelog" fileName="${FILE_PATH}/app_name.log" append="false">
<PatternLayout pattern="${LOG_PATTERN}"/>
</File>
<!-- 滚动日志,切分的日志如:${FILE_PATH}/info/2020-03-28/app_name-info-2020-03-28-23_1.log.gz -->
<!-- 主日志文件位于:${FILE_PATH}/app_name-info.log -->
<!-- 这个会打印出info及高于其级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<!-- 文件后缀配置为gz开启gzip压缩,配置为zip开启zip压缩,log后缀不压缩 -->
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
filePattern="${FILE_PATH}/info/%d{yyyy-MM-dd}/${FILE_NAME}-info-%d{yyyy-MM-dd-HH}_%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!-- interval属性用来指定滚动时间间隔 -->
<!-- 间隔单位由filePattern 中的时间模板最小单位决定,目前是1小时(HH) -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- size属性用来文件达到多大触发切分 -->
<SizeBasedTriggeringPolicy size="1000MB"/>
</Policies>
<!-- DefaultRolloverStrategy 同一文件夹下多少个文件开始滚动覆盖,默认为7个 -->
<!-- 对应于filePattern中的%i -->
<DefaultRolloverStrategy max="100"/>
</RollingFile>
<!-- 滚动日志,切分的日志如:${FILE_PATH}/error/2020-03-28/app_name-error-2020-03-28_1.log.gz -->
<!-- 主日志文件位于:${FILE_PATH}/app_name-error.log -->
<!-- 这个会打印出所有的error及高于其级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log"
filePattern="${FILE_PATH}/error/%d{yyyy-MM-dd}/${FILE_NAME}-error-%d{yyyy-MM-dd}_%i.log.gz">
<!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!-- 间隔单位由filePattern 中的时间模板最小单位决定,目前是1天(dd) -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="1000MB"/>
</Policies>
<DefaultRolloverStrategy max="100"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<!-- 节点可以PascalCase也可以是kebab-case,这里是演示,实际用时最好一致 -->
<appender-ref ref="Console"/>
<AppenderRef ref="RollingFileInfo"/>
<AppenderRef ref="RollingFileError"/>
</Root>
<!-- 节点大小写都可以识别,这里是演示,实际用时最好一致 -->
<!-- 开启mybatis包的DEBUG级别输出 -->
<logger name="org.mybatis" level="debug" />
<!-- 关闭io.micrometer.elastic的所有日志输出 -->
<Logger name="io.micrometer.elastic" level="OFF"/>
<!-- 开启RocketmqClient的WARN级别输出到单独的文件 -->
<!-- additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出 -->
<Logger name="RocketmqClient" level="WARN" additivity="false">
<AppenderRef ref="Filelog"/>
</Logger>
</Loggers>
</Configuration>
这里是收集整理了下常用配置,至于异步Appender以及各种特性未做收纳,可以研读下官方文档,非常详细。