Apache Log4j 架构之输出日志

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

前面介绍了Log4j的总体架构 和 Log4j的初始化, 这篇文章接着介绍Log4j如何输出日志。

Log4j输出日志 | Log4j write log

Log4j输出日志分为六个步骤:全局开关控制、日志等级过滤、封装日志信息、过滤器处理、日志信息格式化、输出至文件。下面分两个环节来介绍这六个步骤是如何实现的:

1、第一环节:预处理

当调用Log4j的方法(如:debug(String, Throwable)、info(String, Throwable))输出日志时,首先对日志信息进行预处理,其序列图如下。
预处理

说明:

  • isDisabled(int level):根据全局日志等级threshold进行判断,如果日志等级低于threshold,不输出日志。
  • isGreaterOrEquals(Priority r):根据当前logger配置的日志等级level进行判断,如果日志等级低于当前logger配置的日志等级,不输出日志。
  • foredLog(String fqcn Priority level, Object message, Throwable t):将日志信息封装成LoggingEvent对象。
  • callAppenders(LoggingEvent event):将LoggingEvent对象分发给所有的Appender。其实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void callAppenders(LoggingEvent event) {
int writes = 0;
for (Category c = this; c != null; c = c.parent) {
// Protected against simultaneous call to addAppender,
// removeAppender,...
synchronized (c) {
if (c.aai != null) {
writes += c.aai.appendLoopOnAppenders(event);
}
if (!c.additive) {
break;
}
}
}
if (writes == 0) {
repository.emitNoAppenderWarning(this);
}
}
public int appendLoopOnAppenders(LoggingEvent event) {
int size = 0;
Appender appender;
if (appenderList != null) {
size = appenderList.size();
for (int i = 0; i < size; i++) {
appender = (Appender) appenderList.elementAt(i);
appender.doAppend(event);
}
}
return size;
}

2、第二环节:输出日志

输出日志前还有两道工序需要处理:Filter处理和日志信息格式化。其执行序列图如下。
输出日志

相应的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public synchronized void doAppend(LoggingEvent event) {
if (closed) {
LogLog.error("Attempted to append to closed appender named ["
+ name + "].");
return;
}
if (!isAsSevereAsThreshold(event.getLevel())) {
return;
}
Filter f = this.headFilter;
FILTER_LOOP: while (f != null) {
switch (f.decide(event)) {
case Filter.DENY:
return;
case Filter.ACCEPT:
break FILTER_LOOP;
case Filter.NEUTRAL:
f = f.getNext();
}
}
this.append(event);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void subAppend(LoggingEvent event) {
this.qw.write(this.layout.format(event));
if (layout.ignoresThrowable()) {
String[] s = event.getThrowableStrRep();
if (s != null) {
int len = s.length;
for (int i = 0; i < len; i++) {
this.qw.write(s[i]);
this.qw.write(Layout.LINE_SEP);
}
}
}
if (shouldFlush(event)) {
this.qw.flush();
}
}

说明:

  • decide(LoggingEvent event):有三种返回值 DENY、ACCEPT、NEUTRAL,DENY表示丢弃当前日志信息,ACCEPT表示输出当前日志信息,NEUTRAL表示继续下一个Filter。Filter只能在XML配置文件中使用,Properties文件中不支持。
  • format(LoggingEvent event):对日志进行格式化处理。
  • write(String string):将日志信息输出至目的地(文件、数据库或网格)。

相关文章 | Index

1、Apache Log4j 架构
2、Apache Log4j 架构之初始化
3、Apache Log4j 架构之输出日志