源碼賞析之深入瞭解log4j的LogManager類

    在上一篇博文中對log4j的結構有了一定的瞭解,這一篇就深入地瞭解一下其中的LogManager類。首先,先了解一下與LogManager相關的類結構;然後,再仔細地說明每個類的作用及其之間的關係。

1       類圖



 

       圖中的方框代表類,圓圈代表接口,實線箭頭代表關聯關係(箭頭的一方爲非箭頭一方的屬性),虛線箭頭代表依賴關係(箭頭的一方爲非箭頭一方的方法參數或局部變量),實線三角箭頭表示繼承關係。

從圖中可知,LogManager類有RepositorySelector屬性和LoggerRepository屬性,並且依賴於Configurator接口。LoggerRepository類有LoggerFactory屬性。DefaultLoggerFactory類有Logger屬性。接下來簡要介紹一下這些類和接口的作用。

LogManager類將ConfiguratorLoggerRepository整合在一起,它在初始化的時候(static)找到Log4J配置文件,並且將其解析到LoggerRepository中。

Configurator用來處理相關的配置文件。Log4J支持兩種配置文件:properties文件和xml文件。

RepositorySelector負責緩存和管理LoggerRepository對象。

LoggerRepository是一個Logger的容器,它會調用LoggerFactory創建Logger並緩存Logger實例,從而具有相同名字的Logger實例不會多次創建,以提高性能。調用Configurator對配置文件進行解析,並將解析後的信息添加到LoggerRepository中。同時它也維護了Logger之間的關係,因爲在Log4J中,所有Logger都組裝成以RootLogger爲根的一棵樹,樹的層次由LoggerName來決定,其中以”.”分隔。

HierarchyLog4J中默認對LoggerRepository的實現類,它用於表達其內部的Logger是以層次結構存儲的。在對LoggerRepository接口的實現中,getLogger()方法是其最核心的實現。

2       RootLogger

       Log4J中,所有Logger都組裝成以RootLogger爲根的一棵樹。

public final class RootLogger extends Logger { 

  /**

   * 使用日誌級別來構造一個日誌記錄器,記錄器名稱默認爲root

   */

  public RootLogger(Level level) {

    super("root");

    setLevel(level);

  }

}

3       LoggerFactory

        這裏使用了創建型設計模式的工廠(Factory)模式。所有需要創建新Logger的方法都可以調用該接口的makeNewLoggerInstance方法。

/**

 *實現該接口可以創建新的日誌記錄器實例。

 */

public interface LoggerFactory {

  public Logger makeNewLoggerInstance(String name);

}

4      LoggerFactory的實現類

       該實現類爲DefaultLoggerFactory

public final class DefaultLoggerFactory implements LoggerFactory {

  public DefaultLoggerFactory() {}

  public Logger makeNewLoggerInstance(String name) {

    return new Logger(name);

  }

}

5       Configurator

        Configurator用來處理相關的配置文件。Log4J支持兩種配置文件:properties文件和xml文件。如下是配置器接口:

public interface Configurator {

}

6       Configurator的抽象實現類

        該抽象實現類是ConfiguratorBase類。

/**

 * 該類包含了一個大部分配置類都需要的功能。

 */

abstract public class ConfiguratorBase implements Configurator {

}

7       ConfiguratorBaseproperty實現類

       該實現類是PropertyConfigurator類,負責對property屬性文件進行解析。

public class PropertyConfigurator extends ConfiguratorBase

    implements ConfiguratorEx {

}

8       ConfiguratorBaseXML實現類

    該實現類是JoranConfigurator類。負責對XML配置文件進行解析。

public class JoranConfigurator extends ConfiguratorBase

    implements ConfiguratorEx {

}

9       LoggerRepository接口

        LoggerRepository它是一個Logger的容器,它會調用LoggerFactory創建Logger並緩存Logger實例,從而具有相同名字的Logger實例不會多次創建,以提高性能。調用Configurator對配置文件進行解析,並將解析後的信息添加到LoggerRepository中。

        同時它也維護了Logger之間的關係,因爲在Log4J中,所有Logger都組裝成以RootLogger爲根的一棵樹,樹的層次由LoggerName來決定,其中以”.”分隔。

        除此之外,它還有一個Threshold屬性,用於過濾所有在Threshold級別以下的日誌。以及其他和Logger操作相關的方法和屬性。

public interface LoggerRepository {

  // 添加一個事件到repository.

  public void addHierarchyEventListener(HierarchyEventListener listener);

  public void setThreshold(Level level);

  public void setThreshold(String val);

  public Level getThreshold();

  public Logger getLogger(String name);

  public Logger getLogger(String name, LoggerFactory factory);

  public Logger getRootLogger();

  public abstract Logger exists(String name);

  public abstract void shutdown();

  public Enumeration getCurrentLoggers();

