深入log4j源码

 

 slf4j即Simple logging facade for Java,其作用类似于JDBC,作为一个日志抽象层,它允许你在后台使用任意一个日志类库,比如log4j。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。常用的日志类库有log4j、logback等,本文就来深入了解一下log4j。

 开发代码中,我们只需要在src下放上log4j.xml或log4j.properties,log4j就能自动找到该配置文件,web工程在spring环境下,我们还可以在web.xml中自定义上述配置文件的路径。

<context-param>   
      <param-name>log4jConfigLocation</param-name>   
      <param-value>WEB-INF/log4j.properties</param-value>   
  </context-param>   
  
  <context-param>   
      <param-name>log4jRefreshInterval</param-name>   
      <param-value>6000</param-value>   
  </context-param>   
  
  <listener>   
      <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>   
  </listener>  

 那么log4j默认是怎么加载配置文件的呢?每一个Logger实例在log里是怎么安排存放的呢?

 Log4j由三个重要的组成构成:日志记录器(Loggers),输出端(Appenders)和日志格式化器(Layout)。
1.日志记录器(Loggers):控制要输出哪些日志记录语句,对日志信息进行级别限制。
2.输出端(Appenders):指定了日志将打印到控制台还是文件中。

3.日志格式化器(Layout):控制日志信息的显示格式。

 log4j初始化

从架构上我们可以看出logger的实现是从logManager来具体完成的,因此初始化在logManager的static块中,如下:

 

扫描二维码关注公众号,回复: 665767 查看本文章
static {
    // By default we use a DefaultRepositorySelector which always returns 'h'.
    Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
    repositorySelector = new DefaultRepositorySelector(h);

    /** Search for the properties file log4j.properties in the CLASSPATH.  */
    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
                               null);

    // if there is no default init override, then get the resource
    // specified by the user or the default config file.
    if(override == null || "false".equalsIgnoreCase(override)) {

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

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

      URL url = null;

      // if the user has not specified the log4j.configuration
      // property, we search first for the file "log4j.xml" and then
      // "log4j.properties"
      if(configurationOptionStr == null) {    
    url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
    if(url == null) {
      url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
    }
      } else {
    try {
      url = new URL(configurationOptionStr);
    } catch (MalformedURLException ex) {
      // so, resource is not a URL:
      // attempt to get the resource from the class path
      url = Loader.getResource(configurationOptionStr); 
    }    
      }
      
      // If we have a non-null url, then delegate the rest of the
      // configuration to the OptionConverter.selectAndConfigure
      // method.
      if(url != null) {
        LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
        try {
            OptionConverter.selectAndConfigure(url, configuratorClassName,
                       LogManager.getLoggerRepository());
        } catch (NoClassDefFoundError e) {
            LogLog.warn("Error during default initialization", e);
        }
      } else {
        LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
      }
    } else {
        LogLog.debug("Default initialization of overridden by " + 
            DEFAULT_INIT_OVERRIDE_KEY + "property."); 
    }  
  }

 让我们深入进去看看是怎么初始化的?

第一步:创建一个默认的RepositorySelector,RepositorySelector在logManager中使用,它的实现类对特定的应用上下文提供了一个LoggerRespository。LoggerRespository的实现类负责追踪应用上下文。ResponsitorySelector提供了一个方法:

public LoggerRepository getLoggerRepository();

LoggerRepository从字面上理解,它是一个Logger的容器,它会创建并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。它的这种特性有点类似Spring的IOC概念。Log4J支持两种配置文件:properties文件和xml文件。Configurator解析配置文件,并将解析后的信息添加到LoggerRepository中。LogManager最终将LoggerRepository和Configurator整合在一起。

LoggerRepository同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以’.’分隔。

除了作为一个Logger容器,它还有一个Threshold属性,用于过滤所有在Threshold级别以下的日志。以及其他和Logger操作相关的方法和属性。

 

public interface LoggerRepository {
public void addHierarchyEventListener(HierarchyEventListener listener);
 boolean isDisabled(int level);
 public void setThreshold(Level level);
 public void setThreshold(String val);
public void emitNoAppenderWarning(Category cat);
 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();
}

 Hierarchy类

Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现,因而首先从这个方法开始。Hierarchy中用一个Hashtable来存储所有Logger实例,它以CategoryKey作为key,Logger作为value,其中CategoryKey是对Logger中Name字符串的封装,之所以要引入这个类是出于性能考虑,因为它会缓存Name字符串的hash code,这样在查找过程中计算hash code时就可以直接取得而不用每次都计算。

第二步:从classpath路径查找log4j.properties属性配置文件。

   1. 判断配置文件有没有重写?

 

/** Search for the properties file log4j.properties in the CLASSPATH.  */
    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null); 
//public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
publicstatic String getSystemProperty(String key, String def) {
    try {
      return System.getProperty(key, def);
    } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
      LogLog.debug("Was not allowed to read system property \""+key+"\".");
      return def;
    }
  }

 2. override为null或者为false时没有重写,然后从系统属性中查找key:log4j.configuration和log4j.configuratorClass属性。如果没有设置log4j.configuration属性,就查找log4j.xml,然后查找log4j.properties文件。使用方法:

 

if(configurationOptionStr == null) {    
    url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
    if(url == null) {
      url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
    }

 使用java2的线程上下文类加载器来查找资源,如果查找失败,则使用加载该类(loader)的类加载器来加载资源。

3.如果设置了log4j.configuration属性,则使用url形式读取,如果资源不是url,则从classpath获取资源:

 

try {
      url = new URL(configurationOptionStr);
    } catch (MalformedURLException ex) {
      // so, resource is not a URL:
      // attempt to get the resource from the class path
      url = Loader.getResource(configurationOptionStr); 
    }

 4. 如果log4j.configuration属性为url形式:

// If we have a non-null url, then delegate the rest of the
      // configuration to the OptionConverter.selectAndConfigure
      // method.
      if(url != null) {
        LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
        try {
            OptionConverter.selectAndConfigure(url, configuratorClassName,
                       LogManager.getLoggerRepository());
        } catch (NoClassDefFoundError e) {
            LogLog.warn("Error during default initialization", e);
        }
      } else {
        LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
      }

 

参考文献:

1. http://www.2cto.com/kf/201207/139798.html  - 深入Log4j源码之LoggerRepository和Configurator

2. http://logging.apache.org/log4j/1.2/manual.html

3. whatsmars.com

猜你喜欢

转载自wely.iteye.com/blog/2287667
今日推荐