1. 程式人生 > >日誌Log精講

日誌Log精講

1日誌為什麼要存在

我們平時寫的工具或應用程式,可以通過人眼檢視除錯來檢視執行過程中出現的錯誤,可以通過system.out.println()來輸出程式執行狀態資訊檢視。

(1)那麼,一個龐大複雜的專案工程,含有大量的介面和方法,執行過程冗長繁雜,還要使用上述的辦法來慢慢排查問題麼?

自然不是,一個設計良好的專案,肯定記錄了專案執行日誌log。

日誌的強大之處,在於它能記錄儲存專案的執行狀態,便於日後查閱,很是方便快捷、易於除錯;更重要的是,他還能夠完成跟蹤除錯、程式狀態記錄、崩潰資料恢復等工作

但如果使用system.out.println(),則太弱了,不僅繁瑣麻煩,輸出的地方和多少都不易控制。

(2)那麼,如何設計一個良好的日誌類來應用到專案中,便於提升專案的開發和維護呢?

答案:在java的世界裡,我們不用考慮和設計日誌類,因為已經存在了很多優秀的日誌系統供我們使用,比如:Log4j和Logback,還有sun公司的java.util.Logging,另外java專案中的各種框架(如:Spring,Mybatis,Httpclient…)等第三方包都有自己的日誌系統來實現日誌功能,但是這麼多的日誌實現系統同時存在,難以避免的就出現了各個日誌系統不相容的災難性情況!

(3)那麼,應該如何解決雜亂的日誌系統不相容的災難情況呢?

解決辦法就是出現了日誌框架。

2 日誌系統和日誌框架

日誌系統:日誌類的具體實現。經典的有log4j,log4j作者推出的被高度評價的logBack,以及jdk自帶的java.util.Logging等。

日誌框架:為解決多個日誌系統的相容性問題而設計的框架,當然如果只有一個日誌系統,就完全沒必要使用日誌框架了。常用的主流日誌框架有commons-logging和slf4j。

下面說一下這兩種主流框架的使用!

3 commons-logging框架

3.1介紹

commons-logging是apache推出的日誌框架,只是規定了日誌的介面規範,可以理解為是一個通用的日誌介面,其設計原理類似於jdk中servlet和jdbc的設計。

目前,主流的日誌系統log4j和java.util.Logging都實現了commons-logging定義的介面。那麼,就可以很方便的使用這些實現了 commons-logging介面的日誌系統了,使用的時候不用去考慮具體的日誌實現或到底是哪種日誌系統,使用者可以自由選擇第三方日誌元件作為具體實現。

舉個便於理解的例子

有這樣兩個實現了Log介面的類:

public class MyLog implements Log {

System.out.println(“info”);

}

public class YourLog implements Log {

System.out.println(“info”);

}

你可以把commons-logging框架理解為Log介面,只是一個定義了規範的介面,把實現了commons-logging框架的眾多日誌系統看作是實現了Log介面的實現類MyLog和YourLog。使用的時候拿著Log介面使用就行了,不用關注具體的實現類;即,使用日誌系統的時候拿著日誌框架commons-logging使用就行了。

3.2 好處總結

第一:簡化了使用和配置,為“所有的Java日誌實現”提供一個統一的介面,並且能分離專案和日誌。

第二:使用日誌框架commons-loging可以自適應的選擇日誌實現系統。

自適應日誌工具選擇功能

不得不說的是它的自適應日誌工具選擇功能?

1)   commons-logging首先在classpath下尋找自己的配置檔案commons-logging.properties,找到則使用自己定義的Log;找不到則繼續查詢是否已定義系統環境變數org.apache.commons.logging.Log,找到則使用自己定義的Log;

2)否則,在classpath中查詢是否有Log4j的路徑,若有則使用Log4j;

3)否則,使用JDK(1.4版本以後提供了日誌功能)自身的日誌實現類;

4)如果還沒有,則使用commons-logging自己提供的一個簡單的日誌實現類SimpleLog;

所以,commons-logging可以保證日誌功能的實現,自適應的儘可能找到一個最合適的工具,這也是它的優勢所在。

       可以看到,commons-logging對程式設計者和Log4j都非常友好。為了簡化配置,一般不使用commons-logging的配置檔案,也不設定與commons-logging相關的系統環境變數,而只需將Log4j的Jar包放置到classpash中就可以了。這樣就很簡單地完成了commons-logging與Log4j的融合。如果不想用Log4j了怎麼辦?只需將classpath中的Log4j的Jar包刪除即可。就這麼簡單!

