最近研究了一下Log4j这个被广发使用的日志组件,缘于三个问题:日志水平调整问题,日志运行时配置修改和日志模块化问题。Log4j不是一天两天的了,相信早就会有相应的解决方法。
先来看第一个问题。生产运行中考虑到日常资源消耗和运行性能是不宜开启低水平日志记录的。然而,一旦生产运行环境出现异常,没有相当详细程度的日志信息的话,要进行快速的定位分析准确的排查判断几乎是不可能的。这个矛盾令人十分纠结,是应该提高日志水平减少不必要的日志输出消耗呢,降低日志水平还是管它有理无理尽量详细记录呢。我的想法是两者都要兼顾,如果要做一个折衷的话,日志组件能够根据运行的记录水平,对日志内容进行细粒度输出控制。
一种解决思路是自定义PatternLayout能够根据当前的日志记录水平来进行不同格式的日志输出。可以在Log4j配置中划定高水平和低水平的日志界限(Threshold),低水平记录(对应于警告及其以下)和高水平记录(对应于错误及其以上)分别采用详细的日志格式和粗略的日志格式。我的扩展实现如下, 像所在文件和所在行数等信息,要获取这些信息需要通过运行时反射得到,对日志性能影响甚大,因而,凡是警告及其以下低级别的日志不予输出,而对于错误及其以上高级别的日志则记录。
public class LogLevelThresholdPatternLayout extends Layout {
private Level threshold = Level.WARN;
private static final String DEFAULT_LOW_LEVEL_PATTERN = "%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %m%n";
private static final String DEFAULT_HIGH_LEVEL_PATTERN = "%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %t %l %m%n";
private PatternLayout lowLevelPatternLayout;
private PatternLayout highLevelPatternLayout;
public void setThreshold(Level threshold) {
this.threshold = threshold;
}
public void setLowLevelConversionPattern(String lowLevelConversionPattern){
lowLevelPatternLayout = new PatternLayout(lowLevelConversionPattern);
}
public void setHighLevelConversionPattern(String highLevelConversionPattern){
highLevelPatternLayout = new PatternLayout(highLevelConversionPattern);
}
@Override
public void activateOptions() {
if (lowLevelPatternLayout == null) {
lowLevelPatternLayout = new PatternLayout(DEFAULT_LOW_LEVEL_PATTERN);
}
lowLevelPatternLayout.activateOptions();
if (highLevelPatternLayout == null) {
highLevelPatternLayout = new PatternLayout(DEFAULT_HIGH_LEVEL_PATTERN);
}
highLevelPatternLayout.activateOptions();
}
@Override
public String format(LoggingEvent event) {
if (threshold.isGreaterOrEqual(event.getLevel())) {
return lowLevelPatternLayout.format(event);
} else {
return highLevelPatternLayout.format(event);
}
}
@Override
public boolean ignoresThrowable() {
return true;
}
}
这时候 Log4j的XML配置就像如下便可:
<appender name="RollingLogFile" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="test.log" />
<layout class="xxx.yyy.zzz.LogLevelThresholdPatternLayout">
<param name="highLevelConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %t %l %m%n" />
<param name="lowLevelConversionPattern" value="%d{HH:mm:ss} [%-5p] %m%n" />
</layout>
</appender>
在查看Log4j的Javadoc进行配置的时候发现了另外一个问题。Log4j在版本1.2.16引入了一个新的类org.apache.log4j.EnhancedPatternLayout来替代原有使用的org.apache.log4j.PatternLayout。如果条件允许,特别是在新的部署环境应该优先使用。于是,上面的配置因此可以改成如下这样:
public class LogLevelThresholdPatternLayout extends Layout {
private Level threshold = Level.WARN;
private EnhancedPatternLayout lowLevelPatternLayout;
private EnhancedPatternLayout highLevelPatternLayout;
public void setThreshold(Level threshold) {
this.threshold = threshold;
}
public void setLowLevelConversionPattern(String lowLevelConversionPattern){
lowLevelPatternLayout = new EnhancedPatternLayout(lowLevelConversionPattern);
}
public void setHighLevelConversionPattern(String highLevelConversionPattern){
highLevelPatternLayout = new EnhancedPatternLayout(highLevelConversionPattern);
}
...
...
}
除此之外,旧版本的的org.apache.log4j.DailyRollingFileAppender同org.apache.log4j.PatternLayout一样,应该优先使用org.apache.log4j.rolling.RollingFileAppender作为替代,这个可以从Apache Logging另外一个子项目apache-log4j-extra找到的。于是去下载log4j-extra来玩一下,刚好发现log4j-extra最近有了更新。apache-log4j-extra基于Log4j1.2版本,其大部分源码源于已经停止继续维护的Log4j-1.3版本。同原有的org.apache.log4j.DailyRollingFileAppender相比,配置上的主要区别是专门分出出一个的org.apache.log4j.rolling.TimeBasedRollingPolicy的类来对文件滚动细节进行具体控制。配置如下:
<appender name="RollingLogFile" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="ActiveFileName" value="test.log" />
<param name="FileNamePattern" value="test.log.%d{yyyy-MM-dd}" />
</rollingPolicy>
...
...
</appender>
回到最初的日志水平调整问题,另外一种解决思路可以通过配置Logger的多个Appender,每个Appender再配置相应的Layout来实现。为了避免多个Appender重复记录,还需要配置对应的Filter和Threshold来过滤相应的日志水平。这种方式会导致配置有点繁琐,但好处是仅通过对Log4j的进行适当配置便可,无需额外扩展代码实现。具体下面的配置如下。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="HIGHLOG" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="test.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="INFO" />
</filter>
</appender>
<appender name="LOWLOG" class="org.apache.log4j.DailyRollingFileAppender">
<param name="Threshold" value="WARN"/>
<param name="File" value="test.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %t %l %m%n" />
</layout>
</appender>
<logger name="xxx.yyy.zzz">
<appender-ref ref="HIGHLOG" />
<appender-ref ref="LOWLOG" />
</logger>
</log4j:configuration>
像上面的配置经过简单试验,是可以达到同上一种方式PatternLayout自定义扩展一样的效果的。然而,这样配置存在一个隐患,在文件滚动的时候多个FileAppender包装下对同一个文件的,能否做到滚动在Appender之间做到同步,能否不会因I/O异常而出现死锁等等是很值得怀疑的,特别是文件发生滚动时又发生大量日志输出的时候,会不会导致整个系统长时间停顿等等。具体的触发机制很难深究,我又没做过实际试验研究不好妄下结论。默认设置下的每日滚动,是发生在每日凌晨这个时间点。这时候一般是业务空闲时期,相信实际中也很难触发文件同步或者数据丢失问题。为了保险起见应该使用由log4j-extra提供的org.apache.log4j.rolling.RollingFileAppender。按照Javadoc的说法,应该是修复了先前的DailyRollingFileAppender表现出的数据同步和丢失问题。
相关推荐
演示了java记录通过log4j2记录日志到mysql数据库中
Log4j日志配置说明,Log4j日志配置说明Log4j日志配置说明,Log4j日志配置说明
Log4j日志等级,具体说明了log4j中的日志记录等级,对于自己写日志有很大帮助。
详细介绍了log4j的使用方法,介绍了java中日志记录如何写入数据库,对于初学者来说很不错的一个文档
logging-log4j日志记录 example 用于项目调试工作。。。
tomcat6.0 配log4j日志的必须文件及配置过程 tomcat6-------lib | |--------tomcat-juli-adapters.jar | |--------log4j.jar | |--------log4j.properites | |----bin |--------tomcat-juli.jar 最后把log4...
log4j.jar log4j日志记录包
Log4j记录日志
log4j日志管理log4j日志管理log4j日志管理log4j日志管理log4j日志管理
logstash采集log4j日志发送到es配置文件,可以把日志根据日志级别区分开,一个级别一条日志是es中的一条数据
log4j日志记录
log4j.rootLogger=debug,CONSOLE,testfile,A1,MAIL ################### # Console Appender ################### log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.Target=...
log4j日志记录工具,在java里记录日志信息,一个很好的工具!
Log4j的包下载完成后,解压,将其中打包好的的log4j-1.x.x.jar导入你的工程LIB中。 Log4j之所以受欢迎的原因之一是它的灵活性。Log4j提供了灵活的配置方法,默认是调用BasicConfigurator.configure()来进行配置,但...
tomcat下的log4j日志配置,给tomcat配置log4j有好几种方法,我知道的有: 一、tomcat级别的统一日志管理 二、每个webapp分别配置log4j
Log4j比较全面的配置 log4j.rootLogger=DEBUG,CONSOLE,A1,im log4j.addivity.org.apache=true # 应用于控制台 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.Threshold=DEBUG log4j....
每天生成一个log4j日志文件,如果只需要将最近一段时间内的日志文件保留,以前或更早的文件不用保留。例如只保留最近一周的日志,日志文件保留3天等等这些。。。通过这个jar包就可以实现。 log4j.properties文件在...
log4j按功能保存日志
本包适用于tomcat8及以下版本 附带log4j.properties 和 配置说明
示范如何在android工程中使用log4j记录日志