在學習springmvc的時候下載了3.1的版本,導入jar包的時候發現commons-logging.jar 和 log4j.jar 這兩個jar包
做項目一直都是開發二期,從沒關注過jar包的組合,以及項目的搭建思路,有感而想,真是失敗。。。
--------------------------------------------------------------------------------------------------------------------------------
通常是使用 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 源碼:
- 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);
- }
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 包下的幾個類的源碼片段:
- 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 的實現
- ......
- }
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。正如我們經常在項目中這樣寫:
- // Run 是我們自己寫的類,LogFactory 是一個專為提供 Log 的工廠(abstract class)
- private static final Log logger = LogFactory.getLog(Run.class);
// 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 片段:
- <listener>
- <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
- </listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
這是由 Spring 為我們提供的實現了標准的 servlet api 中的 javax.servlet.ServletContextListener 接口,用於在 web 容器啟動時做一些初始化操作。我們逐層進入 Spring 的源碼,可以看到如下代碼:
- Log4jConfigurer.initLogging(location, refreshInterval);
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,使得我們不必關注我們正在使用何種日志工具。