Java日誌框架研究及常見配置

    按照基本的定義,日誌便是對程序運行過程當中關鍵事件的記錄;大致日誌分爲運行日誌和開發日誌,運行日誌在業務層面記錄一些關鍵事件,爲後面的跟蹤運行提供幫助,而開發日誌大多數時候是調試日誌,根據事件流的輸出來調試程序;由於開發人員自己的關注領域,運行日誌可能製做的比較少,難以達到跟蹤業務流的做用,而即便是開發日誌,由於開發的調試有各類技巧,即便是跟蹤事件流,使用println也比日誌配置簡單多了,這是一個投資回報的問ti,而人常常性的是短視的,調試可能在這些人眼裏根本不須要認真對待,沒有前期的事件記錄規劃,隨着系統規模膨脹,執行流增多,執行事件保障,這時候沒有一種合理的「開關」機制來選擇查看感興趣事件流,那結局是明晰的,日誌不止提供了一種樹形的開關結構,它還有靈活的輸出控制,在Java經常使用日誌框架Log4J還提供了JMX等接口,能夠經過管理面板來監控和修改日誌記錄行爲,這無疑是很是誘人的。
    大致上Java體系中比較經常使用的日誌框架以下

日誌框架 前端

支持日誌級別 spring

Log4J apache

FATAL ERROR WARN INFO DEBUG TRACE 編程

Java Logging API 後端

SEVERE WARNING INFO CONFIG FINE FINER FINEST 設計模式

Apache Commons Logging api

FATAL ERROR WARN INFO DEBUG TRACE app

SLF4J 框架

ERROR WARN INFO DEBUG TRACE maven

Logback

ERROR WARN INFO DEBUG TRACE

Jboss logging

FATAL ERROR WARN INFO DEBUG TRACE

按照我比較有限的經驗,我大多時候使用的是Log4J1.x版本,在配置一些開源框架時,可能須要到 Apache Commons LoggingSLF4J,在配置Jboss系工具和框架的時候須要用到Jboss logging(例如Hibernate),其餘日誌框架如Java Logging,爲Java自帶的,功能上比較簡單,用的也不是不少 ,而另外一個Logback,這是一個號稱Log4J繼承者的新一代日誌框架,由於我對「新一代」這個名詞比較不感冒,我尚未怎麼看,感興趣的能夠看看。
    基本上,日誌級別是很類似的,這不只僅是Java生態範圍內,整個編程行業應該都有共識,經過日誌級別來標記事件的輕重緩急,咱們能夠經過這個條件進行過濾,顯示咱們感興趣的內容,關於日誌級別之間的相互關係,這個比較簡單,這裏就再也不說了。

    優秀的編程設計原則中有一條「依賴倒置」,要依賴抽象,不要依賴具體,具體的實踐就是面向接口設計了,優秀的項目都會定義高層次抽象,經過封裝隱藏多餘的信息,這樣在代碼變更或者想不修改原有代碼擴展的時候(開閉原則),可以相對容易的知足這些需求,而這些日誌框架設計結構中則充分發揮了面向接口設計的優良傳統,日誌框架的核心接口是Logger類(接口),其餘部分則是圍繞此類(接口)展開,這裏咱們看兩個定義例子,SLF4J的Logger定義以下


Apache Commons Logging的Log定義以下


由於其餘的日誌框架實在都太「重量級」了,方法太多,這裏就不貼出定義了,日誌框架定義了基本的接口,經過提供一個Facade,然後端由誰去實現就不那麼重要了,這裏面Apache Commons Logging、SLF4J、Jboss logging都允許其餘後端實現爲別的日誌框架(大部分爲Java Logging 、Log4J,差別化的部分在講這些日誌框架的時候再詳細敘述),如何將Facade的請求轉爲後端可識別請求,知道一點設計模式的人應該立刻想到適配器模式,這裏,咱們想給出幾個日誌框架如何實現這個接口適配的解決方法,咱們一一道來。
      Apache Commons Logging

這是標準的適配器模式,沒有什麼好講的,Apache Commons Logging提供的實際工做類以下

這裏面咱們比較熟悉的是JDK自帶的Java Logging和Log4J,其餘幾個由於不怎麼經常使用就不說了,關於Apache Commons Logging如何選擇具體的實現類,這個能夠從LogFactory的實現類中查找到邏輯,邏輯代碼以下:

