該讓log4j退休了 - 論Java日志組件的選擇

歷史

log4j可以當(dāng)之無愧地說是Java日志框架的元老来庭,1999年發(fā)布首個(gè)版本宣决,2012年發(fā)布最后一個(gè)版本,2015年正式宣布終止吆你,至今還有無數(shù)的系統(tǒng)在使用log4j弦叶,甚至很多新系統(tǒng)的日志框架選型仍在選擇log4j。

然而老的不等于好的妇多,在IT技術(shù)層面更是如此湾蔓。盡管log4j有著出色的歷史戰(zhàn)績(jī),但早已不是Java日志框架的最優(yōu)選擇砌梆。

在log4j被Apache Foundation收入門下之后,由于理念不合贬循,log4j的作者Ceki離開并開發(fā)了slf4j和logback咸包。

slf4j因其優(yōu)秀的性能和理念很快受到了廣泛歡迎,2016年的統(tǒng)計(jì)顯示杖虾,github上的熱門Java項(xiàng)目中烂瘫,slf4j是使用率第二名的類庫(kù)(第一名是junit)。

logback則吸取了log4j的經(jīng)驗(yàn)奇适,實(shí)現(xiàn)了很多強(qiáng)大的新功能坟比,再加上它和slf4j能夠無縫集成,也受到了歡迎嚷往。

在這期間葛账,Apache Logging則一直在關(guān)門憋大招,log4j2在beta版鼓搗了幾年皮仁,終于在2014年發(fā)布了GA版籍琳,不僅吸收了logback的先進(jìn)功能菲宴,更通過優(yōu)秀的鎖機(jī)制、LMAX Disruptor趋急、"無垃圾"機(jī)制等先進(jìn)特性喝峦,在性能上全面超越了log4j和logback。

slf4j

slf4j是一個(gè)“日志門面”(Logging Facade)呜达,而不是一個(gè)完整的日志框架谣蠢。它提供了一套記錄日志的api,但不提供輸出日志的功能查近,而是通過對(duì)接如log4j眉踱、java.util.logging等日志框架,來實(shí)現(xiàn)日志的輸出嗦嗡。

所以說slf4j的對(duì)標(biāo)選手實(shí)際上是Jakarta Commons Logging(簡(jiǎn)稱JCL)勋锤,二者的最大區(qū)別在于與日志服務(wù)的綁定機(jī)制

JCL采用的動(dòng)態(tài)綁定機(jī)制:

  1. 在進(jìn)程啟動(dòng)時(shí)嘗試獲取名為"org.apache.commons.logging.Log"的配置屬性(可與在commons-logging.properties文件中配置,或使用Java代碼進(jìn)行配置)侥祭,按配置選取對(duì)應(yīng)的日志輸出服務(wù)
  2. 如果沒有獲取到對(duì)應(yīng)配置屬性叁执,會(huì)嘗試在系統(tǒng)參數(shù)中尋找名為"org.apache.commons.logging.Log"的參數(shù)項(xiàng)
  3. 如果1,2均沒有獲取到,會(huì)在classpath下尋找log4j的相關(guān)class矮冬,如果找到谈宛,則使用log4j作為日志輸出服務(wù)
  4. 如果沒有找到log4j,則嘗試使用java.util.logging包作為日志輸出服務(wù)
  5. 如果上述都失敗胎署,則使用SimpleLog作為日志輸出服務(wù)吆录,即將所有日志輸出至控制臺(tái)標(biāo)準(zhǔn)輸出System.err

JCL的動(dòng)態(tài)綁定機(jī)制基于ClassLoader實(shí)現(xiàn),缺點(diǎn)一是效率較低琼牧,二是容易引發(fā)混亂恢筝,在一個(gè)復(fù)雜甚至混亂的依賴環(huán)境下,確定當(dāng)前正在生效的日志服務(wù)是很費(fèi)力的巨坊,特別是在程序開發(fā)和設(shè)計(jì)人員并不理解JCL的機(jī)制時(shí)撬槽,三是最致命的問題:在使用了自定義ClassLoader的程序中,使用JCL會(huì)引發(fā)各類問題趾撵,例如內(nèi)存泄露侄柔、與OSGI沖突等。

