按照基本的定義,日誌便是對程序運行過程當中關鍵事件的記錄;大致日誌分爲運行日誌和開發日誌,運行日誌在業務層面記錄一些關鍵事件,爲後面的跟蹤運行提供幫助,而開發日誌大多數時候是調試日誌,根據事件流的輸出來調試程序;由於開發人員自己的關注領域,運行日誌可能製做的比較少,難以達到跟蹤業務流的做用,而即便是開發日誌,由於開發的調試有各類技巧,即便是跟蹤事件流,使用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 Logging和SLF4J,在配置Jboss系工具和框架的時候須要用到Jboss logging(例如Hibernate),其餘日誌框架如Java Logging,爲Java自帶的,功能上比較簡單,用的也不是不少 ,而另外一個Logback,這是一個號稱Log4J繼承者的新一代日誌框架,由於我對「新一代」這個名詞比較不感冒,我尚未怎麼看,感興趣的能夠看看。
基本上,日誌級別是很類似的,這不只僅是Java生態範圍內,整個編程行業應該都有共識,經過日誌級別來標記事件的輕重緩急,咱們能夠經過這個條件進行過濾,顯示咱們感興趣的內容,關於日誌級別之間的相互關係,這個比較簡單,這裏就再也不說了。
優秀的編程設計原則中有一條「依賴倒置」,要依賴抽象,不要依賴具體,具體的實踐就是面向接口設計了,優秀的項目都會定義高層次抽象,經過封裝隱藏多餘的信息,這樣在代碼變更或者想不修改原有代碼擴展的時候(開閉原則),可以相對容易的知足這些需求,而這些日誌框架設計結構中則充分發揮了面向接口設計的優良傳統,日誌框架的核心接口是Logger類(接口),其餘部分則是圍繞此類(接口)展開,這裏咱們看兩個定義例子,SLF4J的Logger定義以下
![](http://static.javashuo.com/static/loading.gif)
Apache Commons Logging的Log定義以下
![](http://static.javashuo.com/static/loading.gif)
由於其餘的日誌框架實在都太「重量級」了,方法太多,這裏就不貼出定義了,日誌框架定義了基本的接口,經過提供一個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);
日誌框架就寫這些吧,但願能對你們有所幫助,對單一經常使用日誌框架的詳細解析,有機會在寫吧