今天具体说说log4j的控制日志输入和涉及的两个概念:level和filter。
首先来看level和filter的定义,接着看它们是如何在配置文件中使用的。
level:
level就是记log时的severity级别。在之前的blog中已经做了介绍。现在看一个自定义的gmiLevel class的例子:
public class GMILevel extends Level {
//用于自定义level时用。toLevel可直接将这个int转换成对应的level。
public static final int GMI_UNKNOWN_INT = 1000;
//自定义level。
public static final GMILevel GMI_UNKNOWN = new GMILevel(GMI_UNKNOWN_INT,
GMIMessageConstants.Severity.UNKNOWN.toString(), 7);
protected GMILevel(final int level, final String levelStr, final int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
//Level(int level, String levelStr, int syslogEquivalent)
注意通过level的构造函数我们//可以知道每个level有自己的level int和syslogEquivalent(1,2,3等等)。
}
public static boolean isGMILevel(final int i) {
switch (i) {
case GMI_UNKNOWN_INT:
return true;
(这里还有很多其它的GMI_HARMLESS,GMI_CRITICAL等。这个方法用于在别的class中判断是否是gmiLevel)
default:
return false;
}
}
//这个是实现的level的方法。将一个传入的string转换成上面定义好的某种gmiLevel。
public static Level toLevel(final String sArg) {
if (sArg == null) {
return GMI_UNKNOWN;
}
if (sArg.equalsIgnoreCase(GMIMessageConstants.Severity.UNKNOWN.toString())) {
return GMI_UNKNOWN;
} else if (sArg.equalsIgnoreCase(GMIMessageConstants.Severity.INFORMATION.toString())) {
return GMI_INFORMATION;
}
return Level.toLevel(sArg, GMI_UNKNOWN);
}
}
下面可以看到上面定义的level是怎么在filter中使用的。
Filter:
Users should extend this class to implement customized logging event filtering. Note that Category
and AppenderSkeleton
, the parent class of all standard appenders, have built-in filtering rules. It is suggested that you first use and understand the built-in rules before rushing to write your own custom filters.
This abstract class assumes and also imposes that filters be organized in a linear chain.
也就是说,所有的filters组成了一个chain。每个filter都有一个decide方法,返回一个int,表示DENY
, NEUTRAL
or ACCEPT
, 来决定是否再进行下一个filter的判断。
//下面的代码仅对是否是gmilevel进行了控制,其实还可以控制的更细。比如如果是不重要的GMI_HARMLESS,
//则被filter out。
public int decide(final LoggingEvent loggingEvent) {
final int level = loggingEvent.getLevel().toInt();
if (gmi && !isGMILevel(level)) {
return -1;
} else if (!gmi && isGMILevel(level)) {
return -1;
} else {
return 1;
}
}
于是,我们就实现了通过自定义的level和filter来共同控制log4j是否记gmi log。
所以gmi log输出有三级控制:
1,使用在配置文件中定义要输出的所有类型的gmi message。同时在程序代码中调用gmi.log(message)方法。当调用语句中的message和配置文件中的message相吻合时,就准备输出gmi log.
2,而真正输出gmi log的是log4j。所以在log4j还可以实现一级控制:
<logger name="com.reuters.controller">
<level value="GMI_INFORMATION"
class="com.reuters.controller.common.logging.gmi.GMILevel"/>
<appender-ref ref="localgmilogfile_arch"/>
</logger>
这是log4j-config.xml中的一段内容。
A log request of level lR on a logger with effective level lE, passes the logger-
level filter if and only if lR ≥ lE. The request is disabled (and dropped)
otherwise.
这段的意思是:如果一个要求记日志的请求的level是lR,而一个logger的level是lE。当lR ≥ lE的时候,lR的请求就不会被写入log。所以这里很重要的一点就是,越重要的事件的level级别应该越低。0是fatal,level越大的越不重要。
所以上面这段配置的意思是:
在com.reuters.controller下的所有类输出的gmi log,如果level级别大于等于GMI_INFORMATION,都不会被archive(<appender-ref ref="localgmilogfile_arch"/> 这个appender执行的是归档任务)。
3,而在appender一级还可以用filter加入控制
<appender name="localgmilogfile_arch" class="**.ArchiveRollingFileAppender">
<param name="LogDirPath" value="${app.folder}/log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%m%n"/>
</layout>
<filter class="com.reuters.controller.common.logging.gmi.GMIFilter">
<param name="gmi" value="true"/>
</filter>
</appender>
这里可以再次过滤掉不感兴趣的log。