而slf4j則簡(jiǎn)單得多占调,采用靜態(tài)綁定機(jī)制:

  • slf4j為各類日志輸出服務(wù)提供了適配庫(kù)暂题,如slf4j-log4j12,slf4j-simple究珊,slf4j-jdk14等薪者。一個(gè)Java工程下只能引入一個(gè)slf4j適配庫(kù)
  • slf4j會(huì)加載org.slf4j.impl.StaticLoggerBinder作為輸出日志的實(shí)現(xiàn)類。這個(gè)類在每個(gè)適配庫(kù)中都存在苦银,所以slf4j不需要像JCL一樣主動(dòng)去尋找日志輸出實(shí)現(xiàn)啸胧,自然而然地就能與具體的日志輸出實(shí)現(xiàn)綁定起來
  • 當(dāng)需要更換日志輸出服務(wù)時(shí)(比如從logback切換回log4j)赶站,只需要替換掉適配庫(kù)即可

所以slf4j不僅對(duì)比JCL有性能上的優(yōu)勢(shì),使用slf4j的程序員也不需要去翻找配置文件或追蹤啟動(dòng)過程就能夠清除明白地了解當(dāng)前使用的是什么日志輸出服務(wù)纺念。

slf4j的優(yōu)勢(shì)還不止此:

強(qiáng)制輸出String贝椿,避免不規(guī)范代碼

傳統(tǒng)的日志api都接收Object類型的參數(shù),在程序員不遵守規(guī)范時(shí)容易引發(fā)一些錯(cuò)誤陷谱,比如:

SomeObject obj;
//...
logger.info(obj); //如果SomeObject并未覆蓋toString()方法烙博,這里就只記下來了hashcode

又如:

try {
  //...
} catch(Exception e) {
  logger.error(e); //未記錄異常stacktrace
}

slf4j的api強(qiáng)制要求傳入String類型的參數(shù),能夠在一定程度上避免此類不規(guī)范的代碼出現(xiàn)烟逊。

日志模板功能

在使用傳統(tǒng)的日志api時(shí)渣窜,可能會(huì)有這樣的代碼:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

這引發(fā)了兩個(gè)問題:

  1. 需要編寫拼接字符串的代碼,使開發(fā)效率降低
  2. 即使不需要輸出這條日志(比如當(dāng)前日志級(jí)別是ERROR時(shí))宪躯,也會(huì)執(zhí)行拼接字符串的操作乔宿,消耗額外性能,占用額外內(nèi)存

而slf4j使用日志模板功能解決了這兩個(gè)問題:

logger.debug("Entry number: {} is {}", i, String.valueOf(entry[i]));

不僅開發(fā)變得簡(jiǎn)單了访雪,而且slf4j只會(huì)在此條日志確實(shí)需要輸出時(shí)才會(huì)去拼裝字符串详瑞。
并且在輸出異常信息時(shí)也可以使用模板,不會(huì)妨礙stacktrace的輸出:

String s = "Hello world";
try {
  Integer i = Integer.valueOf(s);
} catch (NumberFormatException e) {
  logger.error("Failed to format {}", s, e);
}

slf4j的橋接功能

雖然slf4j如此優(yōu)秀臣缀,但一些類庫(kù)因?yàn)闅v史原因仍然在使用JCL作為日志api(如Spring等)坝橡,為此slf4j還推出了jcl-over-slf4j橋接庫(kù),能夠把使用JCL的API輸出的日志橋接到slf4j上精置,方便那些想要使用slf4j作為日志門面但同時(shí)又要使用Spring等需要依賴JCL的類庫(kù)的系統(tǒng)计寇。

對(duì)于自動(dòng)依賴JCL的類庫(kù),如要橋接至slf4j的話脂倦,除了引入jcl-over-slf4j適配庫(kù)之外番宁,還需要把JCL庫(kù)從classpath中移除±底瑁可以在maven配置中將JCL庫(kù)標(biāo)記為provided:

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>

同時(shí)slf4j還提供了log4j-over-slf4j等橋接庫(kù)贝淤,能夠在不改動(dòng)代碼的前提下把使用各種日志框架輸出的日志都橋接到slf4j,如下圖:

legacy.png

slf4j提供的適配庫(kù)和橋接庫(kù)

