官方参考文档:http://logging.apache.org/log4j/2.x/javadoc.html
公司上一个系统(SSM框架),在测试服务器上(硬盘空间较小)运行差不多半个月之后突然访问不了,一查原因是磁盘空间被占满,再仔细一查大文件,原来是日志堆积太多,又没有定期清理造成的。
第一个想法是,在 Linux 系统下写一个开机启动脚本,该脚本负责定时清理特定目录下的日志文件。网上搜了一下,命令还有点复杂(主要是我太菜了),若是认真研究个两三天倒是也能搞定,时间宝贵,能不能从 log4j 本身入手呢?
============正文开始=============
log4j 本身没有定时清理日志功能,但可以通过自定义 Appender 实现; log4j2 自带定时清理日志功能,且 log4j2 还有其他高级功能,各方面性能均优于 log4j,那还犹豫个JB毛,拉上 log4j2 就开干啊!
【关键点: POM.xml 和 web.xml 的修改】
目录
第一部分:POM.xml 和 web.xml 的改造
参考:https://www.cnblogs.com/new-life/p/9246246.html
https://www.cnblogs.com/yoyotl/p/4920073.html
https://stackoverflow.com/questions/36683852/log4j-error-noclassdeffounderror-org-apache-log4j-xml-domconfigurator
1、POM 依赖 改造
(1)、删除之前的 slf4j-log4j 依赖
<!-- slf4j-log4j 配置--弃用 改用下面的 slf4j-log4j2 -->
<!--
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.9</version>
</dependency>
-->
(2)、添加 slf4j-log4j2 依赖
<!-- slf4j-log4j2 配置 -->
<!-- 首先引入slf4j的核心包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 然后引入slf4j对应log4j2的中间件 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 然后引入log4j2包 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 最后引入另外两个相关的包 -->
<!--用于解决web环境下关闭服务器时可能出现的log4j线程无法及时关闭的warn,web工程需要包含log4j-web,非web工程不需要 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<scope>runtime</scope>
<version>${log4j.version}</version>
</dependency>
<!--使用log4j2的AsyncLogger时需要包含disruptor -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<scope>runtime</scope>
<version>3.4.2</version>
</dependency>
<!-- 最后只需要在项目的resources目录下新建一个log4j2.xml作为log4j2的配置文件即可在项目中使用slf4j输出日志 -->
2、web.xml 改造 【踩坑】
(1)、Servlet 版本升级
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
看到了吧,将【2_5】改为【3_0】,将【2.5】改为【3.0】。
(2)、删除 Log4jConfigListener
这个坑搞了我一个上午,项目启动时老报错:Log4j error: NoClassDefFoundError: org/apache/log4j/xml/DOMConfigurator
直到搜到 stackOverflow上的答案
(3)、设置配置文件位置
- 日志配置文件默认读取路径为src/main/resource
- 若文件放置在默认读取路径下则无需配置读取路径
- 若文件放置到其他路径下则需要在web.xml中设置日志配置文件的读取路径,代码如下:
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>classpath:log4j2.xml</param-value>
</context-param>
关于 log4j2 的配置文件有一点需要注意:很多博客上说,log4j 2.x 版本不再支持像1.x中的.properties后缀的文件配置方式,这是错误的,可能是作者当时写博客的时候还不支持。从 log4j2.4 版本之后就支持.properties 配置方式了。(本文选择了XML配置方式)
详见官方文档: http://logging.apache.org/log4j/2.x/manual/configuration.html
第二部分:log4j2.xml 的配置
此部分参考文章:
Log4j2的onMatch和onMismatch属性值详解
log4j2 RollingFile用法 : 日志按天分,按小时分,按分钟分,按秒分;按大小分;自动(定时)清理
https://blog.csdn.net/cxy1991xm/article/details/90549806
https://blog.csdn.net/zhang168/article/details/46814489
https://www.jianshu.com/p/c6d9b418e433
配置的细节太多,都在参考文章里,不再赘述。这里记录一个关键点:onMatch 和 onMismatch 属性
(1)、onMatch 和 onMismatch 属性
<!--日志优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
onMatch="ACCEPT" 表示匹配该级别及以上
onMatch="DENY" 表示不匹配该级别及以上
onMatch="NEUTRAL" 表示该级别及以上的,由下一个filter处理,如果当前是最后一个,则表示匹配该级别及以上
-- onMatch 相当于数学上的的 >= (包含自身)
onMismatch="ACCEPT" 表示匹配该级别以下
onMismatch="NEUTRAL" 表示该级别以下的,由下一个filter处理,如果当前是最后一个,则不匹配该级别以下的
onMismatch="DENY" 表示不匹配该级别以下的
-- onMismatch 相当于数学上的的 <
(2)、不同级别日志输出到不同日志文件
debug 输出到 debug.log , info 输出到 info.log , >= WARN 输出到 error.log ,
<File name="app_debug" fileName="${log-path}/app/debug.log" append="false">
<Filters>
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36}.%M()/%L - %msg%xEx%n"/>
</File>
<File name="app_info" fileName="${log-path}/app/info.log" append="false">
<Filters>
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36}.%M()/%L - %msg%xEx%n"/>
</File>
<File name="app_error" fileName="${log-path}/app/error.log" append="false">
<Filters>
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36}.%M()/%L - %msg%xEx%n"/>
</File>
level总结:Appender 和 Logger 两个 地方都可以限制 level。
- Logger 这里只有 >= ,
- Appender 那里有 >= 和 < ,且可链式过滤
(3)、定时清理日志 配置 Demo
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="DEBUG" monitorInterval="30">
<Properties>
<Property name="log-path">${sys:catalina.home}/logs</Property>
</Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} -%-4r [%t] %-5p %x - %m%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="${log-path}/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
</File>
<!-- 按秒记录日志,日志保留10秒 -->
<!-- 坑:1、fileName,filePattern,Delete basePath 都要写成绝对路径 -->
<!-- 坑:2、maxDepth 在 basePath 之下有几级目录就写几 -->
<!-- 坑:3、IfFileName 不需要斜线 -->
<RollingFile name="A" fileName="${log-path}/test_log/test_" filePattern="${log-path}/test_log/%d{yyyy-MM-dd-HH-mm-ss}.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-5p] %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
</Policies>
<DefaultRolloverStrategy max="20">
<Delete basePath="${log-path}/test_log" maxDepth="1">
<IfFileName glob="*.log" />
<IfLastModified age="10s" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</appenders>
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<logger name="java.sql" level="INFO"></logger>
<root level="DEBUG">
<appender-ref ref="Console"/>
<appender-ref ref="log"/>
<appender-ref ref="A"/>
</root>
</loggers>
</configuration>