JAVA日誌系統

  1. JAVA日誌系統的演變史

我們先看一個故事。項目經理A帶著一幫兄弟開發了一套複雜的企業ERP系統,這個系統一連開發了好幾年,開發人員也換了好幾撥。

階段一:最開始的時候,項目經理A安排小B在系統中添加日誌功能,在控制檯上打印一些必要的信息。最開始的時候,由於項目的功能比較少,於是小B就是用System.out.println的方式打印日誌信息。經理A感覺這樣使用比較方便,也便於項目小組人員的使用,於是就沿用了下來。

階段二:此時小B被借調到其他項目,小C加入到了項目組中。此時項目經理A要求改造日誌系統,要求能把日誌寫到一個文件中,方便以後分析用戶行為。小C在查看了以前的日誌方式之後,感覺特別low,於是自己寫了一個日誌框架,命名為xiaoC-logging.jar,此舉收到了項目經理A的好評。

階段三:項目組中加入了一個大牛老D,老D發現xiaoC-logging.jar這個日誌框架雖然可以滿足基本的日誌要求,但是還不夠高大上,沒有一些諸如自動歸檔,異步寫入文件,把日誌文件寫入NoSQL數據庫中等功能。於是老D開發了一個更高級的日誌框架叫oldD-logging.jar。

階段四:oldD-logging.jar開發完成之後,需要把原來的xiaoC-logging.jar中的日誌API做修改,把之前的日誌實現寫下來,換上高大上的oldD-logging.jar。

階段五:在這個卸載與上新的過程中,老D的工作量陡增,他感覺很累。不過薑還是老的辣,他參考了JDBC和spring中面向接口的編程方式,制定了一個日誌的門面(一系列的接口),以後所有的日誌的記錄,都只面向接口編程,至於今後怎麼去實現,都要遵循這個接口就可以了。

那麼在JAVA開發中,這正的日誌系統是怎麼演變的呢?簡短地描述下日誌發展,最先出現的是apache開源社區的log4j,這個日誌確實是應用最廣泛的日誌工具,成為了java日誌的事實上的標準。然而,當時Sun公司在jdk1.4中增加了JUL日誌實現,企圖對抗log4j,但是卻造成了混亂,這個也是被人詬病的一點。當然也有其他日誌工具的出現,這樣必然造成開發者的混亂,因為這些日誌系統互相沒有關聯,替換和統一也就變成了比較棘手的一件事。想象下你的應用使用log4j,然後使用了一個其他團隊的庫,他們使用了JUL,你的應用就得使用兩個日誌系統了,然後又有第二個庫出現了,使用了simplelog。這個時候估計讓你崩潰了,這是要鬧哪樣?這個狀況交給你來想想辦法,你該如何解決呢?進行抽象,抽象出一個接口層,對每個日誌實現都適配或者轉接,這樣這些提供給別人的庫都直接使用抽象層即可。不錯,開源社區提供了commons-logging抽象,被稱為JCL,也就是日誌框架了,確實出色地完成了兼容主流的日誌實現(log4j、JUL、simplelog),基本一統江湖,就連頂頂大名的spring也是依賴了JCL。看起來事物確實是美好,但是美好的日子不長,接下來另一個優秀的日誌框架slf4j的加入導致了更加混亂的場面。比較巧的是slf4j的作者(Ceki Gülcü)就是log4j的作者,他覺得JCL不夠優秀,所以他要自己搞一套更優雅的出來,於是slf4j日誌體系誕生了,併為slf4j實現了一個親子——logback,確實更加優雅,但是由於之前很多代碼庫已經使用JCL,雖然出現slf4j和JCL之間的橋接轉換,但是集成的時候問題依然多多,對很多新手來說確實會很懊惱,因為比單獨的log4j時代“複雜”多了,抱怨聲確實很多。到此本來應該完了,但是Ceki Gülcü覺得還是得回頭拯救下自己的“大阿哥”——log4j,於是log4j2誕生了,同樣log4j2也參與到了slf4j日誌體系中,想必將來會更加混亂。接下來詳細解讀日誌系統的配合使用問題。slf4j的設計確實比較優雅,採用比較熟悉的方式——接口和實現分離,有個純粹的接口層——slf4j-api工程,這個裡邊基本完全定義了日誌的接口,所以對於開發來說,只需要使用這個即可。有接口就要有實現,比較推崇的實現是logback,logback完全實現了slf4j-api的接口,並且性能也比log4j更好,同時實現了變參佔位符日誌輸出方式等等新特性。剛剛也提到log4j的使用比較普遍,所以支持這批用戶依然是必須的,slf4j-log4j12也實現了slf4j-api,這個算是對log4j的適配器。同樣推理,也會有對JUL的適配器slf4j-jdk14等等。為了使使用JCL等等其他日誌系統後者實現的用戶可以很簡單地切換到slf4j上來,給出了各種橋接工程,比如:jcl-over-slf4j會把對JCL的調用都橋接到slf4j上來,可以看出jcl-over-slf4j的api和JCL是相同的,所以這兩個jar是不能共存的。jul-to-slf4j是把對jul的調用橋接到slf4j上,log4j-over-slf4j是把對log4j的調用橋接到slf4j。