3.2 經典應用commons-logging+log4j

這種實現出現在各種框架裡,如Spring,Webx,Mybatis等等, 一般為了避免直接依賴具體的日誌實現,一般都是結合commons-logging 來實現。常見應用實現程式碼如下:

    //1: 匯入所有需的commons-logging類
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class XXX{
    //2:要使用日誌的類XXX中定義私有靜態變數logger,,這是目前被普通認為的最好的方式;
//靜態是為了避免出現多個例項,
   private static Log logger = LogFactory.getLog(XXX.class);
    //3:在要使用日誌的方法中使用日誌
   public void method(args){
    log.debug("111"); 
    log.info("222"); 
    log.warn("333"); 
    log.error("444"); 
    log.fatal("555");
    }
}

例項化私有靜態日誌變數的時候,為什麼不寫作LogFactory.getLog(this.getClass())?因為static類成員訪問不到this指標!

通過上述,可以基本滿足輸出日誌資訊,如果要對輸出的資訊有特殊要求,那麼,就需要輸出配置資訊log4j.properties。

3.3 日誌輸出 配置檔案

在程式中,可以通過每個類的私有靜態變數log,將不同性質的日誌資訊輸出到不同型別的日誌檔案或不同目的地(目的地是哪裡?視配置可定,可能是stdout,也可能是檔案,還可能是傳送到郵件,甚至傳送簡訊到手機……詳見下文對log4j.properties的介紹):

l         debug()   輸出“除錯”級別的日誌資訊;

l         info()      輸出“資訊”級別的日誌資訊;

l         warn()    輸出“警告”級別的日誌資訊;

l         error()     輸出“錯誤”級別的日誌資訊;

l         fatal()      輸出“致命錯誤”級別的日誌資訊;

根據不同的性質,日誌資訊通常被分成不同的級別,從低到高依次是:“除錯(DEBUG)”“資訊(INFO)”“警告(WARN)”“錯誤(ERROR)”“致命錯誤(FATAL)”。為什麼要把日誌資訊分成不同的級別呢?這實際上是方便我們更好的控制它。比如,通過Log4j的配置檔案,我們可以設定“輸出‘除錯’及以上級別的日誌資訊”(即“除錯”“資訊”“警告”“錯誤”“致命錯誤”),這對專案開發人員可能是有用的;我們還可以設定“輸出“警告”及以上級別的日誌資訊”(即“警告”“錯誤”“致命錯誤”),這對專案終端使用者可能是有用的。

       僅從字面上理解,也可以大致得出結論:最常用的應該是debug()和info();而warn()、error()、fatal()僅在相應事件發生後才使用。

所以要正確地應用Log4j輸出日誌資訊,log4j.properties

log4j.rootLogger  =  DEBUG, CONSOLE,A1

#最最重要的一個屬性, 分成兩部分:第一個逗號之前的是第一部分,指定輸出級別;後面的是第二部分,指定輸出目的地

 “輸出級別有可選的五個值,分別是DEBUGINFOWARNERRORFATAL,它們是由Log4j系統定義的。如果此處指定的是“WARN”則僅呼叫warn()error()fatal()方法輸出的日誌資訊才被輸出到輸出目的地,而呼叫debug()info()方法輸出的日誌資訊不被輸出到輸出目的地

        “輸出目的地就是我們自己定義的了,就在log4j.properties的後面部分,此檔案定義的輸出目的地CONSOLEFILE ROLLING_FILESOCKETLF5_APPENDERMAILDATABASEA1im。該檔案之所以可作主模板,就是因為它比較 全面地定義了各種常見的輸出目的地(控制檯、檔案、電子郵件、資料庫等)。

為什麼說“CONSOLE”表示將日誌資訊輸出到控制檯呢?那就要看一下後文的定義了:

# 應用於控制檯

log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender 
log4j.appender.Threshold = DEBUG 
log4j.appender.CONSOLE.Target = System.out 
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout 
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] %d-%c -%-4r [%t] %-5p %c %x-%m%n 
#log4j.appender.CONSOLE.layout.ConversionPattern = [start]%d {DATE}[DATE]%n %p[PRIORITY]%n %x[NDC]%n %t[THREAD] n %c[CATEGORY] %n %m[MESSAGE] %n %n

為什麼說“A1”表示將日誌資訊輸出到“SampleMessages.log4j檔案呢?還要看後文的定義:

