一、核心类
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方法可以打印日志
}
}