深入Log4J源码之Log4J Core (八)

2014-11-24 08:26:37 · 作者: · 浏览: 9
J实现的Appender都继承自SkeletonAppender类,该类对Appender接口提供了最基本的实现,并且引入了Threshold的概念,即所有的比当前Appender定义的日志级别阀指要大的日志才会记录下来。

1 public abstract class AppenderSkeleton implements Appender, OptionHandler {
2 protected Layout layout;
3 protected String name;
4 protected Priority threshold;
5 protected ErrorHandler errorHandler = new OnlyOnceErrorHandler();
6 protected Filter headFilter;
7 protected Filter tailFilter;
8 protected boolean closed = false;
9 public AppenderSkeleton() {
10 super();
11 }
12 public void activateOptions() {
13 }
14 abstract protected void append(LoggingEvent event);
15 public boolean isAsSevereAsThreshold(Priority priority) {
16 return ((threshold == null) || priority.isGreaterOrEqual(threshold));
17 }
18 public synchronized void doAppend(LoggingEvent event) {
19 if (closed) {
20 LogLog.error("Attempted to append to closed appender named ["
21 + name + "].");
22 return;
23 }
24 if (!isAsSevereAsThreshold(event.getLevel())) {
25 return;
26 }
27 Filter f = this.headFilter;
28 FILTER_LOOP: while (f != null) {
29 switch (f.decide(event)) {
30 case Filter.DENY:
31 return;
32 case Filter.ACCEPT:
33 break FILTER_LOOP;
34 case Filter.NEUTRAL:
35 f = f.getNext();
36 }
37 }
38 this.append(event);
39 }
40 public void finalize() {
41 if (this.closed)
42 return;
43 LogLog.debug("Finalizing appender named [" + name + "].");
44 close();
45 }
46 }
SkeletonAppender实现了doAppend()方法,它首先检查日志级别是否要比threshold要大;然后如果注册了Filter,则使用Filter对LoggingEvent实例进行过滤,如果Filter返回Filter.DENY则doAppend()退出,否则执行append()方法,该方法由子类实现。

在Log4J中,Filter组成一条链,它定了以decide()方法,由子类实现,若返回DENY则日志不会被记录、NEUTRAL则继续检查下一个Filter实例、ACCEPT则Filter通过,继续执行后面的写日志操作。使用Filter可以为Appender加入一些出了threshold以外的其他逻辑,由于它本身是链状的,而且它的执行是横跨在Appender的doAppend方法中,因而这也是一个典型的AOP的概念。Filter的实现将会在下一小节中讲解。

SkeletonAppender还重写了finalize()方法,这是因为Log4J本身作为一个组件,它可能还是通过其他组件如commons-logging或slf4j组件间接的引入,因而使用它的程序不应该对它存在依赖的,然而在程序退出之前所有的Appender需要调用close()方法以释放它所占据的资源,为了不在使用Log4J的程序手动的close()的方法,以减少Log4J代码的侵入性,因而Log4J将close()的方法调用加入到finalize()方法中,即在垃圾回收器回收Appender实例时就会调用它的close()方法。

WriterAppender类和ConsoleAppender类
WriterAppender将日志写入Java IO中,它继承自SkeletonAppender类。它引入了三个字段:immediateFlush,指定没写完一条日志后,即将日志内容刷新到设备中,虽然这么做会有一点性能上的损失,但是如果不怎么做,则会出现在程序异常终止的时候无法看到部分日志信息,而经常这些丢失的日志信息要用于分析为什么会出现异常终止的情况,因而一般推荐将该值设置为true,即默认值;econding用于定义日志文本的编码方式;qw定义写日志的writer,它可以是文件或是控制台等Java IO支持的流。

在写日志文本前,WriterAppender还会做其他检查,如该Appender不能已经closed、qw和layout必须有值等,而后才可以将layout格式化后的日志行写入设备中。若layout本身不处理异常问题,则有Appender处理异常问题。最后如果每行日志需要刷新,则调用刷新操作。

1 public class WriterAppender extends AppenderSkeleton {
2 protected boolean immediateFlush = true;
3 protected String encoding;
4 protected QuietWriter qw;
5 public WriterAppender() {
6 }
7 public WriterAppender(Layout layout, OutputStream os) {
8 this(layout, new OutputStreamWriter(os));
9 }
10 public WriterAppender(Layout layout, Writer writer) {
11