Log4j 代码学习

18 篇文章 0 订阅

最近闲来无事,正好手头上有Log4j的代码,于是就拿来学习了下。 想来这个小工具也用了很多年了,但是从来没有真正的去了解过内部机制,经过一番学习后,发现结构还是蛮不错的,里面有很多东西值得学习。

 

总的来说,Log4j的代码我认为可以分为这几大部分:

 

1. 产品Logger

2. 附件Appender

3. 仓储Repository

4. 生产工厂Factory

5. 配置管理Configuration

 

产品Logger

Log4j里面最重要的当然就是Logger类了,在代码中,我们通常采用LogManager.getLogger(clazz);或者Logger.getLogger(clazz) 方法来获取Logger对象。我们就来看看这里面到底有什么玄机。

 

下图其实是简略的版本,将一些重要的类放在其它的类图中,以突出Logger和Appender之间的关系。

 


 

 Log4j初始的加载代码其实是在LogManager的静态初始块中,我会在另一篇中提到。但首先,我们先来看看LogManager.getLogger(clazz);和Logger.getLogger(clazz) 到底有什么区别:

 

public class Logger extends Category {
  static public Logger getLogger(String name) {
    return LogManager.getLogger(name);
  }
  ...
}

 

可以看出,Logger本身其实并不做任何生成Logger实例的事情,而是直接代理给LogManager的静态方法。我们在代码中使用任何一种方式都是一样的。

 

如果仔细浏览Logger类里的代码,就会发现,Logger本身几乎不做任何事情,只有几个简单的trace方法将消息以trace的级别来记录。而我们经常使用的方法像info(), error()等,都不在Logger类里。相应的,其实他们都可以在Logger的父类Category里面找到。

 

按照Javadoc,Category类是deprecated and replaced by the subclass Logger,也就是说,在早期的版本中,可能用的都是Category而不是Logger类。而我们常用的一些方法,其实也还是用的是Category类里面的。

 

Category里面有几个很重要的变量:

 

  volatile protected Level level;

  volatile protected Category parent;

  protected LoggerRepository repository;

  AppenderAttachableImpl aai;

 

 

Repository, 每个category都属于某个repository,我们会在下一个章节详细描述LoggerRepository类。

 

level,每一个Category(Logger)都拥有一个级别,这个级别就是我们通常所说的DEBUG,WARN,ERROR这些。这里就不再展开描述了。

 

parent,parent本身又是一个Category,这里就涉及到一个很重要的设计,整个Categeory是一个级联的结构,当我们对category实例执行某项log操作时,它所有的父节点也会相应的执行同样的操作:

 

public void info(Object message) {
    if(repository.isDisabled(Level.INFO_INT)) return;
    if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))
      forcedLog(FQCN, Level.INFO, message, null);
  }

protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) {
    callAppenders(new LoggingEvent(fqcn, this, level, message, t));
}

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);
    }
  }

从红色文字上可以看出,当Category有父亲时,将会首先调用自身的log记录信息,然后调用父亲的方法,直到继承的顶端。

 

下图展示了当用户调用info方法时的序列图:


从上图及代码可以发现,Category本身并没有做任何的记录log的工作,所有的这些工作都是由AppenderAttachableImpl来完成的。这个类会在下一篇文章里详细介绍。

 

至此,关于Logger类的内容已经大致描述,大部分时候,Logger并不做任何事情,而是将工作代理给其它类,但Logger仍然是整个Log4j里最重要的类,同时也是学习Log4j的最佳入口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值