Table of contents


在使用log4j時,遇到幾個問題,剛好這個幾個問題是logback特別強調他可以解決的(所以,我們應該是遇到了大多數使用log4j的開發人員會遇到的麻煩)。 而且,一直以為都是使用SLF4J當作logger的API(slf4j底層是呼叫log4j),logback也可以無痛轉換,所以就試著把Log4j換成logback

Appender Additivity(疊加)是一個很常用的功能,官網上對它的說明是這樣的

    Appender Additivity
     
    The output of a log statement of logger L will go to all the appenders in L and its ancestors. This is the meaning of the term "appender additivity".
     
    However, if an ancestor of logger L, say P, has the additivity flag set to false, then L's output will be directed to all the appenders in L and its ancestors up to and including P but not the appenders in any of the ancestors of P.
     
    Loggers have their additivity flag set to true by default.

example

  Logger Name       Attached Appenders   Additivity Flag   Output Targets           Comment
  ----------------- -------------------- ----------------- ------------------------ ---------------------------------------root              A1                   not applicable    A1                       Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it.
  x                 A-x1, A-x2           true              A1, A-x1, A-x2           Appenders of “x” and of root.
  x.y               none                 true              A1, A-x1, A-x2           Appenders of “x” and of root.
  x.y.z             A-xyz1               true              A1, A-x1, A-x2, A-xyz1   Appenders of “x.y.z”, “x” and of root.
  security          A-sec                false             A-sec                    No appender accumulation since the additivity flag is set to “false”. Only appender A-sec will be used.
  security.access   none                 true              A-sec                    Only appenders of “security” because the additivity flag in “security” is set to “false”.

基本上,也就是設定Additivity=false時,該logger所宣告的所有appenders不會附加到parent上面(常用來抑制logging到root),其子logger也不會繼承這個logger所宣告的appenders。

在配置檔使用變數

配置檔內可以透過<property> tag來定義變數,並透過${變數名稱}來引用它。系統變數也可以直接使用,像${user.home},另外,也可以用一個獨立的屬性檔(filename.properties)來定義屬性,之後再引入 <property file=“filename.properties” />

    <configuration>
     
      <property name="USER_HOME" value="/home/sebastien" />
     
      <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${USER_HOME}/myApp.log</file>
        <encoder>
          <pattern>%msg%n</pattern>
        </encoder>
      </appender>
     
      <root level="debug">
        <appender-ref ref="FILE" />
      </root>
    </configuration>

如果定義的變數需要預設使,那可以透過:-符號,ex: ${aKey:-golden},如果aKey是null,那就會用golden取代

Log4j to Logback porting筆記

換掉library

專案是用maven管理的,所以,換lib相當簡單,到maven的lib搜尋引擎找出最新的logback,換上即可

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.6.3</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.3</version>
</dependency>

換成
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>0.9.30</version>
    </dependency>

結果

因為之前都是slf4j寫的,完全不用改code,但是連其他lib裡的logger都輸出到console了(logback會有一個預設的設定檔,所以,不需要像log4j一樣,一定要給log4.xml或log4j.properties) 但是用預設的設定,會連其它freamwork的log訊息一起輸出(像hibernate),log4j在沒有配置檔時,會吐出設定檔不存在的訊息,但是logback則會以預設(內建)的配置檔做設定。

使用客製的配置檔

logback會在classpath下依序尋找

  1. logback.groovy (logback可以用groovy當設定檔)
  2. logback-test.xml (通常用於測試)
  3. logback.xml (一般是用這個)
  4. BasicConfigurator 如果上面三個都不存在時,會用這個class的內容當作配置檔(預設)
    <configuration  scan="true" scanPeriod="30 seconds" > 
     
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
          <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
      </appender>
     
      <root level="debug">
        <appender-ref ref="STDOUT" />
      </root>
    </configuration>

<configuration  
  scan="true"                 // 更改配置檔內容時,不用重啟程式
  scanPeriod="30 seconds"     // 多久偵測一次變更
> 

Rolling Appender
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${USER_HOME}/foo.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>${USER_HOME}/foo.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- keep 120 days' worth of history -->
            <maxHistory>120</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %30.20logger{0}.%-30method - %msg%n</pattern>
        </encoder>
    </appender>

讓Logger周期性的改名成舊版(Rolling)是一個很常見的需求,像是每天午夜12點整,把log改名成前一日的日期,然後當日的log則從新檔開始,這樣可以避免單一的log檔過大, 這樣的行為,我們叫Daily Rolling。

LogBack的Rolling的設定上很直覺,只要設定fileNamePattern中的日期格式,它就會依設定日期格式的最小單位做Rolling。但要注意的是如果<appender><file>tag有設定路徑,那<fileNamePattern>也要設定相同的路徑,這樣可以讓舊的log(有包含日期的檔名)跟新的log(作用中的log)在同一個目錄

這樣設定後,在使用者的家目錄下,可以看到類似下面的檔案列要

foo.log   // 作用中的log
foo.2012-01-01.log 
foo.2012-01-02.log 
foo.2012-01-03.log 
foo.2012-01-04.log 
...

TBD

  • 出異常時,自動修改設定檔,讓其輸出錯誤細節

Resource