深入Log4J源码之LoggerRepository和Configurator
LoggerRepository从字面上理解,它是一个Logger的容器,它会创建并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。它的这种特性有点类似Spring的IOC概念。Log4J支持两种配置文件:properties文件和xml文件。Configurator解析配置文件,并将解析后的信息添加到LoggerRepository中。LogManager最终将LoggerRepository和Configurator整合在一起。
LoggerRepository接口
LoggerRepository是一个Logger的容器,它负责创建、缓存Logger实例,同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以’.’分隔。
除了做为一个Logger容器,它还有一个Threshold属性,用于过滤所有在Threshold级别以下的日志。以及其他和Logger操作相关的方法和属性。
LoggerRepository的接口定义如下:
1 public inte易做图ce LoggerRepository {
2 public void addHierarchyEventListener(HierarchyEventListener listener);
3 boolean isDisabled(int level);
4 public void setThreshold(Level level);
5 public void setThreshold(String val);
6 public void emitNoAppenderWarning(Category cat);
7 public Level getThreshold();
8 public Logger getLogger(String name);
9 public Logger getLogger(String name, LoggerFactory factory);
10 public Logger getRootLogger();
11 public abstract Logger exists(String name);
12 public abstract void shutdown();
13 public Enumeration getCurrentLoggers();
14 public abstract void fireAddAppenderEvent(Category logger, Appender appender);
15 public abstract void resetConfiguration();
16 }
Hierarchy类
Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现,因而首先从这个方法开始。
Hierarchy中用一个Hashtable来存储所有Logger实例,它以CategoryKey作为key,Logger作为value,其中CategoryKey是对Logger中Name字符串的封装,之所以要引入这个类是出于性能考虑,因为它会缓存Name字符串的hash code,这样在查找过程中计算hash code时就可以直接取得而不用每次都计算。
1 class CategoryKey {
2 String name;
3 int hashCache;
4
5 CategoryKey(String name) {
6 this.name = name;
7 hashCache = name.hashCode();
8 }
9 final public int hashCode() {
10 return hashCache;
11 }
12 final public boolean equals(Object rArg) {
13 if (this == rArg)
14 return true;
15 if (rArg != null && CategoryKey.class == rArg.getClass())
16 return name.equals(((CategoryKey) rArg).name);
17 else
18 return false;
19 }
20 }
getLogger()方法中有一个重载函数提供LoggerFactory接口,它用于没有在LoggerRepository中找到Logger实例时创建相应的Logger实例,默认实现直接创建一个Logger实例,用户可以通过自定义LoggerFactory实现创建自己的Logger实例。
1 public inte易做图ce LoggerFactory {
2 public Logger makeNewLoggerInstance(String name);
3 }
4 class DefaultCategoryFactory implements LoggerFactory {
5 public Logger makeNewLoggerInstance(String name) {
6 return new Logger(name);
7 }
8 }
getLogger()方法首先根据传入name创建CategoryKey实例,而后从缓存ht字段中查找:
1. 如果找到对应的Logger实例,则直接返回该实例。
2. 如果没有找到任何实例,则使用LoggerFactory创建新的Logger实例,并将该实例缓存到ht集合中,同时更新新创建Logger实例的parent属性。更新parent属性最简单的做法是从后往前以’.’为分隔符截取字符串,使用截取后的字符串从ht集合中查找是否存在Logger实例,如果存在,则新创建的Logger实例的parent即为找到的实例,若在整个遍历过程中都没有找到相应的parent实例,则其parent实例为root。然而如果一个“x.y.z.w”Logger起初的parent设置为root,而后出现“x.y.z”Logger实例,那么就需要更新“x.y.z.w”Logger的parent为“x.y.z”Logger实例,此时就会遇到一个如何找到在集合中已经存在的“x.y.z”Logger实例子节点的问题。当然一种简单的做法是遍历ht集合中所有实例,判断那个实例是不是“x.y.z”Logger实例的子节点,是则更新其parent节点。由于每次的遍历会引起一些性能问题,因而Log4J使用ProvisionNode事先将所有的可能相关的子节点保存起来,并将ProvisionNode实例添加到ht集合中,这样只要找到对应的ProvisionNode实例,就可以找到所有相关的子节点了。比如对“x.y.z.w”Logger实例,它会产生三个ProvisionNode实例(当然如果相应的实例已经存在,则直接添加而无需创建,另外,如果相应节点已经是Logger实例,那么将“x.y.z.w”Logger实例的parent直接指向它即可):ProvisionNode(“x”), ProvisionNode(“x.y”), ProvisionNode(“x.y.z”),他们都存储了“x.y.z.w”Logger实例作为其子节点。
1 class ProvisionNode extends Vector {
2 ProvisionNode(Logger logger) {
3 super();
4 this.addElement(logger);
5 }
6 }
7 final private void updateParents(Logger cat) {
8 String name = cat.name;
9 int length = name.length();
10 boolean parentFound = false;
11 // if name = "x.y.z.w", loop thourgh "x.y.z", "x.y" and "x"
12 for (int i = name.lastIndexOf('.', length - 1); i >= 0; i = name
13 .lastIndexOf('.', i - 1)) {
14 String substr = name.substring(0, i);
15 CategoryKey key = new CategoryKey(substr);
16 Object o = ht.get(key);
17 if (o == null) {
18 ProvisionNode pn = new ProvisionNode(cat);
19 ht.put(key, pn);
20 } else if (o instanceof Category) {
21 parentFound = true;
22 cat.parent = (Category) o;
23 break; // no need to update the ancestors of the closest
24 &n
补充:软件开发 , Java ,