適配庫(kù):

  • slf4j-log4j12:使用log4j-1.2作為日志輸出服務(wù)
  • slf4j-jdk14:使用java.util.logging作為日志輸出服務(wù)
  • slf4j-jcl:使用JCL作為日志輸出服務(wù)
  • slf4j-simple:日志輸出至System.err
  • slf4j-nop:不輸出日志
  • log4j-slf4j-impl:使用log4j2作為日志輸出服務(wù)

logback天然與slf4j適配政供,不需要額外引入適配庫(kù)(畢竟是一個(gè)作者寫的)

橋接庫(kù):

  • log4j-over-slf4j:將使用log4j api輸出的日志橋接至slf4j
  • jcl-over-slf4j:將使用JCL api輸出的日志橋接至slf4j
  • jul-to-slf4j:將使用java.util.logging輸出的日志橋接至slf4j
  • log4j-to-slf4j:將使用log4j2輸出的日志橋接至slf4j

題外話:
slf4j唯獨(dú)沒有提供log4j2的適配庫(kù)和橋接庫(kù),log4j-slf4j-impl和log4j-to-slf4j都是Apache Logging自己開發(fā)的朽基,看樣子Ceki和Apache Logging的梁子真的很深啊……倒是Apache沒有端架子布隔,可能也是因?yàn)閟lf4j太火了吧

logback與log4j2

logback和log4j2都宣稱自己是log4j的后代,一個(gè)是出于同一個(gè)作者稼虎,另一個(gè)則是在名字上根正苗紅衅檀。

撇開血統(tǒng)不談,比較一下log4j2和logback:

  • log4j2比logback更新:log4j2的GA版在2014年底才推出霎俩,比logback晚了好幾年哀军,這期間log4j2確實(shí)吸收了slf4j和logback的一些優(yōu)點(diǎn)(比如日志模板)沉眶,同時(shí)應(yīng)用了不少的新技術(shù)
  • 由于采用了更先進(jìn)的鎖機(jī)制和LMAX Disruptor庫(kù),log4j2的性能優(yōu)于logback杉适,特別是在多線程環(huán)境下和使用異步日志的環(huán)境下
  • 二者都支持Filter(應(yīng)該說是log4j2借鑒了logback的Filter)谎倔,能夠?qū)崿F(xiàn)靈活的日志記錄規(guī)則(例如僅對(duì)一部分用戶記錄debug級(jí)別的日志)
  • 二者都支持對(duì)配置文件的動(dòng)態(tài)更新
  • 二者都能夠適配slf4j,logback與slf4j的適配應(yīng)該會(huì)更好一些猿推,畢竟省掉了一層適配庫(kù)
  • logback能夠自動(dòng)壓縮/刪除舊日志
  • logback提供了對(duì)日志的HTTP訪問功能
  • log4j2實(shí)現(xiàn)了“無垃圾”和“低垃圾”模式片习。簡(jiǎn)單地說,log4j2在記錄日志時(shí)蹬叭,能夠重用對(duì)象(如String等)藕咏,盡可能避免實(shí)例化新的臨時(shí)對(duì)象,減少因日志記錄產(chǎn)生的垃圾對(duì)象秽五,減少垃圾回收帶來的性能下降

log4j2和logback各有長(zhǎng)處孽查,總體來說,如果對(duì)性能要求比較高的話坦喘,log4j2相對(duì)還是較優(yōu)的選擇盲再。

附上log4j2與logback性能對(duì)比的benchmark,這份benchmark是Apache Logging出的起宽,有多大水分不知道洲胖,僅供參考

同步寫文件日志的benchmark:

圖片.png

異步寫日志的benchmark:

圖片.png

當(dāng)然,這些benchmark都是在日志Pattern中不包含Location信息(如日志代碼行號(hào) 坯沪,調(diào)用者信息绿映,Class名/源碼文件名等)時(shí)測(cè)定的,如果輸出Location信息的話腐晾,性能誰也拯救不了:

圖片.png

對(duì)Java日志組件選型的建議

  1. slf4j已經(jīng)成為了Java日志組件的明星選手叉弦,可以完美替代JCL,使用JCL橋接庫(kù)也能完美兼容一切使用JCL作為日志門面的類庫(kù)藻糖,現(xiàn)在的新系統(tǒng)已經(jīng)沒有不使用slf4j作為日志API的理由了
  2. 日志記錄服務(wù)方面淹冰,log4j在功能上輸于logback和log4j2,在性能方面log4j2則全面超越log4j和logback巨柒。所以新系統(tǒng)應(yīng)該在logback和log4j2中做出選擇樱拴,對(duì)于性能有很高要求的系統(tǒng),應(yīng)優(yōu)先考慮log4j2

