Apache Commons Logging 是如何决定使用哪个日志实现类的
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Apache Commons Logging 是如何决定使用哪个日志实现类的相关的知识,希望对你有一定的参考价值。
Apache Commons Logging 像 SLF4J 一样,是个通用日志框架,广泛应用在各个开源组件中。说其通用,是因为它本身只提供了简单的日志输出的实现 (org.apache.commons.logging.impl.SimpleLog和 org.apache.commons.logging.impl.NoOpLog),主要是为你统一使用其他专业日志实现的方式,让你在程序中看不到具体日志实现的代码,以配置方式解藕。
那么 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 时是在代码中声明:
在 getLog() 中是通过 getFactory() 方法获得具体的 LogFactory 实现类,究竟也体现在这个方法中,所以这里非常有必要把这个方法的代码拉出来。下面是 commons-loggin1.0.3 的 LogFactory.getFactory() 代码,在新版代码定位 LogFactory 的逻辑是一样的。
// Identify the class loader we will be using
// 找到应用自身所用的加载器
//在 WAS 5.1 下是 com.ibm.ws.classloader.CompoundClassLoader
ClassLoader contextClassLoader =
(ClassLoader)AccessController.doPrivileged(
new PrivilegedAction()
public Object run()
return getContextClassLoader();
);
// Return any previously registered factory for this class loader
// 看看是否有缓存的与此类加载器关联的 LogFactory 实例,有则返回
LogFactory factory = getCachedFactory(contextClassLoader);
if (factory != null )
return factory;
// Load properties file..
// will be used one way or another in the end.
// 加载应用的 Classpath 下的属性文件 commons-logging.properties
// FACTORY_PROPERTIES 常量值是 commons-logging.properties
// commons-logging 一般在这个文件里指定 LogFactory 的实现类
// 注意,它只是去加载这个属性文件,并不马上用里面配置的 LogFactory 类
Properties props=null ;
try
InputStream stream = getResourceAsStream(contextClassLoader,
FACTORY_PROPERTIES);
if (stream != null )
props = new Properties();
props.load(stream);
stream.close();
catch (IOException e)
catch (SecurityException e)
/**** 从下面开始就是 commons-logging 按什么顺找到 LogFactory 实现类 ****/
// First, try the system property
// 1. 查找系统属性 FACTORY_PROPERTY(org.apache.commons.logging.LogFactory)
// 的值所对应的 LogFactory 实现类
try
String factoryClass = System.getProperty(FACTORY_PROPERTY);
if (factoryClass != null )
factory = newFactory(factoryClass, contextClassLoader);
catch (SecurityException e)
; // ignore
// Second, try to find a service by using the JDK1.3 jar
// discovery mechanism. This will allow users to plug a logger
// by just placing it in the lib/ directory of the webapp ( or in
// CLASSPATH or equivalent ). This is similar with the second
// step, except that it uses the (standard?) jdk1.3 location in the jar.
// 2. 使用 JDK1.3 jar 的 Service Provider Interface(SPI) 类发现机制
// 从配置文件 SERVICE_ID(META-INF/services/org.apache.commons.logging.LogFactory)
// 的第一行读取 LogFactory 的实现类名
// 这个 META-INF 目录可以是 WebRoot 的 META-INF,也可以是 classpath 下的 META-INF 目录
if (factory == null )
try
InputStream is = getResourceAsStream(contextClassLoader,
SERVICE_ID);
if ( is != null )
// This code is needed by EBCDIC and other strange systems.
// It's a fix for bugs reported in xerces
BufferedReader rd;
try
rd = new BufferedReader( new InputStreamReader(is, "UTF-8" ));
catch (java.io.UnsupportedEncodingException e)
rd = new BufferedReader( new InputStreamReader(is));
String factoryClassName = rd.readLine();
rd.close();
if (factoryClassName != null &&
! "" .equals(factoryClassName))
factory= newFactory( factoryClassName, contextClassLoader );
catch ( Exception ex )
;
// Third try a properties file.
// If the properties file exists, it'll be read and the properties
// used. IMHO ( costin ) System property and JDK1.3 jar service
// should be enough for detecting the class name. The properties
// should be used to set the attributes ( which may be specific to
// the webapp, even if a default logger is set at JVM level by a
// system property )
// 3. 现在才轮到用前面加载的 commons-logging.properties 文件中的
// FACTORY_PROPERTY(org.apache.commons.logging.LogFactory) 属性指定的 LogFactory 实现类
if (factory == null && props != null )
String factoryClass = props.getProperty(FACTORY_PROPERTY);
if (factoryClass != null )
factory = newFactory(factoryClass, contextClassLoader);
// Fourth, try the fallback implementation class
// 4. 前面几步没有找到 LogFactory 的实现类或有异常的话就用默认的实现类
// 即 LogFactory 为我们准备的 FACTORY_DEFAULT(org.apache.commons.logging.impl.LogFactoryImpl)
if (factory == null )
factory = newFactory(FACTORY_DEFAULT, LogFactory.class .getClassLoader());
if (factory != null )
/**
* Always cache using context class loader..
* 缓存所用的实现类,以后直接使用缓冲中的 LogFactory 实现类
*/
cacheFactory(contextClassLoader, factory);
if ( props!= null )
Enumeration names = props.propertyNames();
while (names.hasMoreElements())
String name = (String) names.nextElement();
String value = props.getProperty(name);
factory.setAttribute(name, value);
return factory;
在代码中,我已加上注释,有缓存的 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 怎么就不能输出日志了呢?
最原始的日志方式,就是在程序的适当地方添加System.out.println()方法,但是带来 的问题是,系统稳定后,日志太大,如果要减少日志量,就需要重新修改程序,虽然只 是注释掉System.out.println()方法。但是万一系统再次出错,又要改。
commons-logging提供分级日志功能,相当于把System.out.println()方法加强了,原来 是一定输出到控制台,现在commons-logging有6级日志,但是apache建议使用4级,即 ERROR、WARN、INFO、DEBUG。
org.apache.commons.logging.LogFactory 错误原因
上述问题的原因是因为在使用Spring框架时没有导入这个包导致的,导入这个包就ok了。
以上是关于Apache Commons Logging 是如何决定使用哪个日志实现类的的主要内容,如果未能解决你的问题,请参考以下文章
错误:NoClassDefFoundError:org / apache / commons / logging / LogFactory
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory解决方法
获取 java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory 异常
Apache Commons Logging 是如何决定使用哪个日志实现类的
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory