`

Apache Commons Logging 是如何决定使用哪个日志实现类的

    博客分类:
  • java
阅读更多

原文是:http://www.blogjava.net/Unmi/archive/2009/05/14/270708.html

Apache Commons Logging 像 SLF4J 一样,是个通用日志框架,广泛应用在各个开源组件中。说其通用,是因为它本身只提供了简单的日志输出的实现 (org.apache.commons.logging.impl.SimpleLog和 org.apache.commons.logging.impl.NoOpLog),主要是为你统一使用其他专业日志实现(Log4j、jdk1.4 Logger、aavalon-Logkit)的方式,让你在程序中看不到具体日志实现的代码,以配置方式解藕。

那么 commons-logging 是怎么决定程序执行时该使用哪个具体的日志实现呢?这里 commons-logging 有两个步骤要做:

1. 定位 org.apache.commons.logging.LogFactory 的实现类(这一步是关键)
2. 定位到的 LogFactory 实现类决定使用哪个 org.apache.commons.logging.Log 实现

那现在我们把注意力主要集中在 commons-logging 如何定位 LogFactory 实现类上来。org.apche.commons.logging.LogFactory 是一个抽象类,所以需要一个 LogFactory 具体类。

通常我们用使用 commons-logging 时是在代码中声明:

Log log = LogFactory.getLog(UnmiTestLog.class);

在 getLog() 中是通过 getFactory() 方法获得具体的 LogFactory 实现类,究竟也体现在这个方法中,所以这里非常有必要把这个方法的代码拉出来。下面是 commons-loggin1.0.3 的 LogFactory.getFactory() 代码,在新版代码定位 LogFactory 的逻辑是一样的。

  1. public   static  LogFactory getFactory()  throws  LogConfigurationException {  
  2.   
  3.     // Identify the class loader we will be using   
  4.     // 找到应用自身所用的加载器   
  5.     //在 WAS 5.1 下是 com.ibm.ws.classloader.CompoundClassLoader   
  6.     ClassLoader contextClassLoader =     
  7.         (ClassLoader)AccessController.doPrivileged(  
  8.             new  PrivilegedAction() {  
  9.                 public  Object run() {  
  10.                     return  getContextClassLoader();  
  11.                 }  
  12.             });  
  13.   
  14.   
  15.     // Return any previously registered factory for this class loader   
  16.     // 看看是否有缓存的与此类加载器关联的 LogFactory 实例,有则返回   
  17.     LogFactory factory = getCachedFactory(contextClassLoader);  
  18.     if  (factory !=  null )  
  19.         return  factory;  
  20.   
  21.   
  22.     // Load properties file..   
  23.     // will be used one way or another in the end.   
  24.     // 加载应用的 Classpath 下的属性文件 commons-logging.properties   
  25.     // FACTORY_PROPERTIES 常量值是 commons-logging.properties   
  26.     // commons-logging 一般在这个文件里指定 LogFactory 的实现类   
  27.     // 注意,它只是去加载这个属性文件,并不马上用里面配置的 LogFactory 类   
  28.     Properties props=null ;  
  29.     try  {  
  30.         InputStream stream = getResourceAsStream(contextClassLoader,  
  31.                                                  FACTORY_PROPERTIES);  
  32.   
  33.         if  (stream !=  null ) {  
  34.             props = new  Properties();  
  35.             props.load(stream);  
  36.             stream.close();  
  37.         }  
  38.     } catch  (IOException e) {  
  39.     } catch  (SecurityException e) {  
  40.     }  
  41.   
  42.     /**** 从下面开始就是 commons-logging 按什么顺找到 LogFactory 实现类 ****/   
  43.   
  44.     // First, try the system property   
  45.     // 1. 查找系统属性 FACTORY_PROPERTY(org.apache.commons.logging.LogFactory)   
  46.     // 的值所对应的 LogFactory 实现类   
  47.       
  48.     try  {  
  49.         String factoryClass = System.getProperty(FACTORY_PROPERTY);  
  50.         if  (factoryClass !=  null ) {  
  51.             factory = newFactory(factoryClass, contextClassLoader);  
  52.         }  
  53.     } catch  (SecurityException e) {  
  54.         ;  // ignore   
  55.     }  
  56.   
  57.   
  58.     // Second, try to find a service by using the JDK1.3 jar   
  59.     // discovery mechanism. This will allow users to plug a logger   
  60.     // by just placing it in the lib/ directory of the webapp ( or in   
  61.     // CLASSPATH or equivalent ). This is similar with the second   
  62.     // step, except that it uses the (standard?) jdk1.3 location in the jar.   
  63.     // 2. 使用 JDK1.3 jar 的 Service Provider Interface(SPI) 类发现机制   
  64.     // 从配置文件 SERVICE_ID(META-INF/services/org.apache.commons.logging.LogFactory)   
  65.     // 的第一行读取 LogFactory 的实现类名   
  66.     // 这个 META-INF 目录可以是 WebRoot 的 META-INF,也可以是 classpath 下的 META-INF 目录   
  67.   
  68.     if  (factory ==  null ) {  
  69.         try  {  
  70.             InputStream is = getResourceAsStream(contextClassLoader,  
  71.                                                  SERVICE_ID);  
  72.   
  73.             if ( is !=  null  ) {  
  74.                 // This code is needed by EBCDIC and other strange systems.   
  75.                 // It's a fix for bugs reported in xerces   
  76.                 BufferedReader rd;  
  77.                 try  {  
  78.                     rd = new  BufferedReader( new  InputStreamReader(is,  "UTF-8" ));  
  79.                 } catch  (java.io.UnsupportedEncodingException e) {  
  80.                     rd = new  BufferedReader( new  InputStreamReader(is));  
  81.                 }  
  82.                   
  83.                 String factoryClassName = rd.readLine();  
  84.                 rd.close();  
  85.                   
  86.                 if  (factoryClassName !=  null  &&  
  87.                     ! "" .equals(factoryClassName)) {  
  88.                       
  89.                     factory= newFactory( factoryClassName, contextClassLoader );  
  90.                 }  
  91.             }  
  92.         } catch ( Exception ex ) {  
  93.             ;  
  94.         }  
  95.     }  
  96.   
  97.   
  98.     // Third try a properties file.    
  99.     // If the properties file exists, it'll be read and the properties   
  100.     // used. IMHO ( costin ) System property and JDK1.3 jar service   
  101.     // should be enough for detecting the class name. The properties   
  102.     // should be used to set the attributes ( which may be specific to   
  103.     // the webapp, even if a default logger is set at JVM level by a   
  104.     // system property )   
  105.     // 3. 现在才轮到用前面加载的 commons-logging.properties 文件中的   
  106.     // FACTORY_PROPERTY(org.apache.commons.logging.LogFactory) 属性指定的 LogFactory 实现类   
  107.   
  108.     if  (factory ==  null   &&  props !=  null ) {  
  109.         String factoryClass = props.getProperty(FACTORY_PROPERTY);  
  110.         if  (factoryClass !=  null ) {  
  111.             factory = newFactory(factoryClass, contextClassLoader);  
  112.         }  
  113.     }  
  114.   
  115.   
  116.     // Fourth, try the fallback implementation class   
  117.     // 4. 前面几步没有找到 LogFactory 的实现类或有异常的话就用默认的实现类   
  118.     // 即 LogFactory 为我们准备的 FACTORY_DEFAULT(org.apache.commons.logging.impl.LogFactoryImpl)   
  119.   
  120.     if  (factory ==  null ) {  
  121.         factory = newFactory(FACTORY_DEFAULT, LogFactory.class .getClassLoader());  
  122.     }  
  123.       
  124.     if  (factory !=  null ) {  
  125.         /**  
  126.          * Always cache using context class loader..  
  127.          * 缓存所用的实现类,以后直接使用缓冲中的 LogFactory 实现类  
  128.          */   
  129.         cacheFactory(contextClassLoader, factory);  
  130.   
  131.         if ( props!= null  ) {  
  132.             Enumeration names = props.propertyNames();  
  133.             while  (names.hasMoreElements()) {  
  134.                 String name = (String) names.nextElement();  
  135.                 String value = props.getProperty(name);  
  136.                 factory.setAttribute(name, value);  
  137.             }  
  138.         }  
  139.     }  
  140.       
  141.     return  factory;  
  142. }  



在代码中,我已加上注释,有缓存的 LogFactory 实现类,取缓存中的,注意缓存是与当前应用的类加载器关联的。若缓存中没有的话按 1、2、3、4 的顺序来找,现在就来说说查找 LogFactory 的顺序:


1. 从系统属性中查找键为 org.apache.commons.logging.LogFactory 的值作为 LogFactory 的实现类;却通过 System.getProperty("org.apache.commons.logging.LogFactory") 获得

2.  使用 JDK1.3 jar 的 Service Provider Interface(SPI) 类发现机制,从配置文件 META-INF/services/org.apache.commons.logging.LogFactory 的的第一行读取 LogFactory 的实现类名。这个 META-INF/services/org.apache.commons.logging.LogFactory 文件可以是某个 Web 应用的根目录中;也可以在 classpath 下,如某个 Jar 包中,WebRoot/WEB-INF/classes 中等。这里需多加留心下 META-INF/services/org.apache.commons.logging.LogFactory 这个目录层次及文件名。

3.  在 Classpath 下的 commons-logging.properties 文件中的,找到 org.apache.commons.logging.LogFactory 属性值作为 LogFactory 实现类

4. 前面三步未找个 LogFactory 的实现类,或有任何异常的情况下,就用默认的实现类,即 LogFactory 为我们准备的 org.apache.commons.logging.impl.LogFactoryImpl



明白了以上的顺序,可以帮助我们理解和解决一些实际的问题,例如,为什么可以不用 commons-logging.properties 也是使用的 log4j 日志实现,部署在 WAS 下的应用 log4j 怎么就不能输出日志了呢?

一 般,某个具体的 LogFactory 类对应就会使用与其相应的 Logger 实现,如 Log4jFactory.getLog() 得到的是 Log4JLogger 实例,WAS 的 TrLogFactory.getLog() 返回的是 TrLog 实例。

老师们教我们用 commons-logging 时也许会让我们在 classpath 下放一个 commons-logging.properties 文件,并在这个文件中写上一行:

org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.Log4jFactory

Log4jFactory 已不推荐使用,新的建议的用法是 LogFactory 统一用 LogFactoryImpl,然后在 LogFactoryImpl 中决定声明哪个 Log 实现类

或者是这么两行:

org.apache.cmmons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger


然 而我们基本都是用的 Log4j 来输出日志,其实不管 commons-logging.properties 是第一种写法还是第二种写法或许(有时不是) 都是多余的,回望 LogFactory.getFactory() 方法,还要再看看 org.apache.commons.logging.impl.LogFactoryImpl 的 getLogClassName() 方法便可知。

LogFactory.getFactory() 在前面三步找不到 LogFactory 实现类时,就会用默认的 LogFactoryImpl,而默认的 LogFactoryImpl.getLog() 时,又会根据以下四个顺序来决定返回什么 Log 实例(Log 实例对应到实际的日志实现),在 LogFactoryImpl.getLogClassName() 中体现了:

1. commons-logging.properties 中的 org.apache.commons.logging.Log 指定的 Log 实现类
2. Log4j 可用就用 org.apache.commons.logging.impl.Log4JLogger
3. Jdk1.4 Logger 可用就用 org.apcache.commons.logging.impl.Jdk14Logger(JDK1.4 开始自带)
4. SimpleLog 可用就用 org.apache.commons.logging.impl.SimpleLog(commons-logging 自带)

所 以这就是为什么了,使用了 commons-logging 的框架类,只要扔个 log4j 的 jar,根本不用 commons-logging.properties 文件就会用 log4j 来输出日志,当然 log4j 自己的配置文件 log4j.xml 或 log4j.properties 是需要的。

那为什么在 Tomcat 或别的应用服务器中 log4j  能正常输出日志,一放到 WAS 5 下却不灵了呢?原因是在 $WAS_HOME/lib/ws-commons-logging.jar 中有个文件 commons-logging.properties,其中有一行 org.apache.commons.logging.LogFactory=com.ibm.ws.commons.logging.TrLogFactory, 虽然你的应用中可能也有一个 commons-logging.properties,可是很不幸,WAS  自己的 commons-logging.properties 优先了,原因是类加载器的委托机制在作用,所以最终 log4j 没派上用场,被 com.ibm.ws.commons.logging.TrLog 取而代之了,解决办法是要抢在它之前,比系统属性中声明 LogFactory 实现类,或是在 META-INF/services/org.apache.commons.logging.LogFactory 中指名 org.apache.commons.logging.impl.Log4jFactory 或 org.apache.commons.logging.impl.LogFactoryImpl 作为实现类名。

以后在使用 commons-logging 通用日志框架时,若出现什么问应具体情况具体分析,相信都不会变离本篇中能解释的情况。

分享到:
评论

相关推荐

    Apache Commons-logging使用实例

    * org.apache.commons.logging.impl.SimpleLog common-logging 自带日志实现类 * org.apache.commons.logging.impl.NoOpLog common-logging 自带日志实现类 3. 使用 JCL 开发 由于 Log4j 的强大,同时开发者又不...

    apache-commons-logging.zip

    Apache Commons Logging常与其他日志框架一起使用,例如Logback(作为Log4j的替代品)或SLF4J(Simple Logging Facade for Java),后者提供了一种更现代且灵活的日志记录接口,可以透明地桥接到各种日志实现,包括...

    commons-logging-1.2_commonslogging_

    在标题"commons-logging-1.2_commonslogging_"中提到的"commons-logging-1.2.jar"就是这个库的1.2版本,它是Spring框架中常用的一个依赖,用于处理日志记录。 Spring框架广泛使用Commons Logging作为其默认的日志...

    org.apache.commons.logging-sources-1.1.1.zip

    Apache Commons Logging 是一个Java日志库,它提供了一个接口,允许开发者使用多种不同的日志框架,如Log4j、Java内置的日志API(java.util.logging)或其他第三方日志实现。`org.apache.commons.logging-sources-...

    apache-commons-logging.jar.zip

    Apache Commons Logging 提供了一个API,开发人员可以使用这个API编写日志语句,然后在运行时通过配置来决定实际的日志实现,如Log4j、java.util.logging(也称为JUL)或简单的控制台日志。 标题中的"apache-...

    commons-logging-1.2

    Commons Logging 是 Apache 组织提供的一款轻量级的日志记录工具库,它的主要目标是为 Java 开发者提供一个简单的接口来使用各种日志框架,如 Log4j、Java Util Logging(JUL)或者 Simple Logging Facade for Java...

    commons-logging-1.2-bin.zip下载

    Commons Logging 提供了一组接口和辅助类,使得应用程序可以透明地使用任何兼容的日志实现,如Log4j、Java内置的日志API(java.util.logging)或者简单的控制台输出。这个库的主要优点是灵活性和可插拔性,开发者...

    Apache Commons Logging整合Log4j简单例子

    Apache Commons Logging(ACL)是Java中一个轻量级的日志API,它允许开发者在不同的日志实现之间进行切换,而无需修改代码。这个API的主要目的是为了统一不同库的日志输出,使得应用开发者可以选择最适合他们项目的...

    commons-logging-1.2.jar

    使用Commons Logging时,开发者不需要直接实例化特定日志系统的类,而是通过Commons Logging提供的接口(如`org.apache.commons.logging.Log`和`org.apache.commons.logging.LogFactory`)来创建和使用日志对象。...

    commons-logging-1.2-bin2014最新版

    Apache Commons Logging 是一个Java日志框架的抽象层,它允许应用程序使用多种不同的日志实现,如Log4j、Java Util Logging或JDK14 Logging。标题提到的"commons-logging-1.2-bin2014最新版"是Apache Commons ...

    commons-logging-1.2.JAR开源包

    Apache Commons Logging,简称为Commons Logging,是Apache软件基金会开发的一个开源日志框架,主要用于提供一个统一的日志API,让开发者能够在不改变代码的情况下,自由切换不同的日志实现库,如Log4j、Java内置的...

    commons logging

    如果想要使用其他日志系统,比如log4j,需要在应用的类路径中包含相应的日志实现,并配置 Commons Logging 以使用这个实现。这通常通过设置系统属性(例如`java.util.logging.config.file`或`log4j.configuration`)...

    commons-logging-1.1.1.rar

    Commons Logging虽然在某些情况下可能存在性能问题,因为它是基于查找机制来决定使用哪个日志实现,但其灵活性和便利性使其在很多项目中被广泛应用。然而,随着Java社区的发展,其他更现代的日志框架如SLF4J和...

    commons-logging-1.2-bin.zip

    Apache Commons Logging,简称为Commons Logging,是Apache软件基金会开发的一个开源日志框架,主要用于提供一个统一的日志接口,使得Java应用程序可以在不改变代码的情况下,自由地切换不同的日志实现库,如Log4j、...

    slf4j与commons-logging处理日志

    Apache Commons Logging是另一个日志抽象层,它的设计理念与SLF4J类似,允许在运行时动态选择日志实现。不过,相比SLF4J,Commons Logging在某些方面存在一些局限性,如性能问题和类加载器问题。尽管如此,它曾是...

    commons-logging-1.1.3-bin.zip

    在实际使用中,Commons Logging提供了一系列的Logger接口,如`org.apache.commons.logging.Log`和`org.apache.commons.logging.LogFactory`。通过`LogFactory.getLog(Class)`方法,可以获取与当前类关联的Logger实例...

    日志框架commons-logging springMVC必备

    这个库文件包含了所有必要的类和接口,使得我们可以调用如`org.apache.commons.logging.Log`和`org.apache.commons.logging.LogFactory`等接口来创建和使用日志对象。例如,我们可以在Spring MVC的控制器中这样写...

    commons-logging-1.1.3-jar包

    2. **自动检测日志实现**:在运行时,Commons Logging会尝试自动检测可用的日志实现,例如Log4j、Java内置日志(java.util.logging.Logger)、Jakarta Commons Logging自身的SimpleLog等。这通过检查类路径中的特定...

Global site tag (gtag.js) - Google Analytics