前言
對于一個應用程序來說日志記錄是必不可少的一部分。線上問題追蹤,基于日志的業務邏輯統計分析等都離不日志。java領域存在多種日志框架,目前常用的日志框架包括Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。但是在我們的系統里面到底該怎么使用日志框架?還在為弄不清commons-logging.jar、log4j.jar、sl4j-api.jar等日志框架之間復雜的關系而感到煩惱嗎?還在為如何統一系統的日志輸出而感到不知所措嘛?比如,要更改Spring的日志輸出為Log4j 2,卻不知該引哪些jar包,只知道去百度一下所謂的博客,照著人家復制,卻無法弄懂其中的原理?本文將弄懂其中的原理,只要你靜下心看本文,你就能隨心所欲更改你系統里的日志框架,統一日志輸出!
日志框架類別
記錄型日志框架
- Jul (Java Util Logging):JDK中的日志記錄工具,也常稱為JDKLog、jdk-logging,自Java1.4以來的官方日志實現。
- Log4j:Apache Log4j是一個基于Java的日志記錄工具。它是由Ceki Gülcü首創的,現在則是Apache軟件基金會的一個項目。 Log4j是幾種Java日志框架之一。
- Log4j2:一個具體的日志實現框架,是Log4j 1的下一個版本,與Log4j 1發生了很大的變化,Log4j 2不兼容Log4j 1。
- Logback:一個具體的日志實現框架,和Slf4j是同一個作者,但其性能更好(推薦使用)。
門面型日志框架
- JCL:Apache基金會所屬的項目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名為Commons Logging
- SLF4J:是一套簡易Java日志門面,本身并無日志的實現。(Simple Logging Facade for Java,縮寫Slf4j)
看到這么多日志框架是否會覺得比較混亂,這些日志框架之間有什么異同,都是由誰在維護,在項目中應該如何選擇日志框架,應該如何使用? 不要急,我們先把這些術語概念先有個印象。讓我們先了解一它們的發展歷史。
日志框架發展史
Java日志的恩怨情仇
- 1996年早期,歐洲安全電子市場項目組決定編寫它自己的程序跟蹤API(Tracing API)。經過不斷的完善,這個API終于成為一個十分受歡迎的Java日志軟件包,即Log4j(由Ceki創建)。
- 后來Log4j成為Apache基金會項目中的一員,Ceki也加入Apache組織。后來Log4j近乎成了Java社區的日志標準。據說Apache基金會還曾經建議Sun引入Log4j到Java的標準庫中,但Sun拒絕了。
- 2002年Java1.4發布,Sun推出了自己的日志庫JUL(Java Util Logging),其實現基本模仿了Log4j的實現。在JUL出來以前,Log4j就已經成為一項成熟的技術,使得Log4j在選擇上占據了一定的優勢。
- 接著,Apache推出了Jakarta Commons Logging,JCL只是定義了一套日志接口(其內部也提供一個Simple Log的簡單實現),支持運行時動態加載日志組件的實現,也就是說,在你應用代碼里,只需調用Commons Logging的接口,底層實現可以是Log4j,也可以是Java Util Logging。
- 后來(2006年),Ceki不適應Apache的工作方式,離開了Apache。然后先后創建了Slf4j(日志門面接口,類似于Commons Logging)和Logback(Slf4j的實現)兩個項目,并回瑞典創建了QOS公司,QOS官網上是這樣描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一個通用,可靠,快速且靈活的日志框架)。
- Java日志領域被劃分為兩大陣營:Commons Logging陣營和Slf4j陣營。
- Commons Logging在Apache大樹的籠罩下,有很大的用戶基數。但有證據表明,形式正在發生變化。2013年底有人分析了GitHub上30000個項目,統計出了最流行的100個Libraries,可以看出Slf4j的發展趨勢更好。
- Apache眼看有被Logback反超的勢頭,于2012-07重寫了Log4j 1.x,成立了新的項目Log4j 2, Log4j 2具有Logback的所有特性。
大神Ceki
log4j
早年,你工作的時候,在日志里使用了log4j框架來輸出,于是你代碼是這么寫的
import org.apache.log4j.Logger;
//省略...
Logger logger = Logger.getLogger(Test.class);
logger.trace("trace");
//省略...
jul
但是,歲月流逝,sun公司對于log4j的出現內心隱隱表示嫉妒。于是在jdk1.4版本后,增加了一個包為java.util.logging,簡稱為jul,用以對抗log4j。于是,你的領導要你把日志框架改為jul,這時候你只能一行行的將log4j的api改為jul的api,如下所示:
import java.util.logging.Logger;
//省略...
Logger loggger = Logger.getLogger(Test.class.getName());
logger.finest("finest");
//省略...
可以看出,api完全是不同的。那有沒有辦法,將這些api抽象出接口,這樣以后調用的時候,就調用這些接口就好了呢?
jcl
這個時候jcl(Jakarta Commons Logging)出現了,說jcl可能大家有點陌生,講commons-logging-xx.jar組件,大家總有印象吧。JCL 只提供 log 接口,具體的實現則在運行時動態尋找。這樣一來組件開發者只需要針對 JCL 接口開發,而調用組件的應用程序則可以在運行時搭配自己喜好的日志實踐工具。JCL可以實現的集成方案如下圖所示
jcl默認的配置:如果能找到Log4j 則默認使用log4j 實現,如果沒有則使用jul(jdk自帶的) 實現,再沒有則使用jcl內部提供的SimpleLog 實現。
于是,你在代碼里變成這么寫了
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//省略...
Log log =LogFactory.getLog(Test.class);
log.trace('trace');
//省略...
至于這個Log具體的實現類,JCL會在ClassLoader中進行查找。這么做,有三個缺點:
- 缺點一是效率較低
- 二是容易引發混亂
- 三是在使用了自定義ClassLoader的程序中,使用JCL會引發內存泄露。
slf4j
于是log4j的作者(Ceki)覺得jcl不好用,自己又寫了一個新的接口api,那么就是slf4j。
我們在代碼中需要寫日志,變成下面這么寫
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//省略...
Logger logger = LoggerFactory.getLogger(Test.class);
//省略...
logger.info("info");
在代碼中,并不會出現具體日志框架的api。程序根據classpath中的橋接器類型,和日志框架類型,判斷出logger.info應該以什么框架輸出!注意了,如果classpath中不小心引了兩個橋接器,那會直接報錯的!
因此,在阿里的開發手冊上才有這么一條
強制:應用中不可直接使用日志系統(log4j、logback)中的 API ,而應依賴使用日志框架 SLF4J 中的 API 。使用門面模式的日志框架,有利于維護和各個類的日志處理方式的統一。
Slf4j的使用
Slf4j與其它日志組件的關系說明
- Slf4j的設計思想比較簡潔,使用了Facade設計模式,Slf4j本身只提供了一個slf4j-api-version.jar包,這個jar中主要是日志的抽象接口,jar中本身并沒有對抽象出來的接口做實現。
- 對于不同的日志實現方案(例如Logback,Log4j...),封裝出不同的橋接組件(例如logback-classic-version.jar,slf4j-log4j12-version.jar),這樣使用過程中可以靈活的選取自己項目里的日志實現。
Slf4j與其它日志組件集成圖
如圖所示,應用調了sl4j-api,即日志門面接口。日志門面接口本身通常并沒有實際的日志輸出能力,它底層還是需要去調用具體的日志框架API的,也就是實際上它需要跟具體的日志框架結合使用。由于具體日志框架比較多,而且互相也大都不兼容,日志門面接口要想實現與任意日志框架結合可能需要對應的橋接器,上圖紅框中的組件即是對應的各種橋接器!
Slf4j與其他各種日志組件的橋接說明
具體的介入方式參考下圖
轉載:《深入掌握Java日志體系,再也不迷路了》
作者公眾號:一角錢技術(org_yijiaoqian)
原文:https://www.toutiao.com/a6905026316341527052/