深入Log4J源码之Log4J Core (一)

2014-11-24 08:26:37 · 作者: · 浏览: 0

即获取Logger实例->判断Logger实例对应的日志记录级别是否要比请求的级别低->若是调用forceLog记录日志->创建LoggingEvent实例->将LoggingEvent实例传递给Appender->Appender调用Layout实例格式化日志消息->Appender将格式化后的日志信息写入该Appender对应的日志输出中。

在简单的介绍了Log4J各个模块类的作用后,以下将详细的介绍各个模块的具体作用以及代码实现。

Logger类
Logger是对记录日志动作的抽象,它提供了记录不同级别日志的接口,日志信息可以包含异常信息也可以不包含:

1 public void debug(Object message) {
2 if(isLevelEnabled(Level.DEBUG)) {
3 forceLog(FQCN, Level.DEBUG, message, null);
4 }
5 }
6 public void debug(Object message, Throwable cause) {
7 if(isLevelEnabled(Level.DEBUG)) {
8 forceLog(FQCN, Level.DEBUG, message, cause);
9 }
10 }
11 protected void forceLog(String fqcn, Level level, Object message, Throwable t) {
12 callAppenders(new LoggingEvent(fqcn, this, level, message, t));
13 }

Logger类包含Level信息 ,如果当前Logger未设置Level值,它也可以中父节点中继承下来,该值可以用来控制该Logger可以记录的日志级别: www.2cto.com

1 protected Level level;
2 public Level getEffectiveLevel() {
3 for(Logger logger = this; logger != null; logger = logger.parent) {
4 if(logger.level != null) {
5 return logger.level;
6 }
7 }
8 return null;
9 }
10 public boolean isLevelEnabled(Level level) {
11 return level.isGreaterOrEqual(this.getEffectiveLevel());
12 }
13 public boolean isDebugEnabled() {
14 return isLevelEnabled(Level.DEBUG);
15 }

Logger是一个命名的实体,其名字一般用”.”分割以体现不同Logger的层次关系,其中Level和Appender信息可以从父节点中获取,因而Logger类中还具有name和parent属性。

1 private String name;
2 protected Logger parent;

在某些情况下,我们希望某些Logger只将日志记录到特定的Appender中,而不想记录在父节点中的Appender中,Log4J为这种需求提供了additivity属性,即对当前Logger节点,如果其additivity属性设置为false,则该Logger不会继承父节点的Appender信息,但是其子节点依然会继承该Logger的Appender信息,除非子节点的additivity属性也设置成了false。

1 private boolean additive = true;
2 public void callAppenders(LoggingEvent event) {
3 int writes = 0;
4
5 for(Logger logger = this; logger != null; logger = logger.parent) {
6 synchronized(logger) {
7 if(logger.appenders != null) {
8 writes += logger.appenders.appendLoopOnAppenders(event);
9 }
10 if(!logger.additive) {
11 break;
12 }
13 }
14 }
15
16 if(writes == 0) {
17 System.err.println("No Appender is configed.");
18 }
19 }

最后,为了支持国际化,Log4J还提供了两个l7dlog()方法,通过指定的key,以从资源文件中获取消息内容。为了使用这两个方法,需要设置资源文件。同样,资源文件也是可以从父节点中继承的。

1 private ResourceBundle resourceBundle;
2 public void l7dlog(Level level, String key, Throwable cause) {
3 if(isLevelEnabled(level)) {
4 String message = getResourceBundleString(key);
5 if(message == null) {
6 message = key;
7 }
8 forceLog(FQCN, level, message, cause);
9 }
10 }
11
12 public void l7dlog(Level level, String key, Object[] params, Throwable cause) {
13
14 if(pattern == null) {
15 message = key;
16 } else {
17 message = MessageFormat.format(pattern, params);
18 }
19
20 }
21
22 protected String getResourceBundleString(String key) {
23 ResourceBundle rb = getResourceBundle();
24
25 return rb.getString(key);
26 }
27 public ResourceBundle getResourceBundle() {
28 for(Logger logger = this; logger != null; logger = logger.parent) {
29 if(logger.resourceBundle != null) {
30 return logger.resourceBundle