對(duì)現(xiàn)有系統(tǒng)日志架構(gòu)的改造建議

  1. 如果現(xiàn)有系統(tǒng)使用JCL作為日志門面洋满,又確實(shí)面臨著JCL的ClassLoader機(jī)制帶來的問題晶乔,完全可以引入slf4j并通過橋接庫(kù)將JCL api輸出的日志橋接至slf4j,再通過適配庫(kù)適配至現(xiàn)有的日志輸出服務(wù)(如log4j)牺勾,如下圖:


    圖片.png

    這樣做不需要任何代碼級(jí)的改造正罢,就可以解決JCL的ClassLoader帶來的問題,但沒有辦法享受日志模板等slf4j的api帶來的優(yōu)點(diǎn)驻民。不過之后在現(xiàn)系統(tǒng)上開發(fā)的新功能就可以使用slf4j的api了翻具,老代碼也可以分批進(jìn)行改造履怯。

  2. 如果現(xiàn)有系統(tǒng)使用JCL作為日志門面,又頭疼JCL不支持logback和log4j2等新的日志服務(wù)裆泳,也可以通過橋接庫(kù)以slf4j替代JCL叹洲,但同樣無法直接享受slf4j api的優(yōu)點(diǎn)。

  3. 如果想要使用slf4j的api晾虑,那么就不得不進(jìn)行代碼改造了疹味,當(dāng)然改造也可以參考1中提到的方式逐步進(jìn)行。

  4. 如果現(xiàn)系統(tǒng)面臨著log4j的性能問題帜篇,可以使用Apache Logging提供的log4j到log4j2的橋接庫(kù)log4j-1.2-api糙捺,把通過log4j api輸出的日志橋接至log4j2。這樣可以最快地使用上log4j2的先進(jìn)性能笙隙,但組件中缺失了slf4j洪灯,對(duì)后續(xù)進(jìn)行日志架構(gòu)改造的靈活性有影響。另一種辦法是先把log4j橋接至slf4j竟痰,再使用slf4j到log4j2的適配庫(kù)签钩。這樣做稍微麻煩了一點(diǎn),但可以逐步將系統(tǒng)中的日志輸出標(biāo)準(zhǔn)化為使用slf4j的api坏快,為后面的工作打好基礎(chǔ)铅檩。

最后附上一些鏈接
slf4j官網(wǎng): https://www.slf4j.org/
logback官網(wǎng): https://logback.qos.ch/
log4j2官網(wǎng): http://logging.apache.org/log4j/2.x/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市莽鸿,隨后出現(xiàn)的幾起案子昧旨,更是在濱河造成了極大的恐慌,老刑警劉巖祥得,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兔沃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡级及,警方通過查閱死者的電腦和手機(jī)乒疏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饮焦,“玉大人怕吴,你說我怎么就攤上這事∠靥撸” “怎么了械哟?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)殿雪。 經(jīng)常有香客問我,道長(zhǎng)锋爪,這世上最難降的妖魔是什么丙曙? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任爸业,我火速辦了婚禮,結(jié)果婚禮上亏镰,老公的妹妹穿的比我還像新娘扯旷。我一直安慰自己,他們只是感情好索抓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布钧忽。 她就那樣靜靜地躺著,像睡著了一般逼肯。 火紅的嫁衣襯著肌膚如雪耸黑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天篮幢,我揣著相機(jī)與錄音大刊,去河邊找鬼。 笑死三椿,一個(gè)胖子當(dāng)著我的面吹牛缺菌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搜锰,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼伴郁,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蛋叼?” 一聲冷哼從身側(cè)響起焊傅,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸦列,沒想到半個(gè)月后租冠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡薯嗤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年顽爹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骆姐。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡镜粤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玻褪,到底是詐尸還是另有隱情肉渴,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布带射,位于F島的核電站同规,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜券勺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一绪钥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧关炼,春花似錦程腹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至社痛,卻和暖如春见转,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褥影。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工池户, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凡怎。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓校焦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親统倒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寨典,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容