commons-logging中无法寻找log4j

馋奶兔 提交于 2019-12-16 15:44:03

【推荐】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"
    };



从上面的代码及说明,证实了网上资料的正确性。接下来,Line289 getInstance(string name)获取Log方法:
/**
     * <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);



得到上面的信息,我在项目中写了一个测试jsp页面,来加载
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之间的关系,下一篇文章分析。今天研究一下。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!