1. 程式人生 > >日誌組件slf4j介紹及配置詳解

日誌組件slf4j介紹及配置詳解

utf 字段 als itl -- owa 輸出結果 apt 查找

1 基本介紹

每一個Java程序員都知道日誌對於任何一個Java應用程序尤其是服務端程序是至關重要的,而很多程序員也已經熟悉各種不同的日誌庫,如java.util.logging、Apache log4j、logback。但如果你還不知道SLF4J(Simple logging facade for Java)的話,那麽是時候在你的項目中學習使用SLF4J了。

SLF4J不同於其他日誌類庫,與其它日誌類庫有很大的不同。SLF4J(Simple logging Facade for Java)不是一個真正的日誌實現,而是一個抽象層( abstraction layer),它允許你在後臺使用任意一個日誌類庫。如果是在編寫供內外部都可以使用的API或者通用類庫,那麽你真不會希望使用你類庫的客戶端必須使用你選擇的日誌類庫。

如果一個項目已經使用了log4j,而你加載了一個類庫,比方說 Apache Active MQ——它依賴於於另外一個日誌類庫logback,那麽你就需要把它也加載進去。但如果Apache Active MQ使用了SLF4J,你可以繼續使用你的日誌類庫而無需忍受加載和維護一個新的日誌框架的痛苦。

總的來說,SLF4J使你的代碼獨立於任意一個特定的日誌API,這是對於API開發者的很好的思想。雖然抽象日誌類庫的思想已經不是新鮮的事物,而且Apache commons logging也已經在使用這種思想了,但SLF4J正迅速成為Java世界的日誌標準。讓我們再看幾個使用SLF4J而不是log4j、logback或者java.util.logging的理由。

2 SLF4J對比Log4J,logback和java.util.Logging的優勢

正如我之前說的,在你的代碼中使用SLF4J寫日誌語句的主要出發點是使得你的程序獨立於任何特定的日誌類庫,依賴於特定類庫可能需要使用不同於你已有的配置,並且導致更多維護的麻煩。除此之外,還有一個SLF4J API的特性是使得我堅持使用SLF4J而拋棄我長期間鐘愛的Log4j的理由,是被稱為占位符(place holder),在代碼中表示為“{}”的特性。占位符是一個非常類似於在Stringformat()方法中的%s,因為它會在運行時被某個提供的實際字符串所替換。這不僅降低了你代碼中字符串連接次數,而且還節省了新建的String對象。通過使用SLF4J,你可以在運行時延遲字符串的建立,這意味著只有需要的String對象才被建立。而如果你已經使用log4j,那麽你已經對於在if條件中使用debug語句這種變通方案十分熟悉了,但SLF4J的占位符就比這個好用得多。

這是你在Log4j中使用的方案,但這並不有趣,而且降低了代碼可讀性,因為它增加了不必要的繁瑣重復代碼(boiler-plate code):

if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
  • 1
  • 2
  • 3

另一方面,如果你使用SLF4J的話,你可以得到更簡潔格式的結果,就像以下展示的一樣:

logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
  • 1

在SLF4J,我們不需要字符串連接而且不會導致暫時不需要的字符串消耗。取而代之,我們在一個以占位符和參數傳遞實際值構成的模板格式下寫日誌信息。你可能會在想萬一我有很多個參數怎麽辦?嗯,那麽你可以選擇使用變量參數版本的日誌方法或者以Object數組傳遞。這是一個相當方便和高效方法的打日誌方法。記住,在生產最終日誌信息字符串之前,這個方法會檢查一個特定的日誌級別是不是打開了,這不僅降低了內存消耗而且預先降低了CPU去處理字符串連接命令的時間。這裏是使用SLF4J日誌方法的代碼,來自於slf4j-log4j12-1.6.1.jar中的Log4j的適配器類Log4jLoggerAdapter。