log4j.appender.A1 = org.apache.log4j.DailyRollingFileAppender 
log4j.appender.A1.File = SampleMessages.log4j 
log4j.appender.A1.DatePattern = yyyyMMdd - HH '.log4j'  
log4j.appender.A1.layout = org.apache.log4j.xml.XMLLayout

log4j.properties檔案模板

##Log4J的配置之簡單使它遍及於越來越多的應用中了
##Log4J配置檔案實現了輸出到控制檯、檔案、回滾檔案、傳送日誌郵件、輸出到資料庫日誌表、自定義標籤等全套功能。擇其一二使用就夠用了。

##此檔案(log4j.properties)內容來自網路,非本文作者liigo原創。
log4j.rootLogger = DEBUG, CONSOLE,A1
log4j.addivity.org.apache = true 

# 應用於控制檯
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
log4j.appender.Threshold = DEBUG
log4j.appender.CONSOLE.Target = System.out
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] %d-%c -%-4r[%t] %-5p %c %x-%m %n
#log4j.appender.CONSOLE.layout.ConversionPattern = [start] %d {DATE}[DATE]%n %p[PRIORITY] %n %x[NDC] %n %t[THREAD] n %c[CATEGORY]%n %m[MESSAGE]%n %n

#應用於檔案
log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.File = file.log
log4j.appender.FILE.Append = false
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern = [framework]  %d-%c-%-4r[%t] %-5p %c%x-%m%n
# Use  this  layout  for  LogFactor  5  analysis

# 應用於檔案回滾
log4j.appender.ROLLING_FILE = org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold = ERROR
log4j.appender.ROLLING_FILE.File = rolling.log
log4j.appender.ROLLING_FILE.Append = true 
log4j.appender.ROLLING_FILE.MaxFileSize = 10KB
log4j.appender.ROLLING_FILE.MaxBackupIndex = 1 
log4j.appender.ROLLING_FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern = [framework]  %d-%c-%-4r[%t]%-5p%c%x-%m%n

