閱讀842 返回首頁    go 阿裏雲 go 技術社區[雲棲]


java日誌和SLF4J隨想

本文漫談java中的日誌:以前怎樣使用日誌,以及類似SLF4J的庫為我們帶來了什麼。

日誌是創建軟件時的基本需求之一,常見的用例如:

  • 軟件開發過程中的調試
  • 生產環境下診斷bug
  • 出於安全目的而跟蹤訪問
  • 創建統計使用的數據
  • 等等


無論用途為何,日誌都應該是詳盡、可配置和可靠的。
曆史
在早期,java日誌使用System.out.println(), System.err.println() 或 e.printStackTrace()。調試信息輸出到標準輸出System.out,錯誤信息輸出到標準錯誤System.err。在生產環境中,二者都被重定向:前者重定向到null(注:不輸出),後者重定向到需要的日誌文件。這種做法夠用但有很大的缺點:不可配置。它是個是或否的開關,要麼全記錄要麼完全不,不能在某一層或某個包上關注具體的日誌。

Log4J充當了救星,它滿足了人們對日誌框架的幾乎所有需求。它引入了許多日誌框架中今天仍在使用的概念(它是我首個使用的框架所以請原諒我若某個概念其實不是它發明的):

  • Logger的概念,每個logger可以單獨配置
  • Appender的概念,每個appender可以將日誌輸出到它想要的任何地方(文件、數據庫、消息等等)
  • Level的概念,開發人員可以單獨配置是否輸出每條日誌

之後,Sun意識到需要在JDK中提供日誌特性,它沒有直接使用Log4J,而是模仿Log4J創建了自己的API。然而,新API的完成度不及Log4J。如果你想使用JDK1.4的日誌API,你可能必須創建自己的Appender-在java API中叫Handler-因為能直接用的日誌目的地隻有控製台和文件。

使用這些框架都需要多份配置,因為不管你選擇哪個,至少有一個你的依賴會使用另一個。Apache Commons Logging是一個將它自己與日誌框架連接的API橋梁。庫應該調用commons-logging,這樣庫使用的實際框架和你的工程是相同的,而不是它強製使用的。現實不總是這樣,所以Commons Logging沒有解決雙重配置問題。此外,Commons Logging還會遇到類加載的問題,導致NoClassDefFoundError報錯。

最後,Log4J核心的開發者由於此處不便細說的原因退出了Log4J工程。他創建了另一個日誌框架,這個本應成為Log4J第二版的日誌框架被命名為SLF4J。

一些奇怪的事實

以下是前述框架困擾我的一些事實,它們不一定都是缺陷但值得指出來:

  • Log4J通過maven強製依賴JMS, Mail和JMX,意味著如果你不嫌麻煩特意排除它們那這些就會出現在你工程的類路徑中。
  • 類似地,Commons Logging通過maven強製依賴Avalon(另一個日誌框架),Log4J, LogKit和Servlet API(!)
  • Log4J中始終包含一個Swing日誌查看器,即使它是用在無需展現的環境中,例如批處理或應用服務器。
  • Log4J 1.3版的主頁重定向到1.2版,而2.0版還在實驗室階段。

選擇哪個框架?

Log4J是可以選擇的框架(對大多數)但它不再開發了。1.2版是參考,1.3版廢棄了而2.0還處在它的早期階段。
Commons Logging是作為庫的好選擇(相比應用),但我無法忍受類加載器的問題,一次就夠了(最後,我拋開Commons Logging直接用了Log4J).
JDK1.4日誌是標準而且不存在多版本共用的問題,但它缺少太多特性,如果不二次開發如數據庫適配器或其它就不能使用。太糟了。。。但仍未回答這個問題:選擇哪個框架?

最近,我公司的架構師決定使用SLF4J,為什麼會選擇它?

SLF4J
SLF4J不及Log4J使用普遍,因為許多架構師和開發者熟悉Log4J而不知道SLF4J,或不關注SLF4J而堅持使用Log4J。此外,Log4J滿足了許多工程的所有日誌需求。不過,有趣的是,Hibernate使用了SLF4J。它擁有一些Log4J沒有的美好特性。

簡單的語法

看這個Log4J示例:
Logger.debug("Hello " + name);
由於字符串拚接的問題(注:上述語句會先拚接字符串,再根據當前級別是否低於debug決定是否輸出本條日誌,即使不輸出日誌,字符串拚接操作也會執行),許多公司強製使用下麵的語句,這樣隻有當前處於DEBUG級別時才會執行字符串拚接:
if (logger.isDebugEnabled()) {

LOGGER.debug(“Hello ” + name);
}

它避免了字符串拚接問題,但有點太繁瑣了是不是?相對地,SLF4J提供下麵這樣簡單的語法:
LOGGER.debug("Hello {}", name);
它的形式類似第一條示例,而又沒有字符串拚接問題,也不像第二條那樣繁瑣。

SLF4J API和實現
此外,SLF4J很好地解耦了API和實現,所以你在開發環境和生產環境中使用它的API都可以極佳地適配。例如,你可以強製使用SLF4J的API,而保持生產環境中用了幾年的舊的Log4J.properties文件。SLF4J的日誌實現是LogKit。

SLF4J橋接
SLF4J具有橋接的特性,你可以移除你的工程及其依賴組件使用的所有Log4J和commons-logging包,隻使用SLF4J。
SLF4J為每一種日誌框架提供一個JAR包:它模仿其它日誌框架的API但將實現引向SLF4J的API(進而使用環境中真實的框架)。一點警告:小心不要讓classpath中同時出現同一種日誌的橋接庫和實現庫,否則會陷入循環。例如,使用Log4J橋接庫時,每個Log4J的API被引向SLF4J,如果SLF4J的Log4J實現同時存在,會再次引向Log4J,這樣循環下去。

SLF4JAPI和Log4J實現
綜合所有考慮,我的建議是使用SLF4J的API和Log4J的實現。這樣,你仍像以前一樣配置Log4J,但調用更簡單的SLF4J接口。要這樣做,你需要:

操作 位置 描述
加入classpath slf4j-api.jar* 主API,沒有它就無法使用SLF4J
slf4j-log4j.jar* SLF4J的Log4J實現
jul-to-slf4j.jar* 允許重定向 JDK 1.4日誌到 SLF4J
jcl-over-slf4j.jar* 重定向commons-logging調用到SLF4J
從classpath移除 commons-logging.jar* 會跟 jcl-over-slf4j.jar裏的commons-logging API衝突
SLF4JBridgeHandler.install()** 主應用 重定向JDK 1.4日誌調用到SLF4J
* Jar名可能包含版本
**隻在你需要單一入口或少量調用

如果你的應用在應用服務器中運行,你可能需要修改它的庫和/或配置來達成以上修改。
更多:
SLF4J項目
Log4J項目
Commons Logging項目
Commons Logging類加載問題


轉載自 並發編程網 - ifeve.com

最後更新:2017-05-19 16:38:15

  上一篇:go  《Spark 官方文檔》監控和工具
  下一篇:go  簡化SLF4J和通用日誌工具的區別