public void debug(String format, Object arg1, Object arg2) {
    if (logger.isDebugEnabled()) {
        FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
        logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

同時,我們應該知道打日誌是對應用程序性能有著很大影響,在生產環節上我們建議只進行必要的日誌記錄。

3 使用配置

3.1 maven依賴

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.21</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.2 日誌系統配置

假設現有如下程序:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger logger = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        int status = 0;
        if (status == 0) {
            logger.info("status:{}", status);
        } else {
            logger.info("status:{}", status);
        }
        logger.info("end!");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

可以使用以下兩種方式對日誌系統的輸出格式、記錄級別、輸出方式等進行配置。

3.2.1 properties文件方式

log4j.properties:

log4j.rootLogger=info, ServerDailyRollingFile, stdout
log4j.appender.ServerDailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ServerDailyRollingFile.DatePattern=‘.‘yyyy-MM-dd
log4j.appender.ServerDailyRollingFile.File=logs/notify-subscription.log
log4j.appender.ServerDailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.ServerDailyRollingFile.layout.ConversionPattern=%d - %m%n
log4j.appender.ServerDailyRollingFile.Append=true
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %p [%c] %m%n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

輸出結果為:

2016-05-12 16:08:21 INFO [club.chuxing.learn.Main] status:0
2016-05-12 16:08:21 INFO [club.chuxing.learn.Main] end!

3.2.2 xml文件方式

首先pom中添加如下依賴:

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-core</artifactId>
  <version>1.1.7</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.7</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 應用名稱 -->
    <property name="APP_NAME" value="logtest" />
    <!--日誌文件的保存路徑,首先查找系統屬性-Dlog.dir,如果存在就使用其;否則,在當前目錄下創建名為logs目錄做日誌存放的目錄 -->
    <property name="LOG_HOME" value="${log.dir:-logs}/${APP_NAME}" />
    <!-- 日誌輸出格式 -->
    <property name="ENCODER_PATTERN"
              value="%d{yyyy-MM-dd  HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n" />
    <contextName>${APP_NAME}</contextName>

    <!-- 控制臺日誌:輸出全部日誌到控制臺 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>${ENCODER_PATTERN}</Pattern>
        </encoder>
    </appender>

    <!-- 文件日誌:輸出全部日誌到文件 -->
    <appender name="FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/output.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 錯誤日誌:用於將錯誤日誌輸出到獨立文件 -->
    <appender name="ERROR_FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
    </appender>

    <!-- 獨立輸出的同步日誌 -->
    <appender name="SYNC_FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/sync.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
    </appender>

    <logger name="log.sync" level="DEBUG" addtivity="true">
        <appender-ref ref="SYNC_FILE" />
    </logger>

    <root>
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

輸出結果為:

2016-05-12 17:08:32.105 [main] INFO club.chuxing.learn.Main - status:0
2016-05-12 17:08:32.114 [main] INFO club.chuxing.learn.Main - end!

3.3 日誌系統配置說明

3.3.1 輸出級別的種類

  • ERROR 為嚴重錯誤 主要是程序的錯誤
  • WARN 為一般警告,比如session丟失
  • INFO 為一般要顯示的信息,比如登錄登出
  • DEBUG 為程序的調試信息

3.3.2 配置日誌信息輸出目的地

log4j.appender.appenderName=??

  1. org.apache.log4j.ConsoleAppender(控制臺)
  2. org.apache.log4j.FileAppender(文件)
  3. org.apache.log4j.DailyRollingFileAppender(每天產生一個日誌文件)
  4. org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件)
  5. org.apache.log4j.WriterAppender(將日誌信息以流格式發送到任意指定的地方)

3.3.3 配置日誌信息的格式

log4j.appender.appenderName.layout = ??

  1. org.apache.log4j.HTMLLayout(以HTML表格形式布局)
  2. org.apache.log4j.PatternLayout(可以靈活地指定布局模式)
  3. org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串)
  4. org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等信息)

3.3.4 ConsoleAppender選項

Threshold=DEBUG:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
Target=System.err:默認情況下是System.out,指定輸出控制臺

3.3.5 FileAppender 選項

Threshold=DEBUG:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
File=mylog.txt:指定消息輸出到mylog.txt文件。
Append=false:默認值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內容。

3.3.6 RollingFileAppender 選項

Threshold=DEBUG:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
File=mylog.txt:指定消息輸出到mylog.txt文件。
Append=false:默認值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內容。
MaxFileSize=100KB: 後綴可以是KB, MB 或者是 GB. 在日誌文件到達該大小時,將會自動滾動,即將原來的內容移到mylog.log.1文件。
MaxBackupIndex=2:指定可以產生的滾動文件的最大數。

3.3.7 日誌信息格式中幾個符號所代表的含義

-X號: X信息輸出時左對齊;
%p: 輸出日誌信息優先級,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: 輸出日誌時間點的日期或時間,默認格式為ISO8601,也可以在其後指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
%r: 輸出自應用啟動到輸出該log信息耗費的毫秒數
%c: 輸出日誌信息所屬的類目,通常就是所在類的全名
%t: 輸出產生該日誌事件的線程名
%l: 輸出日誌事件的發生位置,相當於%C.%M(%F:%L)的組合,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main (TestLog4.java:10)
%x: 輸出和當前線程相關聯的NDC(嵌套診斷環境),尤其用到像java servlets這樣的多客戶多線程的應用中。
%%: 輸出一個”%”字符
%F: 輸出日誌消息產生時所在的文件名稱
%L: 輸出代碼中的行號
%m: 輸出代碼中指定的消息,產生的日誌具體信息
%n: 輸出一個回車換行符,Windows平臺為”\r\n”,Unix平臺為”\n”輸出日誌信息換行

