深入log4j源码

简介:

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

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

 

Xml代码   收藏代码
  1. <context-param>     
  2.       <param-name>log4jConfigLocation</param-name>     
  3.       <param-value>WEB-INF/log4j.properties</param-value>     
  4.   </context-param>     
  5.     
  6.   <context-param>     
  7.       <param-name>log4jRefreshInterval</param-name>     
  8.       <param-value>6000</param-value>     
  9.   </context-param>     
  10.     
  11.   <listener>     
  12.       <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>     
  13.   </listener>    

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

 

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

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

 log4j初始化

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

 

Java代码   收藏代码
  1. static {  
  2.     // By default we use a DefaultRepositorySelector which always returns 'h'.  
  3.     Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));  
  4.     repositorySelector = new DefaultRepositorySelector(h);  
  5.   
  6.     /** Search for the properties file log4j.properties in the CLASSPATH.  */  
  7.     String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,  
  8.                                null);  
  9.   
  10.     // if there is no default init override, then get the resource  
  11.     // specified by the user or the default config file.  
  12.     if(override == null || "false".equalsIgnoreCase(override)) {  
  13.   
  14.       String configurationOptionStr = OptionConverter.getSystemProperty(  
  15.                               DEFAULT_CONFIGURATION_KEY,   
  16.                               null);  
  17.   
  18.       String configuratorClassName = OptionConverter.getSystemProperty(  
  19.                                                    CONFIGURATOR_CLASS_KEY,   
  20.                            null);  
  21.   
  22.       URL url = null;  
  23.   
  24.       // if the user has not specified the log4j.configuration  
  25.       // property, we search first for the file "log4j.xml" and then  
  26.       // "log4j.properties"  
  27.       if(configurationOptionStr == null) {      
  28.     url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);  
  29.     if(url == null) {  
  30.       url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);  
  31.     }  
  32.       } else {  
  33.     try {  
  34.       url = new URL(configurationOptionStr);  
  35.     } catch (MalformedURLException ex) {  
  36.       // so, resource is not a URL:  
  37.       // attempt to get the resource from the class path  
  38.       url = Loader.getResource(configurationOptionStr);   
  39.     }      
  40.       }  
  41.         
  42.       // If we have a non-null url, then delegate the rest of the  
  43.       // configuration to the OptionConverter.selectAndConfigure  
  44.       // method.  
  45.       if(url != null) {  
  46.         LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");  
  47.         try {  
  48.             OptionConverter.selectAndConfigure(url, configuratorClassName,  
  49.                        LogManager.getLoggerRepository());  
  50.         } catch (NoClassDefFoundError e) {  
  51.             LogLog.warn("Error during default initialization", e);  
  52.         }  
  53.       } else {  
  54.         LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");  
  55.       }  
  56.     } else {  
  57.         LogLog.debug("Default initialization of overridden by " +   
  58.             DEFAULT_INIT_OVERRIDE_KEY + "property.");   
  59.     }    
  60.   }  

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

 

第一步:创建一个默认的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操作相关的方法和属性。

 

Java代码   收藏代码
  1. public interface 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时就可以直接取得而不用每次都计算。

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

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

 

Java代码   收藏代码
  1. /** Search for the properties file log4j.properties in the CLASSPATH.  */  
  2.     String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null);   
  3. //public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";  

 

Java代码   收藏代码
  1. publicstatic String getSystemProperty(String key, String def) {  
  2.     try {  
  3.       return System.getProperty(key, def);  
  4.     } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx  
  5.       LogLog.debug("Was not allowed to read system property \""+key+"\".");  
  6.       return def;  
  7.     }  
  8.   }  

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

 

 

Java代码   收藏代码
  1. if(configurationOptionStr == null) {      
  2.     url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);  
  3.     if(url == null) {  
  4.       url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);  
  5.     }  

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

 

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

 

Java代码   收藏代码
  1. try {  
  2.       url = new URL(configurationOptionStr);  
  3.     } catch (MalformedURLException ex) {  
  4.       // so, resource is not a URL:  
  5.       // attempt to get the resource from the class path  
  6.       url = Loader.getResource(configurationOptionStr);   
  7.     }  

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

 

Java代码   收藏代码
  1. // If we have a non-null url, then delegate the rest of the  
  2.       // configuration to the OptionConverter.selectAndConfigure  
  3.       // method.  
  4.       if(url != null) {  
  5.         LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");  
  6.         try {  
  7.             OptionConverter.selectAndConfigure(url, configuratorClassName,  
  8.                        LogManager.getLoggerRepository());  
  9.         } catch (NoClassDefFoundError e) {  
  10.             LogLog.warn("Error during default initialization", e);  
  11.         }  
  12.       } else {  
  13.         LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");  
  14.       }  

 

原文链接:[http://wely.iteye.com/blog/2287667]

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
安全 Java
Log4j2漏洞分析源码篇
Log4j2漏洞分析源码篇
343 0
Log4j2漏洞分析源码篇
|
XML Java 测试技术
|
4月前
|
Java Spring
spring boot 配置log4j 日志
spring boot 配置log4j 日志
69 0
|
3月前
|
Java Spring
【Spring Boot】logback和log4j日志异常处理
【1月更文挑战第25天】【Spring Boot】logback和log4j日志异常处理
|
5月前
|
XML JSON Java
最牛逼 Java 日志框架—Log4j2,性能无敌,横扫对手
Logback 算是JAVA 里一个老牌的日志框架,从06年开始第一个版本,迭代至今也十几年了。不过logback最近一个稳定版本还停留在 2017 年,好几年都没有更新;logback的兄弟 slf4j 最近一个稳定版也是2017年,有点凉凉的意思。