#應用於socket
log4j.appender.SOCKET = org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost = localhost
log4j.appender.SOCKET.Port = 5001 
log4j.appender.SOCKET.LocationInfo = true 
# Set up  for  Log Facter 5
log4j.appender.SOCKET.layout = org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern = [start] %d {DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n

# Log Factor  5  Appender
log4j.appender.LF5_APPENDER = org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords = 2000 

# 傳送日誌給郵件
log4j.appender.MAIL = org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold = FATA
log4j.appender.MAIL.BufferSize = 10 
log4j.appender.MAIL.From = [email protected]
log4j.appender.MAIL.SMTPHost = www.wusetu.com
log4j.appender.MAIL.Subject = Log4J Message
log4j.appender.MAIL.To = [email protected]
#SMTP傳送認證的帳號名
log4j.appender.MAIL.SMTPUsername=****
#SMTP傳送認證帳號的密碼
log4j.appender.MAIL.SMTPPassword=******
log4j.appender.MAIL.layout = org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern = [framework]  %d-%c-%-4r[%t]%-5p%c%x-%m%n

# 用於資料庫
log4j.appender.DATABASE = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL = jdbc:mysql: // localhost:3306/test 
log4j.appender.DATABASE.driver = com.mysql.jdbc.Driver
log4j.appender.DATABASE.user = root
log4j.appender.DATABASE.password = 
log4j.appender.DATABASE.sql = INSERT INTO LOG4J (Message) VALUES ( '[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n' )
log4j.appender.DATABASE.layout = org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern = [framework] %d-%c-%-4r[%t]%-5p%c%x-%m%n
log4j.appender.A1 = org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File = SampleMessages.log4j
log4j.appender.A1.DatePattern = yyyyMMdd - HH '.log4j' 
log4j.appender.A1.layout = org.apache.log4j.xml.XMLLayout

#自定義Appender
log4j.appender.im  =  net.cybercorlin.util.logger.appender.IMAppender
log4j.appender.im.host  =  mail.cybercorlin.net
log4j.appender.im.username  =  username
log4j.appender.im.password  =  password
log4j.appender.im.recipient  =  [email protected]
log4j.appender.im.layout = org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern  = [framework]  %d-%c-%-4r[%t]%-5p%c%x-%m%n

# 結束

4slf4j框架

4.1介紹

slf4j全稱為Simple Logging Facade for JAVA,一個java的簡單日誌門面。類似於Apache Common-Logging,是對不同日誌框架提供的一個門面封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,他在編譯時靜態繫結真正的Log庫。使用SLF4J時,如果你需要使用某一種日誌實現,那麼你必須選擇正確的SLF4Jjar包的集合。

4.2經典應用slf4j+logback

Logback必須配合sl4j使用。由於logbacksl4j是同一個作者,其相容性不言而喻。但sl4j面臨與其他日誌框架和日誌系統的相容性問題

常見應用實現程式碼如下:

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;    
public class XXX { 
    private static Log logger = LogFactory.getLog(this.getClass()); 
}

4.3日誌輸出 配置檔案

類似於commons-logging的輸出配置檔案,slf4j也同樣需要一個配置檔案logback.xml,通過該配置檔案,在程式執行之後,slf4j會自動載入logback的日誌實現,然後logback從類路徑中讀取logback.xml檔案中的日誌配置資訊,實現日誌的記錄等功能。

一個logback.xml模板配置示例及註釋詳解:

<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:當此屬性設定為true時,配置檔案如果發生改變,將會被重新載入,預設值為true。 
     scanPeriod:設定監測配置檔案是否有修改的時間間隔,如果沒有給出時間單位,預設單位是毫秒。當scan為true時,此屬性生效。預設的時間間隔為1分鐘。
     debug:當此屬性設定為true時,將打印出logback內部日誌資訊,實時檢視logback執行狀態。預設值為false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <!-- 上下文變數設定,用來定義變數值,其中name的值是變數的名稱,value的值時變數定義的值。
        通過<property>定義的值會被插入到logger上下文中。定義變數後,可以使“${}”來使用變數。 -->
    <property name="CONTEXT_NAME" value="logback-test" />

    <!-- 上下文名稱:<contextName>, 每個logger都關聯到logger上下文,
        預設上下文名稱為“default”。但可以使用<contextName>設定成其他名字,用於區分不同應用程式的記錄。
        一旦設定,不能修改。 -->
    <contextName>${CONTEXT_NAME}</contextName>

    <!-- <appender>是<configuration>的子節點,是負責寫日誌的元件。
        有兩個必要屬性name和class。
        name指定appender名稱,
        class指定appender的實現類。 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 對日誌進行格式化。 -->
        <encoder>
            <pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 被寫入的檔名,可以是相對目錄,也可以是絕對目錄,如果上級目錄不存在會自動建立,沒有預設值。 -->
        <file>${logs.dir}/logback-test.log</file>

        <!-- 當發生滾動時的行為  -->
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <!-- 必須包含“%i”例如,假設最小值和最大值分別為1和2,命名模式為 mylog%i.log,會產生歸檔檔案mylog1.log和mylog2.log。還可以指定檔案壓縮選項,例如,mylog%i.log.gz 或者 沒有log%i.log.zip -->
            <FileNamePattern>${logs.dir}/logback-test.%i.log</FileNamePattern>
            <!-- 視窗索引最小值 -->
            <minIndex>1</minIndex>
            <!-- 視窗索引最大值 -->
            <maxIndex>1</maxIndex>
        </rollingPolicy>

        <!-- 啟用滾動的條件。 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!-- 活動檔案的大小,預設值是10MB -->
            <maxFileSize>30MB</maxFileSize>
        </triggeringPolicy>

        <!-- 對記錄事件進行格式化。 -->
        <encoder>
            <charset>UTF-8</charset>
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n
            </Pattern>
        </encoder>
    </appender>

    <!-- 特殊的<logger>元素,是根logger。只有一個level屬性,應為已經被命名為"root".
        level:設定列印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能設定為INHERITED或者同義詞NULL。預設是DEBUG。
        <root>可以包含零個或多個<appender-ref>元素,標識這個appender將會新增到這個loger。 -->
    <root>
        <level value="WARN" />
        <appender-ref ref="stdout" />
        <appender-ref ref="file" />
    </root>

    <!-- 用來設定某一個 包 或者具體的某一個 類 的日誌列印級別、以及指定<appender>, 
        name:用來指定受此logger約束的某一個包或者具體的某一個類。
        level:用來設定列印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,還有一個特俗值INHERITED或者同義詞NULL,代表強制執行上級的級別。如果未設定此屬性,那麼當前loger將會繼承上級的級別。 
        additivity:是否向上級logger傳遞列印資訊。預設是true。(這個logger的上級就是上面的root)
        <logger>可以包含零個或多個<appender-ref>元素,標識這個appender將會新增到這個logger。-->
    <logger name="xuyihao.logback.test" level="DEBUG" additivity="true"></logger>

</configuration>

參考資料:

《java日誌元件介紹(common-logging,log4j,slf4j,logback )》

《log4j和log》

《logback.xml配置模版》