1. 程式人生 > >java-web環境整合各種主流日誌框架(jcl,jul,slf4j,log4j,logback)總結

java-web環境整合各種主流日誌框架(jcl,jul,slf4j,log4j,logback)總結

  最近一段時間,學習了java日誌管理方面的內容,用過很久,但是沒有系統的學習相關。 趁這個機會,將各種日誌相關的內容,以及框架,和整合使用方面的內容拉通學習一下。 

  我將從以下的方面進行筆記的整理:

1.log4j1與log4j2在web整合方面的區別;

2.日誌框架標準與實現的區別,及舉例;面向框架的spring-jcl以及slf4j標準,在web方面的使用記錄;

3.slf4j與其它各種日誌框架的整合使用;

4.slf4j日誌框架自動發現的實現原理,及其原始碼解讀;

5.各種日誌框架(不包括log4j2)自動載入配置檔案的原理;

6.如何進行日誌框架的無縫遷移;

log4j1與log4j2在web整合方面的區別

   我們應當知道,javaweb專案與一般的java專案是有區別的。 這些區別可以體現在: 啟動的邏輯不同;環境上下文不同;使用者互動方式不同等等。 因為web專案很多很多的框架可以使用,針對不同的目標型別,不同的邏輯構成。有些是不同的邏輯元件,有些是相同的業務邏輯元件不同的實現。  所以可複用元件,或者說標準尤為重要。   日誌元件就是這樣的一種存在。

  web環境搭建就沒有什麼必要介紹了。 以前記錄過一篇:地址在這裡

  看一下log4j1的使用

    依賴:

     使用:

   配置:

  這樣一個log4j的日誌就可以使用了。 不需要其它任何額外的配置。

在看一看log4j2的使用(基於web專案):

    依賴:

   使用:

   配置:

註冊web上下文:

     這樣,log4j2就可以在專案中使用了。

    實際上,這裡的log4j2採用的是spring-jcl標準。  於是這就引出第二個問題了。

2.日誌框架標準與實現的區別,及舉例;面向框架的spring-jcl以及slf4j標準,在web方面的使用記錄:

   先說第一個問題:標準與實現

      至此,我們實際上已經接觸了很多對這樣的邏輯關係的元件:

          比如:

              jdbc標準,由j2ee標準提出,對應的實現有:mysql的實現,sql-server的實現,oracle的實現。  其中,mysql的實現: mysql-connector.jar,由mysql官方提供。

              servlet標準,由j2ee標準提出,對應的實現有: tomcat伺服器容器,jetty容器,websocket,websphere等。 其中典型的tomcat由apache基金會提供並開源。

              orm標準,由j2ee標準提出,對應的是是現有: mybatis,hibernate。 

              datasource標準,由jdk定義,對應的實現有: dbcp2實現,spring的相關實現,druid的實現。 其中druid由阿里巴巴提供。 

           類似於這樣的標準與實現還有很多很多。。 

           要說它們的本質,那就是一堆介面,與一堆實現了這些介面的類組成。 當然在這個途中進行了大量的設計模式的使用,效能的考量,相應的擴充套件等。

           日誌框架的標準與實現也是這樣。

第二個問題:spring-jcl:

     spring-jcl的原始碼較少,因此我們不妨將它的原始碼類結構貼上:

    可見,它實際上整合的是apache的commons-loging。 只不過,進行了一些適配。  其中的關鍵是:LogFactory抽象類,以及LOG介面標準。 它的核心原始碼片段:

  

      

  以上給是該類初始化的靜態塊方法,它會根據上下文就進行適配,需注意它們實際上是有優先順序關係的。   原理很簡單,就是用類載入器去載入相應日誌框架的spi,若找到,則判定為當前系統採用該日誌框架。 否則依次進行迭代,直至採用jul。 因為它位於jdk中,無論如何都會有該日誌框架的。 

   通過原始碼可以看到,它提供了三個大型別的日誌框架實現,分別是: log4j2日誌框架實現(因為該spi類只存在Log4j2中);slf4j日誌框架標準;jul(java util logging)日誌實現。  它們具有一定的優先順序關係。 

    至於為什麼是Log4j2,可以給出原始碼驗證:

    ok,這是spring-jcl作為日誌標準為基礎的邏輯思路。   我上文使用的slf4j並沒有說它是日誌實現,而是標準。 為什麼這麼說呢? 這就是接下來的一個問題:

slf4j標準

  先看一看slf4j的專案結構:

 

    這也很好的印證了spring-jcl的動態發現,選擇日誌實現框架。 

     slf4j的標準是由這裡的Logger介面提供的。 至於具體的實現,將在後面的問題中進行討論。

最後,來看一看在web專案的中的使用:

    涉及到使用,那麼必須的是標準的實現。  由於spring-jcl預設優先順序的關係,log4j2我們必須先註釋掉,然後才能使用slf4j。 但是我們知道,這是一個標準,如何使用呢?

    通過蒐集相關的資料,我們瞭解到,slf4j對於主流的日誌框架實現都提供了適配,或者基於該標準進行開發。

    如:slf4j-simple,它的依賴

 

     它是一個實現類,實現了slf4j的logger介面。

   slf4j-jdk4,它的依賴是

    他提供的是基於slf4j的logger介面,針對java.util,logging實現的適配類。 相應的實現由Jdk提供實現,無需依賴其它的第三方。

