【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
也许大家看到这个题目的时候,会想,commons-logging对log4j做了很好的支持,为什么会找不到呢!
但是我想告诉大家的事,今天由于我对Tomcat下lib的理解不深,就出现了一个这样让人费解的现象:
前提:
1. classPath下配置了log4j.properties文件
操作:
1. org.apache.commons.logging.LogFactory.getLog(key)获取的Log
2. 通过Log.getName(),发现是org.apache.commons.logging.impl.Jdk14Logger
于是,我在网上查找了一些资料,得到了一致的观点(来源:http://javacrazyer.iteye.com/blog/1135493):
(1).common-logging首先在CLASSPATH中查找commons-logging.properties文件。这个属性文件至少定义org.apache.commons.logging.Log属性,它的值应该是上述任意Log接口实现的完整限定名称。如果找到 org.apache.commons.logging.Log属相,则使用该属相对应的日志组件。结束发现过程。
(2).如果上面的步骤失败(文件不存在或属相不存在),common-logging接着检查系统属性org.apache.commons.logging.Log。如果找到org.apache.commons.logging.Log系统属性,则使用该系统属性对应的日志组件。结束发现过程。
(3).如果找不到org.apache.commons.logging.Log系统属性,common-logging接着在CLASSPATH中寻找log4j的类。如果找到了就假定应用要使用的是log4j。不过这时log4j本身的属性仍要通过log4j.properties文件正确配置。结束发现过程。
(4).如果上述查找均不能找到适当的Logging API,但应用程序正运行在JRE 1.4或更高版本上,则默认使用JRE 1.4的日志记录功能。结束发现过程。
(5).最后,如果上述操作都失败(JRE 版本也低于1.4),则应用将使用内建的SimpleLog。SimpleLog把所有日志信息直接输出到System.err。结束发现过程。
但是,我明确的在classpath下配置了log4j信息,为什么我却得到的不是org.apache.commons.logging.impl.Log4jLogger呢?这件事,让我重新查看了commons-logging的源码。发现代码相对简单。
在org.apache.commons.logging.impl.LogFactoryImpl的Line162,发现了如下代码:
/**
* The names of classes that will be tried (in order) as logging
* adapters. Each class is expected to implement the Log interface,
* and to throw NoClassDefFound or ExceptionInInitializerError when
* loaded if the underlying logging library is not available. Any
* other error indicates that the underlying logging library is available
* but broken/unusable for some reason.
*/
private static final String[] classesToDiscover = {
LOGGING_IMPL_LOG4J_LOGGER,
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
/**
* <p>Construct (if necessary) and return a <code>Log</code> instance,
* using the factory's current set of configuration attributes.</p>
*
* <p><strong>NOTE</strong> - Depending upon the implementation of
* the <code>LogFactory</code> you are using, the <code>Log</code>
* instance you are returned may or may not be local to the current
* application, and may or may not be returned again on a subsequent
* call with the same name argument.</p>
*
* @param name Logical name of the <code>Log</code> instance to be
* returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped)
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public Log getInstance(String name) throws LogConfigurationException {
Log instance = (Log) instances.get(name);
if (instance == null) {
instance = newInstance(name);
instances.put(name, instance);
}
return instance;
}
从这里,我找到了一个很关键的代码,即newInstance(name)。通过这个方法,我很快的确定到了另外的一个子方法:createLogFromClass,发现它原来是通过反射的方式检验Log4j是否存在的,即:
Class c = Class.forName(logAdapterClassName, true, currentCL);
org.apache.commons.logging.impl. Log4jLogger ,
非常幸运的是,我得到了一个幸运的异常500,提示我,无法加载org/apache/log4j/Category。我查看了log4j.jar,并在其中找到了此类。为什么Log4jLogger无法加载呢?
首先,我证明了Log4j确实在项目的classpath下。方法同加载Log4jLogger一样,只需要通过Class.forName()即可验证。(项目能正常加载)
至此:
确定问题:由于commons-logging.jar是存放在Tomcat下共享lib的,而log4j是存放在项目工程中。
关于:Tomcat下共享lib和项目中lib之间的关系,下一篇文章分析。今天研究一下。
来源:oschina
链接:https://my.oschina.net/u/161336/blog/178118