最近的一个需求,需要记录接口访问的详情,如请求入参、出参、请求上下文、路径、消耗时间以及成功失败等信息,将这些信息以结构化的形式输出到单独的日志文件中,方便以后入库到elasticsearch中做分析。

百度搜索自定义日志级别的文章一大把,基本上都是使用Filter和xml配置的方式,这种方式能行,但我感觉写的代码略多,而且我的工程中用的properties的方式配置的,个人也比较懒也就不想改动,就没有采用这种方式。

log4j中每个appenders都有一个threshold,这个值默认是null,它对应的是配置文件中log4j.appender.errorfile.Threshold = debug|info|error|...项,如果不配置,会将所有级别日志打印到这个appender中,如果是debug则会将debug及以上的级别日志打印到这个appender中。

在类AppenderSkeleton中有一个很重要的方法,它的默认实现如下

1
2
3
public boolean isAsSevereAsThreshold(Priority priority) {
return ((threshold == null) || priority.isGreaterOrEqual(threshold));
}

所以,很重要的一点就是自定义一个appender然后重写这个方法,修改判断逻辑,只是将等于自定义级别的日志打印到appender中。详细代码如下

1
2
3
4
5
6
public class ConsumeLogLevel extends Level {
public final static int CONSUME_INT = Priority.DEBUG_INT+50;
public ConsumeLogLevel(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
}

自定义日志级别类,我设置了一个比debug级别高一点的级别consume。这样的好处是不会将consume的日志打印到info中(如果设置了Threshold=info的appender)。

1
2
3
4
5
6
7
public class ConsumeLog {
private static Logger logger = Logger.getLogger(ConsumeLog.class);
protected static final Level consumeLevel = new ConsumeLogLevel(ConsumeLogLevel.CONSUME_INT,"CONSUME", 7);
public static void logConsumeInfo(Object objLogInfo){
logger.log(consumeLevel, objLogInfo);
}
}

定义一个类用于打印日志,创建一个日志级别实例consumeLevel,调用logger打印。

1
2
3
4
5
6
7
8
9
10
public class ConsumeAppender extends DailyRollingFileAppender {
@Override
public Priority getThreshold() {
return ConsumeLog.consumeLevel;
}
@Override
public boolean isAsSevereAsThreshold(Priority priority) {
return this.getThreshold().equals(priority);
}
}

自定义appender,这个例子中继承自DailyRollingFileAppender,实际以需求而定可以继承自其它appender,重写两个方法,在isAsSevereAsThreshold方法中修改判断逻辑,只有等于自定义级别的日志才打印。其中getThreshold()方法写死返回consumeLevel。

1
2
3
4
5
6
7
log4j.rootLogger=debug,consume
log4j.appender.consume=com.xxx.log.ConsumeAppender
log4j.appender.consume.File=../logs/bss-intf-cpct-consume-log.log
log4j.appender.consume.DatePattern='.'yyyy-MM-dd
log4j.appender.consume.append=true
log4j.appender.consume.layout=org.apache.log4j.PatternLayout
log4j.appender.consume.layout.ConversionPattern=xxx %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n

log4j.properties配置如上,需要修改appender类名为自定义appender。

实际使用

1
ConsumeLog.logConsumeInfo(xxx);

在实际中可以根据需求在ConsumeLogLevel中定义多种级别,ConsumeLog中实例化多个ConsumeLogLevel级别,针对每个级别创建一个自定义appender就可以实现多个自定义日志级别打印日志到多个文件中了。