由於代碼比較長,這裏只貼出關鍵部分邏輯,能夠看得很清楚,用戶經過配置文件指定後端實現,反之經過查找類路徑來選擇具體實現類。
SLF4J

SLF4J也存在上述相似的適配方式,這裏咱們給出SLF4J有些不一樣的bridge方式,其形式相似適配,以下

這個結構有些複雜,這裏是SLF4J到Log4J的橋接,咱們看到Log4jLoggerFactory提供了相似的適配功能,這種方式的好處,按照官方文檔的說法是提供了編譯時綁定,排除了運行時的探測方式,性能會好一些,看着這圖有些抽象,這裏給出一個具體的應用場景,以下
   Spring日誌配置
    Spring項目使用了Apache Commons Logging日誌框架,由於該日誌框架比較老舊,咱們通常使用Log4J做爲其後端實現,經過上面的介紹,咱們知道只要將Log4J放入ClassPath中就能夠了,若是使用maven,在依賴中添加Log4J就能夠了,這裏如何使用SLF4J的橋接,結果有些複雜,首先須要去掉Apache Commons Logging依賴,而後添加Spring到SLF4J的Bridge,而後使用一個後端實現Log4J,所以在添加一個SLF4J到Log4J的Bridge,最後是Log4J實現,用maven管理的話,結果以下
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.2.2.BUILD-SNAPSHOT</version>
        <exclusions>
            <exclusion>
                <!--排除commons-logging依賴-->
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <!--commons-logging到SLF4J之間的Bridge-->
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.5.8</version>
    </dependency>
<dependency>
        <!--SLF4J接口-->
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <!--SLF4J到Log4J之間的Bridge-->
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <!--Log4J實現-->
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.14</version>
    </dependency>
</dependencies>

這種方式比較複雜,由於在平時工做中沒有用過SLF4J日誌框架,因此這種方式看起來有些畫蛇添足,但考慮到SLF4J做爲事實上的Java Logging標準,如何更加有效的在不一樣前端Facade Logger接口到後端實現之間自由適配,SLF4J提供了一個很好的形式,以下


前端到後端的適配可以更加方便,這確實是一個很是有趣的想法。
Jboss logging

    事實上這個日誌框架貌似不是很出名,可是基本上Jboss系的產品、框架都會使用這個日誌框架,所以,咱們也須要好好的學習一下,該日誌結構以下

這裏的也是一種適配器方式,只是最終暴露的使用接口是特定的LoggerProvider,查找特定日誌框架的LoggerProvider是由LoggerProviders提供的,代碼以下

代碼顯示在LoggerProviders加載時就開始查找特定LoggerProvider,具體查找邏輯以下

上面看到的是經過配置文件查找

後半部分是根據ClassPath查找。
Jboss logging比較奇特的地方是其提供了一個JBossLogManagerLogger,這個Logger使用的內部Logger爲JBoss容器託管日誌管理器,這個能夠注意一下

上面分析了日誌框架前端Facade和後端實現如何適配,如今咱們選取一個具體的框架來給出一些常見配置,但願可以對你們有用,咱們選取的就是Log4J,由於其餘框架我使用的頻率比較低,二來由於日誌框架的概念都相似,熟悉了一個其餘的應該也能夠快速掌握,下面讓咱們展開對Log4J的論述。
    Log4J來自於Apache社區,是目前應用最廣的一個日誌框架,官方提供了兩個大的版本,爲Log4J 1.x和Log4J 2.x,這兩個主版本連官網連接都不同,看來Log4J 2.x應該針對前一版本問ti而從新設計,以致於致使和前一版本的不兼容,但由於Log4J 1.x普及的根深蒂固,不少日誌框架沒有及時跟上對Log4J 2.x後端的支持,Log4J 2.x完全替換掉Log4J 1.x還有很長的一段路要走。

    這裏咱們給出的是Log4J 1.x的版本介紹,關於Log4J 2.x,咱們有機會再進行探索;在Log4J的體系裏,有三個最重要的概念:Logger、Appender、Layout,Logger即爲日誌寫入入口,Appender定義了將日誌時間輸出的策略,Layout給出輸出的格式,三者的關係以下

