Log4j这个东东,大家一直都在用,可是真正有多少人知道,为什么要那样配列?或者说有多少不抄袭可以自己配一份log4j列?读者可以问问自己能否办到。我们本章就一些重要的环节结合源码进行分析。
一、Log4j是在那个地方读取配置文件的?
在回答这个问题前,首先想想我们在程序中是怎么用Log4j来打印日志的?
1.获取Logger,如
private static Logger logger = Logger.getLogger(App.class);
2.在方法中调用logger方法打印日志
logger.info("this is the main Method");
于是我们跟进getLogger()方法查看实现,发现该方法如下定下:
static
public
Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
}
他是委托LogManager去实现的。于是我们再次跟进LogManager,通过看查该类的结构,发现有个静态的static初始块,我们一读代码,发现原来Log4j的配置文件就是在这里读取的,而且他是先查找log4j.xml,如果查找不到再去查找log4j.properteis。
其实还有一个解决这个问题的办法,就是将log4j的源码,以项目的形式导入eclipse/idea中,然后全局搜索log4j.properties或是log4j.xml。为什么想到搜这2个文件名?因为log4j默认配置文件名就是这2个,那么他在程序中,肯定会有常量来定义这个2个文件名,就算没人常量,也会有直接根据这2个文件名加载配置的地方。这是一个程序开发者正常的逻辑,和处理办法。
二、我们在log4j.properties中,经常都会配置一个log4j.rootLogger?它的作用是什么?
我们在配置文件中,定主了rootLogger,Appender,Layout, 为什么我们程序中log就会按照这些规格来输出列? 难道我们自己定义的Logger跟这这个rootLogger有什么关系?
其实从命名,我们是不是猜测到rootLogger,是我们定义的其它Logger的超类列? 事实上也确实如此!我们一路踏实getLogger()方法,一直跟踪到了Hierarchy.getLogger();
public
Logger getLogger(String name, LoggerFactory factory) {
//System.out.println("getInstance("+name+") called.");
CategoryKey key = new CategoryKey(name);
// Synchronize to prevent write conflicts. Read conflicts (in
// getChainedLevel method) are possible only if variable
// assignments are non-atomic.
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;
} else if (o instanceof ProvisionNode) {
//System.out.println("("+name+") ht.get(this) returned ProvisionNode");
logger = factory.makeNewLoggerInstance(name);
logger.setHierarchy(this);
ht.put(key, logger);
updateChildren((ProvisionNode) o, logger);
updateParents(logger);
return logger;
}
else {
// It should be impossible to arrive here
return null; // but let's keep the compiler happy.
}
}
}
其实我们创建的Logger,都会存放到一个 命名为ht 的HashTable中。最关键的是updateParents(logger);这个方法,于是我们进一步跟踪这个方法,发现,如果一个Looger找不到父Logger,那么
if(!parentFound)
cat.parent = root;
会将它的parent设为rootLogger。 这个方法的具体代码,我就不贴了,大家可以自己去看。那么rootLogger的东西(Appender,Layout),是不是他可以直接继承过来用列?
那么关于rootLogger的继承体系和依赖关系 ,我画了一张比较简单的uml草图
(~~~~~~~~~~~~~~~~~~~图片占位,CSDN上传图片又挂掉了~~~~~~~~~~~~~~~~~~~~~~~)
上传图片试了N次都是挂掉了,只能简单描述一下吧,或者大家进去看一下类结构吧
RootLogger继承Logger,而Logger又继承至Category。
Category里面有个变量 AppenderAttachableImpl aai; 这个类就是封装了一个 protected Vector appenderList;来存放Appender。
由此可以看出Logger和Appender是 1:N的关系
至于Appender的继承体系太比较我,不描述了,大家自己去看。说了这么多,大家再回头对照看下配置文件,是不是一下子明白了很多?