咱们在作项目时,日志的记录是必不可少的一项任务,而咱们一般是使用 apache 的 log4j 日志管理工具。然而,在项目中,咱们常常会看到两个 jar 包:commons-logging.jar 和 log4j.rar。为何咱们在使用 log4j 的同时还要引入 commons-logging.jar 呢,或者说不用 commons-logging.jar 可不能够,这二者之间究竟是怎么的一种关系呢?
做为记录日志的工具,它至少应该包含以下几个组成部分(组件):
1. Logger
记录器组件负责产生日志,并可以对日志信息进行分类筛选,控制什么样的日志应该被输出,什么样的日志应该被忽略。它还有一个重要的属性 - 日志级别。无论何种日志记录工具,大概包含了以下几种日志级别:DEBUG, INFO, WARN, ERROR 和 FATAL。
2. Level
日志级别组件。
3. Appender
日志记录工具基本上经过 Appender 组件来输出到目的地的,一个 Appender 实例就表示了一个输出的目的地。
4. Layout
Layout 组件负责格式化输出的日志信息,一个 Appender 只能有一个 Layout。
咱们再来看看 log4j.jar,打开 jar 包,咱们能够看到 Logger.class(Logger),Level.class(Level), FileAppender.class(Appender), HTMLLayout.class(Layout)。其它的咱们先忽略不看,这几个字节码文件正好是记录日志必不可少的几个组件。
接下来看看 commons-logging 中的 org.apache.commons.logging.Log.java 源码: php
package org.apache.commons.logging; public interface Log { public boolean isDebugEnabled(); public boolean isErrorEnabled(); public boolean isFatalEnabled(); public boolean isInfoEnabled(); public boolean isTraceEnabled(); public boolean isWarnEnabled(); public void trace(Object message); public void trace(Object message, Throwable t); public void debug(Object message); public void debug(Object message, Throwable t); public void info(Object message); public void info(Object message, Throwable t); public void warn(Object message); public void warn(Object message, Throwable t); public void error(Object message); public void error(Object message, Throwable t); public void fatal(Object message); public void fatal(Object message, Throwable t); }
很显然,只要实现了 Log 接口,它就是一个名副其实的 Logger 组件,也验证了 Logger 组件具备日志级别的属性。继续看 commons-logging org.apache.commons.logging.impl 包下的几个类的源码片断:java
package org.apache.commons.logging.impl;
import org.apache.commons.logging.Log; import org.apache.log4j.Logger; import org.apache.log4j.Priority; import org.apache.log4j.Level; import ......
public class Log4JLogger implements Log, Serializable { // 对 org.apache.commons.logging.Log 的实现 ...... }
------------------------------------------------------------------
package org.apache.commons.logging.impl;
import org.apache.commons.logging.Log; import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger;
public class Jdk14Logger implements Log, Serializable { // 对 org.apache.commons.logging.Log 的实现 ...... }
好了,分析到这里,咱们应该知道,真正的记录日志的工具是 log4j 和 sun 公司提供的日志工具。而 commons-logging 把这两个(实际上,在 org.apache.commons.logging.impl 包下,commons-logging 仅仅为咱们封装了 log4j 和 sun logger)记录日志的工具从新封装了一遍(Log4JLogger.java 和 Jdk14Logger.java),能够认为 org.apache.commons.logging.Log 是个傀儡,它只是提供了对外的统一接口。所以咱们只要能拿到 org.apache.commons.logging.Log,而不用关注到底使用的是 log4j 仍是 sun logger。正如咱们常常在项目中这样写:c++
// Run 是咱们本身写的类,LogFactory 是一个专为提供 Log 的工厂(abstract class) private static final Log logger = LogFactory.getLog(Run.class);
既然如此,咱们向构建路径加了 commons-logging.jar 和 log4j.jar 两个 jar 包,那咱们的应用程序到底使用的 log4j 仍是 sun logger 呢?咱们能不能认为因为加了 log4j.jar 包,就认为系统使用的就是 log4j 呢?事实上固然不是这样的,那我还认为我正在使用 jdk 而认为系统使用的是 sun logger 呢。使用 Spring 的朋友能够在 web.xml 中看到以下 listener 片断:web
<listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
这是由 Spring 为咱们提供的实现了标准的 servlet api 中的 javax.servlet.ServletContextListener 接口,用于在 web 容器启动时作一些初始化操做。咱们逐层进入 Spring 的源码,能够看到以下代码: spring
Log4jConfigurer.initLogging(location, refreshInterval);
终于找到了 org.springframework.util.Log4jConfigurer,这正是 log4j 提供给咱们的初始化日志的类。至此,咱们终于明白了咱们系统的的确确使用的是 log4j 的日志工具。
但是问题又来了,org.apache.commons.logging.Log 和 org.apache.log4j.Logger 这两个类,经过包名咱们能够发现它们都是 apache 的项目,既然以下,为什么要动如此大的动做搞两个东西(指的是 commons-logging 和 log4j)出来呢?事实上,在 sun 开发 logger 前,apache 项目已经开发了功能强大的 log4j 日志工具,并向 sun 推荐将其归入到 jdk 的一部分,但是 sun 拒绝了 apache 的提议,sun 后来本身开发了一套记录日志的工具。但是如今的开源项目都使用的是 log4j,log4j 已经成了事实上的标准,但因为又有一部分开发者在使用 sun logger,所以 apache 才推出 commons-logging,使得咱们没必要关注咱们正在使用何种日志工具。 apache
补充:编程
Apache针对不一样的语言平台为作了一系列日志工具包,可应用于java、.net、php、c++,这些日志包都是免费的,使用很是方便,能够极大提升编程效率。而且,Apache为了让众多的日志工具备一个相同操做方式,还实现作了一个通用日志工具包:commons-logging,也称Jakarta Commons Logging (JCL)。commons-logging是为那些须要创建在不一样环境下使用不一样日志架构的组件或库的开发者建立的,其中包括Apache Log4j以及Java log的日志架构。把日志信息commons-logging的Log接口,并由commons-logging在运行时决定使用哪一种日志架构。如今,Apache通用日志工具commons-logging和Log4j已经成为Java日志的标准工具。api
JCL有两个基本的抽象类:Log(基本记录器)和LogFactory(负责建立Log实例)。当commons-logging.jar被加入到CLASSPATH(一般将commons-logging.jar放在web project下的WebContent\WEB-INF\lib目录中)以后,它会合理地猜想你想用的日志工具,而后进行自我设置,用户根本不须要作任何设置。默认的LogFactory是按照下列的步骤去发现并决定那个日志工具将被使用的(按照顺序,寻找过程会在找到第一个工具时停止,这个顺序很是重要):架构
寻找当前factory中名叫org.apache.commons.logging.Log配置属性的值app
寻找系统中属性中名叫org.apache.commons.logging.Log的值
若是应用程序的classpath中有log4j,则使用相关的包装(wrapper)类(Log4JLogger)
若是应用程序运行在jdk1.4的系统中,使用相关的包装类(Jdk14Logger)
使用简易日志包装类(SimpleLog)
上述的加载顺序能够经过commons-logging中LogSource.java的源代码能够看出,源代码以下,具体能够参考注释:
commons-logging的工做过程以下,
package log.sample; public class ca { static { // Is Log4J Available?用户Log4J是否可用 try { /** * 经过Class.forName("org.apache.log4j.Logger"))来查找Log4J, * 只有将log4j.jar添加到classpath之后才能找到, * 这也是为何默认状况下只要将log4j.jar文件放在lib文件夹中 * 而不须要在common-logging.properties配置文件中进行配置就能自动使用log4j的缘由 */ if (null != Class.forName("org.apache.log4j.Logger")) { log4jIsAvailable = true; } else { log4jIsAvailable = false; } } catch (Throwable t) { log4jIsAvailable = false; } // Is JDK 1.4 Logging Available?原来同上面的Log4J try { if ((null != Class.forName("java.util.logging.Logger")) && (null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger"))) { jdk14IsAvailable = true; } else { jdk14IsAvailable = false; } } catch (Throwable t) { jdk14IsAvailable = false; } // Set the default Log implementation,经过common-logging.properties配置文件来决定日志实现方式 String name = null; try { name = System.getProperty("org.apache.commons.logging.log"); if (name == null) { name = System.getProperty("org.apache.commons.logging.Log"); } } catch (Throwable t) { } if (name != null) { try { setLogImplementation(name); } catch (Throwable t) { try { setLogImplementation ("org.apache.commons.logging.impl.NoOpLog"); } catch (Throwable u) { ; } } } else { try { if (log4jIsAvailable) {//若是log4j可用,默认优先使用Log4JLogger setLogImplementation ("org.apache.commons.logging.impl.Log4JLogger"); } else if (jdk14IsAvailable) {//第二优先使用Jdk14Logger setLogImplementation ("org.apache.commons.logging.impl.Jdk14Logger"); } else {//最后使用commoms-logging中的自带的实现,但它不进行任何操做 setLogImplementation ("org.apache.commons.logging.impl.NoOpLog"); } } catch (Throwable t) { try { setLogImplementation ("org.apache.commons.logging.impl.NoOpLog"); } catch (Throwable u) { ; } } } } }
commonslogging自动加载log顺序 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" };