由於Log4J中有Category的概念,這裏我把它當作一個Logger,三者的關係這樣看起來比較明顯,Logger接收日誌記錄,Appender接收Logger發來的日誌事件,而後根據Layout定義格式進行輸出,這裏沒有什麼好講的,咱們開始講如何配置。
    Log4J 1.x有兩種配置方式,一種是Java properties文件,一種是XML,第一種用的比較多一些,這個和Log4J 2.x正好相反,配置文件配置主要配置上面三個對象,配置Logger,而後配置使用哪一種具體Appender,而後定義格式,看一個例子,以下:

log4j.rootLogger=DEBUG, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n
如上,必定要定義的Logger是root Logger,這裏構成了一個相似樹形結構,頂部節點定義默認行爲,子節點能夠對行爲進行修改定製,這裏定義rootLogger使用DEBUG級別,使用A1這個Appender,注意這裏的寫法:第一個爲日誌級別,後面能夠跟不少個Appender;線面定義了這個具體的Appender A1,咱們看到其使用的是ConsoleAppender,後面咱們給這個Appender指定Layout,這裏的Layout爲PatternLayout,這個比較經常使用,最後一行定義了日誌格式,這種格式寫法相似於printf那種格式化字符串,裏面的佔位符構成爲"%[格式修飾符]日誌含義符號"(中括號裏的爲可選),格式修飾符比較簡單,「-」表明靠左對其,不加就是靠右對其,後面的數字表明瞭該日誌內容部分的最少顯示長度,若是有「.」,"."以後爲該日誌內容最長顯示長度,超過長度會進行裁短;後面的日誌含義符號以下表

符號

意義

c(小寫)

輸出日誌的類別

C(大寫)

輸出觸發日誌請求的類的全稱

d

輸出日誌事件的日期時間

F

輸出日誌請求類文件名

l

輸出請求日誌調用位置信息

L

輸出請求日誌代碼行號

m

輸出應用提供的日誌消息

M

輸出請求日誌方法名

n

輸出平臺特定的行分隔符

p

輸出日誌級別

r

輸出從程序啓動到如今的毫秒數

t

輸出生成這個日誌事件線程的名字


這裏在給出一個例子
log4j.rootLogger=DEBUG, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%p [%t] %c{2} (%M:%L) - %m%n

log4j.logger.org.apache.log4j.examples=INFO, A2

log4j.appender.A2=org.apache.log4j.FileAppender
log4j.appender.A2.File=${user.home}/test

#若是test文件存在就先清空它
log4j.appender.A2.Append=false

log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%5r %-5p [%t] %c{2} - %m%n
上面的例子稍微複雜一些,這裏設定了一個rootLogger,並制定了其日誌級別和Appender,後面定義了一個名爲org.apache.log4j.examples 的Logger,這個Logger又定義了日誌級別和Appender,經過這裏的例子能夠看出Log4J這種樹形的關係具備足夠的靈活性。
    關於XML的配置,這裏給出一個實例,你們能夠根據上面對properties的理解來配置XML,以下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j=' http://jakarta.apache.org/log4j/'>
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
        </layout>
    </appender>
    <category name="org.apache.log4j.xml">
        <priority value="info" />
    </category>
    <Root>
        <priority value ="debug" />
        <appender-ref ref="STDOUT" />
    </Root>
</log4j:configuration>
這裏的category如前面分析結構時所說,能夠認爲其就是一個Logger,這個結構也比較一目瞭然,沒有什麼要講的,你們能夠根據本身的實際須要來調整結構。
這裏還有個問ti是配置文件命名的問ti,默認狀況下,若是配置文件命名爲log4j.xml或log4j.properties時Log4J會自動加載,若是由於某種緣由不能這樣命名,那麼久須要編碼來實現配置的加載了,代碼片斷以下
String resource =
                "/app/resources/example.properties";
        URL configFileResource =
                InitUsingPropertiesFile.class.getResource(resource);
        PropertyConfigurator.configure(configFileResource);

日誌框架就寫這些吧,但願能對你們有所幫助,對單一經常使用日誌框架的詳細解析,有機會在寫吧
相關文章
相關標籤/搜索