Log4j源码浅析

Jun 20, 2013


一、核心类

log4j的核心类主要有:Logger(Category)、LoggerManager、LoggingEvent、Hierarchy、Configurator及其子类、Appender及其子类。下面介绍一下各个类的主要作用:

1.Logger,一个Logger实例就是一个记录器,调用静态方法getLogger()可以获得一个记录器,该类继承自Category

2.Category,Category类继承自AppenderAttachable接口,该接口有另一个实现AppenderAttachableImpl,先看一下AppenderAttachableImpl类
    AppenderAttachableImpl类的主要属性有:
        protected Vector  appenderList; //一个存储Appender对象的列表
	    
		protected boolean additive = true; //设置输出源附加特性,如果为false,则不再向父记录器查找
		
    除了实现了AppenderAttachable接口的添加、删除Appender对象和判断Appender对象是否存在之外,还定义了另外一个很重要的方法:
        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;
	    }
    这个方法传入参数的是一个打印日志请求,方法的功能是遍历appender对象列表,调用其doAppend(event)方法,此处应用了观擦者模式。
	
	CateGory类的分析:
	public class Category implements AppenderAttachable {
	    ...
	    volatile protected Level level; //记录器级别
		
		volatile protected Category parent; //父记录器
		
		protected LoggerRepository repository; //记录器容器
		
		AppenderAttachableImpl aai; //Appender容器类的具体实现
		
		protected boolean additive = true; //输出源附加特性,若为false,则不再向父记录器查找
		
		//下面这方法展示了additive的应用
		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) { //根据additive的设置确定是否需要继续往父记录器查找
						break;
					}
			    }
			}
		}
	}
	  
3.LoggerManager,负责记录器的初始化工作等

4.LoggingEvent,一次打印日志请求的封装

5.Hierarchy,记录器(Loggers)的容器类

6.Configurator,解析日志配置文件的类

7.Appender,定义了日志输出的策略

二、初始化流程

当程序中调用Logger.getLogger("test")来生成一个记录器的时候,其运行过程如下:

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

LoggerManager定义了一个初始化过程的静态块:
static {
	Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG)); //定义了一个Hierarchy对象,Hierarchy类稍后分析
	...
	OptionConverter.selectAndConfigure(url, configuratorClassName,
			   LogManager.getLoggerRepository());
	...
}

Hierarchy是一个记录器的容器,此处初始化为一个RootLogger的对象,进入到Hierarchy内部:
public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
    ...
    Hashtable ht; // 定义一个Hashtable来存放记录器
	Logger root;  // 定义根记录器
    ...
	
	public Hierarchy(Logger root) {
	    ht = new Hashtable();
		...
		this.root = root;
		...
	}
	...
	//获取记录器
	public Logger getLogger(String name, LoggerFactory factory)
	{
		CategoryKey key = new CategoryKey(name);

		Logger logger;

		synchronized(ht)
		{
		    Object o = ht.get(key);
		    if(o == null) //容器中如果不存在要查找的记录器,就生成一个记录器
		    {
				logger = factory.makeNewLoggerInstance(name);
				logger.setHierarchy(this);
				ht.put(key, logger); //将生成的记录器存放到容器中
				updateParents(logger); //设置新生成记录器的父记录器,这个很重要,记录器的继承都是通过设置父记录器实现的
				return logger;
			} 
			else if(o instanceof Logger)
			{
				return (Logger) o;
			}
			...
	    }
	}
	...
	//设置记录器的父记录器
	final private void updateParents(Logger cat) {
		String name = cat.name;
		int length = name.length();
		boolean parentFound = false;

		// if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z"
		for(int i = name.lastIndexOf('.', length-1); i >= 0;
										 i = name.lastIndexOf('.', i-1))
		{
			String substr = name.substring(0, i);

			CategoryKey key = new CategoryKey(substr); // simple constructor
			Object o = ht.get(key);
			// Create a provision node for a future parent.
			if(o == null)
			{
		        ...
		    } 
			else if(o instanceof Category)
			{
				parentFound = true;
				cat.parent = (Category) o;
				break; // no need to update the ancestors of the closest ancestor
			}
			...
		}
		// If we could not find any existing parents, then link with root.
		if(!parentFound)
		    cat.parent = root;
	}
	...
}

下面再来看看OptionConverter.selectAndConfigure()方法:

public class OptionConverter {
   ...
    //设置Configurator对象,然后通过Configurator对象的doConfigure()方法来获取配置文件的信息
    static public void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy)
    {
	    Configurator configurator = null;
	    String filename = url.getFile();

	    ...

	    if(clazz != null) {
		    LogLog.debug("Preferred configurator class: " + clazz);
		    configurator = (Configurator) instantiateByClassName(clazz,
								  Configurator.class,
								  null);
		    if(configurator == null) {
		     LogLog.error("Could not instantiate configurator ["+clazz+"].");
		     return;
		}
	    } else {
		    configurator = new PropertyConfigurator();
	    }

	    configurator.doConfigure(url, hierarchy); //获取配置文件信息
   }
}

以PropertyConfigurator类的doConfigure()方法为例,看看这个类是怎么处理的:

public class PropertyConfigurator implements Configurator {
    ...
	public void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
	    ...
		doConfigure(props, hierarchy);
	}
	
	public void doConfigure(Properties properties, LoggerRepository hierarchy) {
	    ...
		configureRootCategory(properties, hierarchy);
		configureLoggerFactory(properties);
		parseCatsAndRenderers(properties, hierarchy);
		...
	}
	//在doConfigure里面的configureRootCategory()和parseCatsAndRenderers()这两个方法里又调用了parseCategory()方法
	void parseCategory(Properties props, Logger logger, String optionKey,
	     String loggerName, String value) {
	    ...
		appender = parseAppender(props, appenderName);
        if(appender != null) {
            logger.addAppender(appender); //给logger这个记录器加上appender
        }
		...
	}
}
到此,基本分析了log4j的初始化过程

###三、日志打印流程
日志打印以Category的debug()方法为例:
public class Category implements AppenderAttachable {

    public void debug(Object message) {
		if(repository.isDisabled(Level.DEBUG_INT))
		  return;
		if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) { //级别大于DEBUG才打印
		  forcedLog(FQCN, Level.DEBUG, 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); //调用AppenderAttachableImpl对象的appendLoopOnAppenders()方法打印日志
				}
				if(!c.additive) {
				  break;
				}
			}
		}
		...
	}
}

下面看AppenderAttachableImpl的实现:
public class AppenderAttachableImpl implements AppenderAttachable {
     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); //调用doAppend方法来打印日志
		  }
		}    
		return size;
	}
}
我们看看Appender的实现类WriterAppender的append()方法是如何实现的:
public class WriterAppender extends AppenderSkeleton
{
    public void append(LoggingEvent event) {
	    ...
	    subAppend(event);
	}
	
	protected void subAppend(LoggingEvent event) {
	    ...
	    this.qw.write(this.layout.format(event)); //qw是一个QuietWriter对象,调用其write方法可以打印日志
	}
}