一個示例配置文件

log4j.debug=true   
log4j.rootLogger=DEBUG,D,E

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = logs/logs.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = DEBUG
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/error.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = ERROR
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

過濾器
過濾器,執行一個過濾器會有返回個枚舉值,即DENY,NEUTRAL,ACCEPT其中之一。返回DENY,日誌將立即被拋棄不再經過其他過濾器;返回NEUTRAL,有序列表裏的下個過濾器過接著處理日誌;返回ACCEPT,日誌會被立即處理,不再經過剩余過濾器。
過濾器被添加到<Appender> 中,為<Appender> 添加一個或多個過濾器後,可以用任意條件對日誌進行過濾。<Appender> 有多個過濾器時,按照配置順序執行。

下面是幾個常用的過濾器:

  • LevelFilter: 級別過濾器,根據日誌級別進行過濾。如果日誌級別等於配置級別,過濾器會根據onMath 和 onMismatch接收或拒絕日誌。有以下子節點:
    <level>:設置過濾級別
    <onMatch>:用於配置符合過濾條件的操作
    <onMismatch>:用於配置不符合過濾條件的操作
  • ThresholdFilter: 臨界值過濾器,過濾掉低於指定臨界值的日誌。當日誌級別等於或高於臨界值時,過濾器返回NEUTRAL;當日誌級別低於臨界值時,日誌會被拒絕。
    例如:過濾掉所有低於INFO級別的日誌。
  • EvaluatorFilter: 求值過濾器,評估、鑒別日誌是否符合指定條件。有一下子節點:
    <evaluator>:
    鑒別器,常用的鑒別器是JaninoEventEvaluato,也是默認的鑒別器,它以任意的Java布爾值表達式作為求值條件,求值條件在配置文件解釋過成功被動態編譯,布爾值表達式返回true就表示符合過濾條件。evaluator有個子標簽<expression>,用於配置求值條件。
    求值表達式作用於當前日誌,logback向求值表達式暴露日誌的各種字段:
NameTypeDescription
event LoggingEvent 與記錄請求相關聯的原始記錄事件,下面所有變量都來自event,例如,event.getMessage()返回下面”message”相同的字符串
message String 日誌的原始消息,例如,設有logger mylogger,”name”的值是”AUB”,對於 mylogger.info(“Hello {}”,name); “Hello {}”就是原始消息。
formatedMessage String 日誌被各式話的消息,例如,設有logger mylogger,”name”的值是”AUB”,對於 mylogger.info(“Hello {}”,name); “Hello Aub”就是格式化後的消息。
logger String logger 名。
loggerContext LoggerContextVO 日誌所屬的logger上下文。
level int 級別對應的整數值,所以 level > INFO 是正確的表達式。
timeStamp long 創建日誌的時間戳。
marker Marker 與日誌請求相關聯的Marker對象,註意“Marker”有可能為null,所以你要確保它不能是null。
mdc Map 包含創建日誌期間的MDC所有值得map。訪問方法是:mdc.get(“myKey”) 。mdc.get()返回的是Object不是String,要想調用String的方法就要強轉,例如,((String) mdc.get(“k”)).contains(“val”) .MDC可能為null,調用時註意。
throwable java.lang.Throwable 如果沒有異常與日誌關聯”throwable” 變量為 null. 不幸的是, “throwable” 不能被序列化。在遠程系統上永遠為null,對於與位置無關的表達式請使用下面的變量throwableProxy
throwableProxy IThrowableProxy 與日誌事件關聯的異常代理。如果沒有異常與日誌事件關聯,則變量”throwableProxy” 為 null. 當異常被關聯到日誌事件時,”throwableProxy” 在遠程系統上不會為null

<onMatch>:用於配置符合過濾條件的操作
<onMismatch>:用於配置不符合過濾條件的操作
例如:過濾掉所有日誌消息中不包含“billing”字符串的日誌。

參考來源:
[1] http://www.importnew.com/7450.html#comment-204549
[2] http://www.tuicool.com/articles/6VRnui
[3] http://blog.csdn.net/linwei_1029/article/details/8844939
[4] http://blog.csdn.net/xuanjiewu/article/details/7587586
[5] http://blog.csdn.net/haidage/article/details/6794540

日誌組件slf4j介紹及配置詳解