JAVA日誌系統

日誌門面和日誌實現

我們需要在左邊選一個門面(抽象層)、右邊來選一個實現。那麼最優選擇就是

日誌門面: SLF4J,日誌實現:Logback。

SpringBoot的底層是Spring框架,Spring框架默認是用JCL。但是SpringBoot選用 SLF4j和logback;

  1. slf4j使用原理規則

我們進入到slf4j的官網,打開用戶手冊,即可看到簡單的用法案例:

JAVA日誌系統

slf4j簡單用法

當然在使用之前,需要導入slf4j的jar和 logback的實現jar,但是如果是換做其他的日誌實現,需要哪些jar包的依賴呢,官方網站給我們提供了一個圖:

JAVA日誌系統

我們看一下這個圖,上面的每一列都是講如何把slf4j(日誌門面)與其他日誌的實現進行整合的過程。其中淺藍色表示抽象的日誌API,即我們說的日誌門面(slf4j-api.jar),深藍色是具體的日誌實現,青色表示一個日誌的適配器,灰色表示和slf4j沒有直接關聯的日誌實現。

第一列:日誌門面使用的是slf4j-api.jar,但是沒有任何日誌實現的話,那麼日誌講不會輸出任何內容。

第二列:說的是slf4j和logback如何綁定。日誌門面使用的是slf4j-api.jar,那麼日誌的實現使用logback-classic.jar和logback-core.jar。我們之前講過,logback是slf4j的親兒子,所以slf4j可以和logback無縫連接。

第三列:說的是slf4j和log4j如何綁定,我們知道slf4j和log4j的作者是同一個人,但是作為藉口出現的slf4j出現的要比log4j晚,所以slf4j和log4j並不能無縫連接,需要一個適配層slf4j-log4j12.jar。然後這個適配器實現了slf4j-api.jar中的接口和抽象類,但是是實現還是用的log4j的API去實現。由此解決了log4和slf4j整合的問題。

第四列:說的是slf4j和JUL(java.util.logging)如何綁定,原理同第三列。

第五列:slf4j本身也提供了一個簡單實現,叫做slf4j-simple.jar。二者可以無縫連接。

第六列:slf4j本身也提供了一個沒有任何實現的slf4j-nop.jar。這日誌沒有任何操作,不會輸出日誌。

看懂這個圖,就瞭解了需要導入哪些jar文件的依賴了。每一個日誌的實現框架都有自己的配置文件。但是以後的日誌系統裡面,這麼多的日誌實現,應該使用什麼樣的日誌配置文件呢,我們有個原則就是:

使用slf4j以後,配置文件還是做成日誌實現框架自己本身的配置文件。

  1. 其他日誌框架統一轉換為slf4j

回想我們在做ssm框架整合的時候,spring和springmvc使用的是JCL(jarkart commons-logging),mybatis使用的是log4j日誌框架,有可能其他的jar文件依賴的是JCL(Java util logging),那麼怎麼把這些不同的的日誌框架轉化為同意的slf4j的日誌呢,官方網站裡給我們提供了一個圖:

第一塊:

說的是一個系統中,如果要是用slf4j作統一的日誌門面,並且用logback做日誌的實現,但是此時還有commons-logging、log4j、jul等日誌實現怎麼辦呢?此時需要用jcl-over-slfj.jar替代commons-logging,用log4j-over-slf4j.jar替代log4j.jar,並且添加jul-to-slf4j.jar(不能替換掉JUL,因為這是JDK自帶的)

第二塊:

說的是一個系統中,如果要是用slf4j作統一的日誌門面,並且用log4j做日誌的實現,但是此時還有commons-logging、jul等日誌實現怎麼辦呢?此時需要用jcl-over-slfj.jar替代commons-logging,並且添加jul-to-slf4j.jar(不能替換掉JUL,因為這是JDK自帶的),並且添加slf4j和log4j的適配層slf4j-log4j12.jar以及實現層log4j.jar。

第三塊:

說的是一個系統中,如果要是用slf4j作統一的日誌門面,並且用JUL做日誌的實現,但是此時還有commons-logging、log4j等日誌實現怎麼辦呢?此時需要用jcl-over-slfj.jar替代commons-logging,用log4j-over-slf4j.jar替代log4j.jar,並且添加slf-jdk14.jar作為適配層,最後是用JDK自帶的JUL作為日誌的實現。

總結:如何讓系統中所有的日誌都統一到slf4j;

1、將系統中其他日誌框架先排除出去;

2、用中間包來替換原有的日誌框架(根據上圖);

3、我們導入slf4j其他的實現


分享到:


相關文章: