当前位置:编程学习 > JAVA >>

深入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 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,