slf4j-jcl,它的依賴

   它提供的是基於slf4j的logger介面的,針對commons-logging實現的適配類,它需要依賴jcl的實現:

slf4j-log4j12,它的依賴

  它提供的是基於slf4j的logger介面的,針對log4j實現的適配類,它需要依賴log4j的實現,這裡使用的是log4j1:

  logbak,它的依賴

  它提供的是基於slf4j的Logger介面的實現類。

以上所說的適配類,以及實現類可以通過檢視繼承關係得到驗證

   注意,這裡有一個有趣的現象,spring-jcl 作為日誌框架為其它日誌實現提供支援,包括對slf4j提供支援。  spring-jcl是基於commons-logging實現的;  而作為slf4j日誌標準來說,它同時也提供了對jcl的整合支援。  這就感覺有那麼一絲絲怪異,形成了一個類似環形的結構。。   不過,這只是感覺上來說,spring-jcl中的那個日誌工廠與commons-logging的日誌工廠應該並不相同,只不過他們提供了相同的包結構。如果是這樣的話, 在一定程度上可能會存在潛在的bug。 具體怎樣等以後有興趣了在看看看吧。

   如何使用

      針對這些slf4j標準的實現,它的原理與spring-jcl優點相似,就是在執行時動態的尋找實現。 因此,這裡舉一個例子,其它的類推即可,至於適配的那種,則需要根據本身框架實現來進行配置使用即可。 比如,就拿Simple來說:

   第一步,當然是引入相關依賴;

   第二步,建立配置檔案:

      如:

   第三步,在相應的位置使用日誌框架,它會自動掃描,動態尋找依賴,如spring-jcl:

   執行,即可。  其它的日誌使用類似!!   包括logback。    這樣的話,簡單的問題背後難免引起我們的思考。  因此這就是接下來思考的問題了。

slf4j日誌框架自動發現的實現原理,及其原始碼解讀:

   這個問題實際上可以與spring-jcl類比著看。  先回顧一下spring-jcl是如何實現自動選擇實現的:  它實際上是通過類載入器去載入特定的類,如果載入成功,則認為使用該框架作為實現,否則就一路迭代,選擇其它的實現。  直到最終會確定一個一個實現類。   這個思路有點類似於責任鏈傳遞模式。

    那麼slf4j是如何實現的呢? 

    我主要從兩個方面入手分析了這個問題: 第一,如何確定有哪些實現模組在當前專案中(如何發現);  第二,如何載入實現專案的啟動類(如何實現)

    第一個問題:  我是通過檢視資料,與檢視原始碼進行分析的,過程如下:

  這是位於slf4j的日誌工廠的其中一個靜態方法,通過原始碼邏輯,可以分析出,它實際上是完成了獲得實現模組的功能。 這個操作通過類載入器實現:

  

  該類載入器是繼承於URLClassLoader。  它的名稱為: 平行類載入器。    當然前面有一些關於類載入機制的雙親委派機制。  這是jvm部分的基礎知識,也是java的核心知識。 很早以前看過了。(大概昨年春節的時候看過原始碼,也從此開啟奮鬥之旅。)。 需要注意,在web容器中的類載入器機制中,並沒有繼續採用雙親委派機制了。  通過執行時除錯,我們可以分析出,獲得資源的這個實際實現是由tomcat容器的一個類載入器實現的:

  

(為了實驗的目的,我特意放了兩個依賴進來。)

  由於在idea中我並沒有採用內建容器,所以具體的實現細節原始碼沒有查看了。   至此,第一個問題就解決了。 

第二個問題

     記得我們之前看過基於Slf4j的實現類,其中除了一些適配類,實現類外,有一個類沒有去描述過它,因為它既不是抽象類,也不是其他類的介面卡類。 並且這個類位於slf4j-api的專案中。  

     看一看它的原始碼片段:

  在slf4j的抽象工廠中有如下的一些關於它的操作:

   

 

 

它的核心方法程式碼片段

自此,第二個問題也解決的差不多了。   它的實現方法是: 抽象+ 代理

  各種日誌框架(不包括log4j2)自動載入配置檔案的原理:

     這個問題,各個日誌框架實現類都有各自的實現。  仍然是採用類比的思想,挑一個簡單的來看看,複雜的就不看了。   要說簡單,那自然就是slf4j-simple了。 

    它的核心業務類: SimpleLogger。 

    具有如下的一些程式碼片段:

   其它自動載入配置檔案的原理估計也差不多。  就不看了。 

那麼就剩最後一個問題了:

如何進行日誌框架的無縫遷移:

     實際上,這個問題在上面的分析過程中已經解決了。  只要是基於標準實現的,要替換實現,我們要做的僅僅是替換相應的依賴即可,並且這個過程使用者不需要其它額外的配置(當然,對於不同實現之間的必要的適配還是需要做的)。   關於日誌使用的部分,就告一段落。