  public abstract void fireAddAppenderEvent(Category logger, Appender appender);

  public abstract void resetConfiguration();

}

10             LoggerRepository的子接口

public interface LoggerRepositoryEx extends LoggerRepository {

  void addLoggerRepositoryEventListener(LoggerRepositoryEventListener listener);

  void removeLoggerRepositoryEventListener(LoggerRepositoryEventListener listener);

  void addLoggerEventListener(LoggerEventListener listener);

  void removeLoggerEventListener(LoggerEventListener listener);

  String getName();

  void setName(String repoName);

  void fireRemoveAppenderEvent(Category logger, Appender appender);

  void fireLevelChangedEvent(Logger logger);

  void fireConfigurationChangedEvent();

  PluginRegistry getPluginRegistry();

  Map getProperties();

  String getProperty(String key);

  void setProperty(String key, String value);

  void setLoggerFactory(LoggerFactory loggerFactory);

  LoggerFactory getLoggerFactory();

}

11            LoggerRepositoryEx的實現類

        該實現類是Hierarchy類。

        HierarchyLog4J中默認對LoggerRepository的實現類,它用於表達其內部的Logger是以層次結構存儲的。在對LoggerRepository接口的實現中,getLogger()方法是其最核心的實現。

Hierarchy中用一個Hashtable來存儲所有Logger實例,它以CategoryKey作爲keyLogger作爲value,其中CategoryKey是對LoggerName字符串的封裝,之所以要引入這個類是出於性能考慮,因爲它會緩存Name字符串的hash code,這樣在查找過程中計算hash code時就可以直接取得而不用每次都計算。

class CategoryKey {

  String name;

  int hashCache;

  CategoryKey(String name) {

    this.name = name;

    hashCache = name.hashCode();

  }

}

        getLogger()方法首先根據傳入name創建CategoryKey實例,而後從緩存ht字段中查找:

1.       如果找到對應的Logger實例,則直接返回該實例。

2.       如果沒有找到任何實例,則使用LoggerFactory創建新的Logger實例,並將該實例緩存到ht集合中,同時更新新創建Logger實例的parent屬性。更新parent屬性最簡單的做法是從後往前以’.’爲分隔符截取字符串,使用截取後的字符串從ht集合中查找是否存在Logger實例,如果存在,則新創建的Logger實例的parent即爲找到的實例,若在整個遍歷過程中都沒有找到相應的parent實例,則其parent實例爲root。然而如果一個“x.y.z.wLogger起初的parent設置爲root,而後出現“x.y.zLogger實例,那麼就需要更新“x.y.z.wLoggerparent爲“x.y.zLogger實例,此時就會遇到一個如何找到在集合中已經存在的“x.y.zLogger實例子節點的問題。當然一種簡單的做法是遍歷ht集合中所有實例,判斷那個實例是不是“x.y.zLogger實例的子節點,是則更新其parent節點。由於每次的遍歷會引起一些性能問題,因而Log4J使用ProvisionNode事先將所有的可能相關的子節點保存起來,並將ProvisionNode實例添加到ht集合中,這樣只要找到對應的ProvisionNode實例,就可以找到所有相關的子節點了。比如對“x.y.z.wLogger實例,它會產生三個ProvisionNode實例(當然如果相應的實例已經存在,則直接添加而無需創建,另外,如果相應節點已經是Logger實例,那麼將“x.y.z.wLogger實例的parent直接指向它即可):ProvisionNode(x), ProvisionNode(x.y), ProvisionNode(x.y.z),他們都存儲了“x.y.z.wLogger實例作爲其子節點。

public class Hierarchy implements LoggerRepositoryEx, RendererSupport {

  private LoggerFactory loggerFactory;

  private final ArrayList repositoryEventListeners;

  private final ArrayList loggerEventListeners;

  String name;

  Hashtable ht;  // Loggers所在的Hashtable.

  Logger root;  // Root logger.

  /**

   * 構造一個logger hierarchy.

   */

  public Hierarchy(final Logger rootLogger) {

    ht = new Hashtable();

    repositoryEventListeners = new ArrayList(1);

    loggerEventListeners = new ArrayList(1);

    this.root = rootLogger;

    this.objectMap = new HashMap();

    // Enable all level levels by default.

    setThreshold(Level.ALL);

    this.root.setHierarchy(this);

    rendererMap = new RendererMap();

    rendererMap.setLoggerRepository(this);

    properties = new Hashtable();

    loggerFactory = new DefaultLoggerFactory();

  }

  public Logger getLogger(final String loggerName) {

    return getLogger(loggerName, loggerFactory);

  }

  public Logger getLogger(final String loggerName, final LoggerFactory factory) {

    CategoryKey key = new CategoryKey(loggerName);

    Logger logger;

    synchronized (ht) {

      Object o = ht.get(key);

      if (o == null) {

        LogLog.debug("Creating new logger [" + loggerName + "] in repository [" + getName() + "].");

        logger = factory.makeNewLoggerInstance(loggerName);

        logger.setHierarchy(this);

        ht.put(key, logger);

        updateParents(logger);

        return logger;

      } else if (o instanceof Logger) {

          LogLog.debug("Returning existing logger [" + loggerName + "] in repository [" + getName() + "].");

          return (Logger) o;

      } else if (o instanceof ProvisionNode) {

        logger = factory.makeNewLoggerInstance(loggerName);

        logger.setHierarchy(this);

        ht.put(key, logger);

        updateChildren((ProvisionNode) o, logger);

        updateParents(logger);

        return logger;

      } else {

        // 代碼運行時不會到達這裏。

        return null; // 但是這樣可以避免編譯器告警。

      }

    }

  }

}

12             RepositorySelector接口

    RepositorySelector負責緩存和管理LoggerRepository對象。

public interface RepositorySelector {

  public LoggerRepository getLoggerRepository();

}

13             RepositorySelector的子接口

        該子接口爲RepositorySelectorEx 接口。

public interface RepositorySelectorEx extends RepositorySelector {

  LoggerRepository detachRepository(String name);

}

14             RepositorySelectorEx的實現類

        該實現類爲DefaultRepositorySelector類。

public class DefaultRepositorySelector implements RepositorySelectorEx {

  private LoggerRepository defaultRepository;

  public DefaultRepositorySelector(final LoggerRepository repository) {

    if (repository == null)

      throw new NullPointerException();

    this.defaultRepository = repository;

  }

  public LoggerRepository getLoggerRepository() {

    return defaultRepository;

  }

}

15             LogManager

        LogManagerConfiguratorLoggerRepository整合在一起,它在初始化的時候(static)找到Log4J配置文件,並且將其解析到LoggerRepository中。

public class LogManager {

    private static RepositorySelector repositorySelector;

    private static boolean debug = false;

    /**

     * 默認的LoggerRepository實例。

     */

public static final LoggerRepository defaultLoggerRepository;

// 初始化部分。

    static {

       String debugProp = System.getProperty("log4j.debug");

       if(Boolean.valueOf(debugProp).booleanValue()) {

          debug = true;

       }

       if(debug) {

          System.out.println("**Start of LogManager static initializer");

       }

       Hierarchy hierarchy =  new Hierarchy(new RootLogger(Level.DEBUG));

       defaultLoggerRepository = hierarchy;

       hierarchy.setName(Constants.DEFAULT_REPOSITORY_NAME);

       repositorySelector = new DefaultRepositorySelector (defaultLoggerRepository);

       String configuratorClassName = OptionConverter.getSystemProperty(Constants.CONFIGURATOR_CLASS_KEY, null);

       String configurationOptionStr = OptionConverter.getSystemProperty(Constants.DEFAULT_CONFIGURATION_KEY, null);

       if (configurationOptionStr == null) {

          if (Loader.getResource(Constants.DEFAULT_XML_CONFIGURATION_FILE) != null) {

               configurationOptionStr = Constants.DEFAULT_XML_CONFIGURATION_FILE;

            } else if (Loader.getResource(Constants.DEFAULT_CONFIGURATION_FILE) != null) {

               configurationOptionStr = Constants.DEFAULT_CONFIGURATION_FILE;

                }

            }

        if(debug) {

            System.out.println("*** configurationOptionStr=" + configurationOptionStr);

        }

        // 整合ConfiguratorLoggerRepository部分。

        IntializationUtil.initialConfiguration(defaultLoggerRepository, configurationOptionStr, configuratorClassName);

        String repositorySelectorStr = OptionConverter.getSystemProperty("log4j.repositorySelector", null);

        if (repositorySelectorStr == null) {

            // 不做任何事情, 默認的repository已經在前面配置好了。

        } else if (repositorySelectorStr.equalsIgnoreCase("JNDI")) {

            if(debug) {

                System.out.println("*** Will use ContextJNDISelector **");

            }

            repositorySelector = new ContextJNDISelector();

            guard = new Object();

        } else {

            Object r = OptionConverter.instantiateByClassName(repositorySelectorStr, RepositorySelector.class, null);

            if (r instanceof RepositorySelector) {

                if(debug) {System.out.println("*** Using [" + repositorySelectorStr+ "] instance as repository selector.");

                }

                repositorySelector = (RepositorySelector) r;

                guard = new Object();

            } else {

                if(debug) {System.out.println("*** Could not insantiate [" + repositorySelectorStr+ "] as repository selector.");

                    System.out.println("*** Using default repository selector");

                }

                repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository);

            }

        }

        if(debug) {

            System.out.println("** End of LogManager static initializer");

        }

    }

public static Logger getLogger(String name) {

    return repositorySelector.getLoggerRepository().getLogger(name);

    }

 

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章