首页 > 其他分享 >Apache Commons Logging 是如何决定使用哪个日志实现类的

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

时间:2023-08-29 18:36:13浏览次数:26  
标签:logging Commons commons apache LogFactory org Apache Logging null


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
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
9. public
10. return
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
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. null
29. try
30.         InputStream stream = getResourceAsStream(contextClassLoader,  
31.                                                  FACTORY_PROPERTIES);  
32.   
33. if  (stream !=  null
34. new
35.             props.load(stream);  
36.             stream.close();  
37.         }  
38. catch
39. catch
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
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. new  BufferedReader( new  InputStreamReader(is,  "UTF-8"
79. catch
80. new  BufferedReader( new
81.                 }  
82.                   
83.                 String factoryClassName = rd.readLine();  
84.                 rd.close();  
85.                   
86. if  (factoryClassName !=  null
87. ""
88.                       
89.                     factory= newFactory( factoryClassName, contextClassLoader );  
90.                 }  
91.             }  
92. catch
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. class
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
134.                 String name = (String) names.nextElement();  
135.                 String value = props.getProperty(name);  
136.                 factory.setAttribute(name, value);  
137.             }  
138.         }  
139.     }  
140.       
141. return
142. }

Java代码

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


在代码中,我已加上注释,有缓存的 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 通用日志框架时,若出现什么问应具体情况具体分析,相信都不会变离本篇中能解释的情况。

标签:logging,Commons,commons,apache,LogFactory,org,Apache,Logging,null
From: https://blog.51cto.com/u_16240770/7278998

相关文章

  • 16、Flink 的table api与sql之连接外部系统_ 读写外部系统的连接器和格式以及Apache H
    (文章目录)本文介绍了ApacheHive连接器的使用,以具体的示例演示了通过java和flinksqlcli创建catalog。本文依赖环境是hadoop、zookeeper、hive、flink环境好用,本文内容以flink1.17版本进行介绍的,具体示例是在1.13版本中运行的(因为hadoop集群环境是基于jdk8的,flink1.17版本需......
  • 实操教程 | 触发器实现 Apache DolphinScheduler 失败钉钉自动告警
    作者|sqlboy-yuzhenc背景介绍在实际应用中,我们经常需要将特定的任务通知给特定的人,虽然ApacheDolphinScheduler在安全中心提供了告警组和告警实例,但是配置起来相对复杂,并且还需要在定时调度时指定告警组。通过这篇文章,你将学到一个简单的方法,无需任何配置,只需要在用户表(t_......
  • 双缓冲异步日志(Async Logging)
    文章目录一、日志系统简介二、功能需求三、性能需求四、高效的异步日志1、异步日志的概念2、双缓冲异步日志解析3、AsyncLogging源码4、代码运行图示五、双缓冲异步日志的相关问题一、日志系统简介日志通常用于故障诊断和追踪(trace),也可用于性能分析。日志通常是分布式系统中事......
  • Apache RocketMQ 5.0 消息进阶:如何支撑复杂的业务消息场景?
    作者:隆基一致性首先来看RocketMQ的第一个特性-事务消息,事务消息是RocketMQ与一致性相关的特性,也是RocketMQ有别于其他消息队列的最具区分度的特性。以大规模电商系统为例,付款成功后会在交易系统中订单数据库将订单状态更新为已付款。然后交易系统再发送一条消息给Rocke......
  • centos7.9+php+apache 配置阿里云ssl
     1、前往阿里云官网申请免费证书选择 2、证书存放在/etc/cert 3、httpd.cof配置ssl<VirtualHost_default_:443> ServerName域名 DocumentRoot网站根目录 SSLEngineon SSLProtocolall-SSLv2-SSLv3 SSLCipherSuiteHIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:......
  • 使用Apache IoTDB进行IoT相关开发的架构设计与功能实现(11)
    账户管理报表IoTDB可以为用户提供账号权限管理操作,保障数据安全。接下来我将通过以下具体示例向朋友们展示基本的用户权限管理操作,介绍详细的SQL语法和用法详细信息。基本概念用户用户是数据库的合法用户。用户对应于唯一的用户名,并具有密码作为身份验证方式。在使用数据库之前,一......
  • Apache SeaTunnel 2.3.3 版本发布,CDC 支持 Schema Evolution!
    时隔两个月,ApacheSeaTunnel终于迎来大版本更新。此次发布的2.3.3版本在功能和性能上均有较大优化改进,其中大家期待已久的CDCSchemaevolution(DDL变更同步)、主键Split拆分、JDBCSink自动建表功能、SeaTunnelZeta引擎支持作业配置支持变量替换和传参等都是更新的亮......
  • ETL之apache hop系列3-hop Server环境部署与客户端发布管道工作流
    前言该文档主要是apachehop2.5的Windows10和Linuxdocker环境部署和客户端发布工作流和管道的相关内容不使用Docker直接使用应用程序包,下载压缩包文件后,需要解压缩文件并使用以下命令启动HopGUI:在Linux上:./hop-gui.sh在Windows上(需要JDK11):Hop-gui.bat一、Windo......
  • ubuntu15.04下用apache+iasp部署asp+mssqlserver运行环境
    最近由于工作需要,尝试了一下在linux下配置asp运行环境,耗时两周,在网上能找到的相关资料和软件都比较匮缺,特记录一下。一、硬件环境及操作系统windows2008+vm,win2008安装了sql2005作为数据库服务器,vm虚拟机安装ubuntu15.04用apache2.0.59+iasp2.1作为webserver二、ubuntu15.04安......
  • 使用Apache IoTDB进行IoT相关开发的架构设计与功能实现(10)
    所有参数的详细说明见表3-5。表3-5线性填充参数列表参数名称(不区分大小写)解释路径,前缀路径查询路径;必填项T查询时间戳(只能指定一个);必填项data_type填充方法使用的数据类型。可选值为int32、int64、浮点型、双精度型、布尔值、文本;可选字段before_range,after_range表示线性方......