logback官方文檔中文翻譯第六章:Layouts

第六章:Layouts

layout 是 logback 的組件,負責將日志事件轉換為字符串。Layout 接口中的 format() 方法接受一個表示日志事件的對象 (任何類型) 并返回一個字符串。Layout 接口的概要如下:

public interface Layout<E> extends ContextAware, LifeCycle {

  String doLayout(E event);
  String getFileHeader();
  String getPresentationHeader();
  String getFileFooter();
  String getPresentationFooter();
  String getContentType();
}

這個接口相對簡單,但是它可以滿足大部分的格式化需求。

Logback-classic

logback-classic 僅僅用來處理 ch.qos.logback.classic.spi.ILoggingEvent 類型的日志事件。我們將在這個部分說明這個事實。

定制 Layout

讓我們為 logback-classic 模塊實現一個簡單但是實用的功能,打印應用啟動所耗費的時間,日志事件的級別,被綜括號包裹的調用者線程,logger 名,破折號后面跟日志信息,以及新起一行。

類似下面的輸出:

10489 DEBUG [main] com.marsupial.Pouch - Hello world.

下面是一種可能的實現:

Example: MySampleLayout.java

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout extends LayoutBase<ILoggingEvent> {

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    sbuf.append(" [");
    sbuf.append(event.getThreadName());
    sbuf.append("] ");
    sbuf.append(event.getLoggerName();
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(CoreConstants.LINE_SEP);
    return sbuf.toString();
  }
}

MySampleLayout 繼承自 LayoutBase。這個類管理所有 layout 實例的狀態信息,例如:layout 是否啟動或者停止,頭部,尾部以及內容類型數據。它讓開發者通過自己 Layout 集中在日志具體的格式化上。LayoutBase 類是通用的。在它的類聲明上,MySampleLayout 繼承 LayoutBase<ILoggingEvent>

在上面這個例子中,doLayout 方法忽略了日志事件中任何可能的異常。在實際應用中,你可能需要打印異常信息。

配置自定義的 layout

配置自定義的 layout 跟其它的組件一樣的配置。根據之前提到的,FileAppender 及其子類期望一個 encoder。為了去滿足這個需求,我們將一個包裹了我們自己定義的 MySampleLayoutLayoutWrappingEncoder 的實例傳遞給 FileAppender。下面是配置示例:

Example: sampleLayoutConfig.xml

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout" />
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

chapters.layouts.SampleLogging 這個簡單的應用通過第一個參數接收配置文件,然后打印了一個 debug 信息,接著打印了 error 信息。

logback-examples 文件夾下通過以下命令來運行:

java chapters.layouts.SampleLogging src/main/java/chapters/layouts/sampleLayoutConfig.xml

將會輸出:

0 DEBUG [main] chapters.layouts.SampleLogging - Everything's going well
0 ERROR [main] chapters.layouts.SampleLogging - maybe not quite...

這種足夠簡單。讀者應該會發現,在 MySampleLayout2.java 中,我們自定義的 layout 做了一點點的修改。正如本手冊一直提到的,為 layout 或者其它 logback 的組件添加一個屬性,跟為這個屬性添加一個 set 方法一樣簡單。

MySampleLayout2 類包含了兩個屬性。第一個是可以將一個前綴添加到輸出的日志中。第二個屬性可以用來選擇是否展示發送日志請求的線程名。

下面是 MySampleLayout2 類:

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout2 extends LayoutBase<ILoggingEvent> {

  String prefix = null;
  boolean printThreadName = true;

  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }

  public void setPrintThreadName(boolean printThreadName) {
    this.printThreadName = printThreadName;
  }

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    if (prefix != null) {
      sbuf.append(prefix + ": ");
    }
    sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    if (printThreadName) {
      sbuf.append(" [");
      sbuf.append(event.getThreadName());
      sbuf.append("] ");
    } else {
      sbuf.append(" ");
    }
    sbuf.append(event.getLoggerName());
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(LINE_SEP);
    return sbuf.toString();
  }
}

添加相應的 set 方法就可以開啟屬性的配置。PrintThreadName 屬性是 boolean 而不是 String 類型。關于配置 logback 的詳細信息請參見第三章:logback 的配置第十一章將會提供更詳細的內容。下面是關于 MySampleLayout2 的相關配置:

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout2"> 
        <prefix>MyPrefix</prefix>
        <printThreadName>false</printThreadName>
      </layout>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

PatternLayout

logback 配備了一個更加靈活的 layout 叫做 PatternLayout。跟所有的 layout 一樣,PatternLayout 接收一個日志事件并返回一個字符串。但是,可以通過調整 PatternLayout 的轉換模式來進行定制。

PatternLayout 中的轉換模式與 C 語言中 printf() 方法中的轉換模式密切相關。轉換模式由字面量與格式控制表達式也叫轉換說明符組成。你可以在轉換模式中自由的插入字面量。每一個轉換說明符由一個百分號開始 '%',后面跟隨可選的格式修改器,以及用綜括號括起來的轉換字符與可選的參數。轉換字符需要轉換的字段。如:logger 的名字,日志級別,日期以及線程名。格式修改器控制字段的寬度,間距以及左右對齊。

正如我們已經在其它地方提到過的,FileAppender 及其子類需要一個 encoder。因為,當將 FileAppender 及其子類與 PatternLayout 結合使用時,PatternLayout 必須用 encoder 包裹起來。鑒于 FileAppender/PatternLayout 結合使用很常見,因此 logback 單獨設計了一個名叫 PatternLayoutEncoder 的 encoder,包裹了一個 PatternLayout,因此它可以被當作一個 encoder。下面是通過代碼配置 ConsoleAppenderPatternLayoutEncoder 使用的例子:

Example: PatternSample.java

package chapters.layouts;

import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;

public class PatternSample {

  static public void main(String[] args) throws Exception {
    Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    LoggerContext loggerContext = rootLogger.getLoggerContext();
    // we are not interested in auto-configuration
    loggerContext.reset();

    PatternLayoutEncoder encoder = new PatternLayoutEncoder();
    encoder.setContext(loggerContext);
    encoder.setPattern("%-5level [%thread]: %message%n");
    encoder.start();

    ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
    appender.setContext(loggerContext);
    appender.setEncoder(encoder); 
    appender.start();

    rootLogger.addAppender(appender);

    rootLogger.debug("Message 1"); 
    rootLogger.warn("Message 2");
  } 
}

在上面這個例子中,轉換模式被設置為 "%-5level [%thread]: %message%n ",關于 logback 中簡短的轉換字符將會很快給出。運行 PatternSample

java java chapters.layouts.PatternSample

將會輸出如下信息:

DEBUG [main]: Message 1 
WARN  [main]: Message 2

在轉換模式 "%-5level [%thread]: %message%n" 中,字面量與轉換說明符之間沒有明顯的分隔符。當對轉換模式進行解析的時候,PatternLayout 有能力對字面量 (空格符,方括號,冒號) 和 轉換說明符進行區分。在上面的例子中,轉換說明符 %-5level 表示日志事件的級別的字符應該向左對齊,保持五個字符的寬度。具體的轉換格式將會在下面介紹。

PatternLayout 中,括號用于對轉換模式進行分組。'(' 與 ')' 有特殊的含義,因此如果想用作字面量,需要進行特殊的轉義。圓括號的特殊含義將在下面 進行詳細的介紹。

之前提到過,特定的轉換模式可以通過花括號指定可選的參數。一個簡單的可選轉換模式可以是 %logger{10}。在這里 "logger" 就是轉換字符,10 就是可選參數。可選參將在下面詳細介紹。

轉換字符與它們的可選參數在下面的表格中進行詳細敘述。當多個轉換字符在同一個單元格中被列出來,它們被當作別名來考慮。

轉換字符 效果
c{length}<br />lo{length}<br />logger{length} 輸出 logger 的名字作為日志事件的來源。轉換字符接收一個作為它的第一個也是為一個參數。轉換器的簡寫算法將會縮短 logger 的名字,但是通過不會丟失重要的信息。設置 length 的值為 0 是一個例外。它將會導致轉換字符返回 logger 名字中最右邊的點右邊的字符。下面的表格提供了一個示例:<br /><table><tr><th>轉換說明符</th><th>logger的名字</th><th>結果</th></tr><tr><td>%logger</td><td>mainPackage.sub.sample.Bar</td><td>mainPackage.sub.sample.Bar</td></tr><tr><td>%logger{0}</td><td>mainPackage.sub.sample.Bar</td><td>Bar</td></tr><tr><td>%logger{5}</td><td>mainPackage.sub.sample.Bar</td><td>m.s.s.Bar</td></tr><tr><td>%logger{10}</td><td>mainPackage.sub.sample.Bar</td><td>m.s.s.Bar</td></tr><tr><td>%logger{15}</td><td>mainPackage.sub.sample.Bar</td><td>m.s.sample.Bar</td></tr><tr><td>%logger{16}</td><td>mainPackage.sub.sample.Bar</td><td>m.sub.sample.Bar</td></tr><tr><td>%logger{26}</td><td>mainPackage.sub.sample.Bar</td><td>mainPackage.sub.sample.Bar</td></tr></table>logger 名字最右邊的部分永遠不會被簡寫,即使它的長度比 length 的值要大。其它的部分可能會被縮短為一個字符,但是永不會被移除。<br />
C{length} <br />class{length} 輸出發出日志請求的類的全限定名稱。<br />跟 %logger% 轉換符一樣,它也可以接收一個整型的可選參數去縮短類名。0 表示特殊含義,在打印類名時將不會輸出包的前綴名。默認表示打印類的全限定名。<br />生成調用者類的信息并不是特別快。因此,應該避免使用,除非執行速度不是問題。
contextName<br />cn 輸出日志事件附加到的 logger 上下文的名字。
d{pattern} date{pattern} d{pattern, timezone} date{pattern, timezone} 用于輸出日志事件的日期。日期轉換符允許接收一個字符串作為參數。字符串的語法與 SimpleDateFormat 中的格式完全兼容。<br />你可以指定 "ISO8601" 來表示將日期格式為 ISO8601 類型。如果沒有指定日期格式,那么 %date 轉換字符默認為 ISO860 類型。<br />這里有一個例子。它假設當前時間為 2006.10.20 星期五,作者剛剛吃完飯準備寫這篇文檔。<br /><table><tr><th>轉換模式</th><th>結果</th></tr><tr><td>%d</td><td>2006-10-20 14:06:49,812</td></tr><tr><td>%date</td><td>2006-10-20 14:06:49,812</td></tr><tr><td>%date{ISO8601}</td><td>2006-10-20 14:06:49,812</td></tr><tr><td>%date{HH:mm:ss.SSS}</td><td>14:06:49.812</td></tr><tr><td>%date{dd MMM yyyy;HH:mm:ss.SSS}</td><td>20 oct. 2006;14:06:49.812</td></tr></table><br />第二個參數用于指定時區。例如, '%date{HH:mm:ss.SSS, Australia/Perth}' 將會打印世界上最孤立的城市,澳大利亞佩斯所在時區的日期。如果沒有指定時區參數,則默認使用 Java 平臺所在主機的時區。如果指定的時區不能識別或者拼寫錯誤,則 TimeZone.getTimeZone(String) 方法會指定時區為 GMT。<br />常見錯誤: 對于 HH:mm:ss,SSS 模式,逗號會被解析為分隔符,所以最終會被解析為 HH:mm:ssSSS 會被當作時區。如果你想在日期模式中使用逗號,那么你可以這樣使用,%date{"HH:mm:ss,SSS"} 用雙引號將日期模式包裹起來。
F / file 輸出發出日志請求的 Java 源文件名。<br />由于生成文件的信息不是特別快,因此,應該避免使用,除非速度不是問題。
caller{depth}<br />caller{depthStart..depthEnd}<br />caller{depth, evaluator-1, ... evaluator-n}<br />caller{depthStart..depthEnd, evaluator-1, ... evaluator-n} 輸出生成日志的調用者所在的位置信息。<br />位置信息依賴 JVM 的實現,但是通常由調用方法的全限定名以及調用者的來源組成。以及由圓括號括起來的文件名與行號。<br />caller 轉換符還可以接收一個整形的參數,用來配置展示信息的深度。<br />例如,%caller{2} 會展示如下的信息:<br /><pre style="background-color:#eaecef">0 [main] DEBUG - logging statement<br />Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)<br />Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)</pre> %caller{3} 會展示如下信息:<br /><pre><span style="background-color:#eaecef">16 [main] DEBUG - logging statement <br />Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) <br />Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) <br />Caller+2 at mainPackage.ConfigTester.main(ConfigTester.java:38) </span></pre><br />caller 轉換符還可以接收一個范圍用來展示深度在這個范圍內的信息。<br />例如,%caller{1..2} 會展示如下信息:<br /><pre>[main] DEBUG - logging statement <br />Caller+0 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)</pre><br />轉換字符還可以接收一個 evaluator,在計算調用者數據之前通過指定的標準對日志事件進行測驗。例如,%caller{3, CALLER_DISPLAY_EVAL} 會在 CALLER_DISPLAY_EVAL 返回一個肯定的答案,才會顯示三行堆棧信息。<br />將在下面詳細敘述 evaluator。
L / line 輸出發出日志請求所在的行號。<br />生成行號不是特別快。因此,不建議使用,除非生成速度不是問題。
m / msg / message 輸出與日志事件相關聯的,由應用程序提供的日志信息。
M / method 輸出發出日志請求的方法名。<br />生成方法名不是特別快,因此,應該避免使用,除非生成速度不是問題。
n 輸出平臺所依賴的行分割字符。<br />轉換字符提供了像 "\n" 或 "\r\n" 一樣的轉換效果。因此指定行分隔符它是首選的指定方式。
p / le / level 輸出日志事件的級別。
r / relative 輸出應用程序啟動到創建日志事件所花費的毫秒數
t / thread 輸出生成日志事件的線程名。
X{key:-defaultVal}<br /> mdc{key:-defaultVal} 輸出生成日志事件的線程的 MDC (mapped diagnostic context)。<br />如果 MDC 轉換字符后面跟著用花括號括起來的 kye,例 %MDC{userid},那么 'userid' 所對應 MDC 的值將會輸出。如果該值為 null,那么通過 :- 指定的默認值 將會輸出。如果沒有指定默認值,那么將會輸出空字符串。<br />如果沒有指定的 key,那么 MDC 的整個內容將會以 "key1=val1, key2=val2" 的格式輸出。<br />查詳情請見 第八章
ex{depth} exception{depth} throwable{depth} ex{depth, evaluator-1, ..., evaluator-n} exception{depth, evaluator-1, ..., evaluator-n} throwable{depth, evaluator-1, ..., evaluator-n} 輸出日志事件相關的堆棧信息,默認情況下會輸出全部的堆棧信息。<br /> throwable 轉換詞可以接收如下的參數:<br /><ul><li>short:輸出堆棧信息的第一行
</li><li>full:輸出全部的堆棧信息</li><li>任意整數:輸出指定行數的堆棧信息</li></ul><br />下面是一些示例:<br /><table><thead><th>轉換模式</th><th>結果</th></thead><tbody><tr><td>%ex</td><td><pre>mainPackage.foo.bar.TestException: Houston we have a problem<br /> at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)<br /> at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)<br /> at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td></tr><tr><td>%ex{short}</td><td><pre><br />mainPackage.foo.bar.TestException: Houston we have a problem<br /> at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)</pre></td></tr><tr><td>%ex{full}</td><td><pre><br />mainPackage.foo.bar.TestException: Houston we have a problem<br /> at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)<br /> at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)<br /> at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td></tr><tr><td>%ex{2}</td><td><pre>mainPackage.foo.bar.TestException: Houston we have a problem<br /> at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)<br /> at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)</pre></td></tr></tbody></table><br />在輸出前,轉換字符還可以使用給定的標準再次檢驗日志事件。例如,使用 %ex{full, EX_DISPLAY_EVAL},只有 EX_DISPLAY_EVAL 返回一個否定的答案,才會輸出全部的堆棧信息。evaluator 在接下來的文檔中將會進一步敘述。<br />如果你沒有指定 %throwable 或者其它跟 throwable 相關的轉換字符,那么 PatternLayout 會在最后一個轉換字符加上這個。因為堆棧信息非常的重要。如果你不想展示堆棧信息,那么可以使用 %nopex (作者原文為 $nopex) 可以替代 %throwable。詳情見 %nopex。
<a style="text-decoration:none" name="xThrowable" href="#xThrowable">xEx{depth}</a> <br />xException{depth} xThrowable{depth} <br />xEx{depth, evaluator-1, ..., evaluator-n} <br />xException{depth, evaluator-1, ..., evaluator-n} <br />xThrowable{depth, evaluator-1, ..., evaluator-n} 跟 %throwable 類似,只不過多了類的包信息。<br />在每個堆棧信息的末尾,多了包含 jar 文件的字符串,后面再加上具體的實現版本。這項創造性的技術是來自 James Strachan 的建議。如果該信息不確定,那么類的包信息前面會有一個波浪號 (~)。<br />下面是一個例子:<br /><pre>java.lang.NullPointerException<br /> at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3]<br /> at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]<br /> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0_06]<br /> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.5.0_06]<br /> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.5.0_06]<br /> at java.lang.reflect.Method.invoke(Method.java:585) ~[na:1.5.0_06]<br /> at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) [junit-4.4.jar:na]<br /> at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) [junit-4.4.jar:na]<br /> ...etc </pre>logback 努力的去確保類的包信息正確的展示,即使是在復雜的類加載層次中。但是,一個不能保證信息的絕對正確,那么在這些數據的前面將會多一個波浪符 (~)。因此,從理論上來說,打印的類的包信息跟真實的類的包信息是有區別的。在上面的例子中,類 Wombat 的包信息前面有一個波浪符,在實際的情況中,它真實包可能為 [wombat.jar:1.7]。<br />但是請注意潛在的性能損耗,計算包信息默認是禁止的。當啟用了計算包信息,那么 PatternLayout 將會自動認為在字符串模式的末尾 %xThrowable 替代了 %throwable。<br />根據用戶的反饋,Netbeans 會阻止包信息的打印。
nopex<br />nopexception 這個轉換字符不會輸出任何數據,因此,它可以用來有效忽略異常信息。<br />%nopex 轉換字符允許用戶重寫 PatternLayout 內部的安全機制,該機制將會在沒有指定其它處理異常的轉換字符時,默認添加 %xThrowable。
marker 輸出與日志請求相關的標簽。<br />一旦標簽包含子標簽,那么轉換器將會根據下面的格式展示父標簽與子標簽。<br />parentName [child1, child2]
property{key} 輸出屬性 key 所對應的值。相關定義參見 定義變量 以及作用域。如果 key 在 logger context 中沒有找到,那么將會去系統屬性中找。<br />key 沒有默認值,如果缺失,則會展示 " Property_HAS_NO_KEY" 的錯誤信息。
<a style="text-decoration:none" name="replace" href="#replace">replace(p){r, t}</a> 在子模式 'p' 產生的字符中,將所有出現正則表達式 'r' 的地方替換為 't'。例如,"%replace(%msg){'\s', ''}" 將會移除事件消息中所有空格。<br />模式 'p' 可以是任意復雜的甚至由多個轉換字符組成。例如,"%replace(%logger %msg){'.', '/'}" 將會替換 logger 以及消息中所有的點為斜桿。
rEx{depth} rootException{depth} rEx{depth, evaluator-1, ..., evaluator-n} rootException{depth, evaluator-1, ..., evaluator-n} 輸出與日志事件相關的堆棧信息,根異常將會首先輸出,而是標準的"根異常最后輸出"。下面是一個輸出例子:<br /><pre>java.lang.NullPointerException<br /> at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3]<br /> at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]<br />Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat': <br /> at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) [spring-2.0.jar:2.0]<br /> at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) [spring-2.0.jar:2.0]<br /> at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) [tomcat-6.0.26.jar:6.0.26]</pre>%rootException 跟 %xException 類似,也允許一些可選的參數,包括深度以及 evaluator。它也會輸出包信息。簡單來說,%rootException 跟 %xException 非常的類似,僅僅是異常輸出的順序完全相反。<br /> %rootException 的作者 Tomasz Nurkiewicz 在他的博客說明了他所作的貢獻 "Logging exceptions root cause first"

% 有特殊的含義

在給定的轉換模式上下文中,% 有特殊的含義。如果作為字面量,需要進行轉義。例如,"%d %p % %m%n"。

轉換字符對字面量的限制

在大多數的情況下,字面量包括空格或者其它的分隔符,所以它們不會與轉換字符混淆。例如,"%level [%thread] - %message%n" 包含字面量字符 " [" 與 "] - "。但是,如果一個轉換字符后面緊跟著一個字面量,那么 logback 的模式解析器將會錯誤的認為這個字面量也是轉換字符的一部分。例如,"%date%nHello" 將會被解析成兩個轉換字符 %date 與 %nHello,但是 %nHello 不是一個轉換字符,所以 logback 將會輸出 %PARSER_ERROR[nHello]。如果你想要區分 %n 跟 Hello,可以通過給 %n 傳遞一個空參數。例如,"%date%n{}Hello" 將會被解析為 %date %n 再緊跟著一個字符串 "Hello"。

格式修改器

默認情況下,相關信息按照原樣輸出。但是,在格式修改器的幫助下,可以對每個數據字段進行對齊,以及更改最大最小寬度。

可選的格式修改器放在百分號跟轉換字符之間。

第一個可選的格式修改器是左對齊標志,也就是減號 (-) 字符。接下來的是最小字段寬度修改器,它是一個十進制常量,表示輸出至少多少個字符。如果字段包含很少的數據,它會選擇填充左邊或者右邊,直到滿足最小寬度。默認是填充左邊 (右對齊),但是你可以通過左對齊標志來對右邊進行填充。填充字符為空格。如果字段的數據大于最小字段的寬度,會自動擴容去容納所有的數據。字段的數據永遠不會被截斷。

這個行為可以通過使用最大字段寬度修改器來改變,它通過一個點后面跟著一個十進制常量來指定。如果字段的數據長度大于最大字段的寬度,那么會從數據字段的開頭移除多余的字符。舉個??,如果最大字段的寬度是 8,數據長度是十個字符的長度,那么開頭的兩個字符將會被丟棄。這個行為跟 C 語言中 printf 函數從后面開始截斷的行為相違背。

如果想從后面開始截斷,可以在點后面增加一個減號。如果是這樣的話,最大字段寬度是 8,數據長度是十個字符的長度,那么最后兩個字符將會被丟棄。

下面是各種格式修改器的例子:

格式修改器 左對齊 最小寬度 最大寬度 備注
%20logger false 20 none 如果 logger 的名字小于 20 個字符的長度,那么會在左邊填充空格
%-20logger true 20 none 如果 logger 的名字小于 20 個字符的長度,那么會在右邊填充空格
%.30logger NA none 30 如果 logger 的名字大于 30 個字符的長度,那么從前面開始截斷
%20.30logger false 20 30 如果 logger 的名字大于 20 個字符的長度,那么會從左邊填充空格。但是如果 logger 的名字大于 30 字符,將會從前面開始截斷
%-20.30logger true 20 30 如果 logger 的名字小于 20 個字符的長度,那么從右邊開始填充空格。但是如果 logger 的名字大于 30 個字符,將會從前面開始截斷
%.-30logger NA none 30 如果 logger 的名字大于 30 個字符的長度,那么從后面開始截斷

下面的表格列出了格式修改器截斷的例子。但是請注意綜括號 "[]" 不是輸出結果的一部分,它只是用來區分輸出的長度。

格式修改器 logger 的名字 結果
[%20.20logger] main.Name [???????????main.Name]
[%-20.20logger] main.Name [main.Name???????????]
[%10.10logger] main.foo.foo.bar.Name [o.bar.Name]
[%10.-10logger] main.foo.foo.bar.Name [main.foo.f]

只輸出日志等級的一個字符

除了可以輸出 TRACE, DEBUG, WARN, INFO 或者 ERROR 來表示日志等級之外,還是輸出T, D, W, I 與 E 來進行表示。你可以自定義轉換器 或者利用剛才討論的格式修改器來縮短日志級別為一個字符。這個轉換說明符可能為 "%.-1level"。

轉換字符的選項

一個轉換字符后面可以跟一個選項。它們通過綜括號來聲明。我們之前已經看到了一些可能的選項。例如之前的 MDC 轉換說明符 %mdc{someKey}

一個轉換說明符可能有多個可選項。一個轉換說明符可以充分利用我們即將介紹到的 evaluator,可以添加多個 evaluator 的名字到可選列表。如下:

<pattern>%-4relative [%thread] %-5level - %msg%n \
  %caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</pattern>

如果這些選項中包含了一些特殊字符,例如花括號,空格,逗號。你可以使用單引號或者雙引號來包裹它們。例如:

<pattern>%-5level - %replace(%msg){'\d{14,16}', 'XXXX'}%n</pattern>

我們傳遞 \d{16}XXXXreplace 轉換字符。它將消息中 14,15 或者 16 位的數字替換為 XXXX,用來混淆信用卡號碼。在正則表達式中,"\d" 表示一個數字的簡寫。"{14,16}" 會被解析成 "{14,16}",也就是說前一個項將會被重復至少 14 次,至多 16 次。

特殊的圓括號

在 logback 里,模式字符串中的圓括號被看作為分組標記。因此,它能夠對子模式進行分組,并且直接對子模式進行格式化。在 0.9.27 版本,logback 開始支持綜合轉換字符,例如 %replace 可以對子模式進行轉換。

例如一下模式:

%-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n

將會對子模式 "%d{HH:mm:ss.SSS} [%thread]" 進行分組輸出,為了在少于 30 個字符時進行右填充。

如果沒有進行分組將會輸出:

13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2

如果對 "%-30()" 進行分組將會輸出:

13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
13:09:38 [btpool0-7]       INFO  c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7]       DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Found factor 2

后者的格式更加容易閱讀。

如果你想將圓括號當作字面量輸出,那么你需要對每個圓括號用反斜杠進行轉義。就像 (%d{HH:mm:ss.SSS} [%thread]) 一樣。

著色

如上所述的圓括號分組,允許對子模式進行著色。在 1.0.5 版本,PatternLayout 可以識別 "%black","%red","%green","%yellow","%blue","%magenta","%cyan", "%white", "%gray", "%boldRed","%boldGreen", "%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite" 以及 "%highlight" 作為轉換字符。這些轉換字符都還可以包含一個子模式。任何被顏色轉換字符包裹的子模式都會通過指定的顏色輸出。

下面是關于著色的配置文件。

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--              在 Windows 平臺下,設置 withJansi = true 來開啟 ANSI 顏色代碼需要 Jansi 類庫 -->
<!--              需要在 classpath 引入 org.fusesource.jansi:jansi:1.8 包 -->
<!--              在基于 Unix 操作系統,像 Linux 以及 Mac OS X 系統默認支持 ANSI 顏色代碼 -->
        <withJansi>true</withJansi>
        <encoder>
            <pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
        </encoder>
    </appender>
    
    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

下面是相關的輸出:

[main] WARN  c.l.TrivialMain - a warning message 0
[main] DEBUG c.l.TrivialMain - hello world number1
[main] DEBUG c.l.TrivialMain - hello world number2
[main] INFO  c.l.TrivialMain - hello world number3
[main] DEBUG c.l.TrivialMain - hello world number4
[main] WARN  c.l.TrivialMain - a warning message 5
[main] ERROR c.l.TrivialMain - Finish off with fireworks

其實是有顏色的,但是 md 不支持直接對字體顏色進行操作,而我懶得去折騰 HTML

只需要幾行代碼就可以創建一個著色轉換字符。在自定義轉換說明符部分,我們將討論怎樣在配置文件中注冊一個轉換字符。

Evaluators

像之前提到的,當一個轉換字符需要基于一個或者多個 EventEvaluator 對象動態表現時,EventEvaluator 對象根據規則可以決定給定的日志事件是否匹配。

讓我們來回顧一下包含 EventEvaluator 的例子。下一個配置文件輸出日志事件到控制臺,顯示日期,線程,日志級別,消息,以及調用者數據。獲取日志事件調用者的信息成本比較高,只有當日志請求來源特定的 logger,或者消息包含特定的字符串時,我們才會這樣做。換句話說,在調用者信息是多余的情況下,我們不應該去影響應用的性能。

Evaluator 與 評價表達式 (evaluation expressions) 都會在第七章 詳細介紹。如果你想利用 evaluator 去做一些有意思的事情,你必須看一下對這個的詳細介紹。下面的例子基于 JaninoEventEvaluator,所以需要 Janino 類庫。查看相關文檔進行設置。

Example: callerEvaluatorConfig.xml

<configuration>
  <evaluator name="DISP_CALLER_EVAL">
    <expression>logger.contains("chapters.layouts") &amp;&amp; \
      message.contains("who calls thee")</expression>
  </evaluator>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <encoder>
      <pattern>
        %-4relative [%thread] %-5level - %msg%n%caller{2, DISP_CALLER_EVAL}
      </pattern>
    </encoder>
  </appender>

  <root level="DEBUG"> 
    <appender-ref ref="STDOUT" /> 
  </root>
</configuration>

上面的評價表達式用來匹配從名為 "chapters.layouts" logger 發出,并且消息中包含字符串 "who calls thee" 的日志事件。由于 XML 的編碼規則,& 符號需要被轉義為 &amp;

下面的類利用了配置文件中所提到的特性。

Example: CallerEvaluatorExample.java

package chapters.layouts;

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

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

public class CallerEvaluatorExample {

  public static void main(String[] args)  {
    Logger logger = LoggerFactory.getLogger(CallerEvaluatorExample.class);
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();

    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(lc);
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
      // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

    for (int i = 0; i < 5; i++) {
      if (i == 3) {
        logger.debug("who calls thee?");
      } else {
        logger.debug("I know me " + i);
      }
    }
  }
}

上面的應用沒有什么特別的地方。發出五條日志請求,第三條的的請求信息為 "who calls thee?"。

通過命令:

java chapters.layouts.CallerEvaluatorExample src/main/java/chapters/layouts/callerEvaluatorConfig.xml

將會輸出:

0    [main] DEBUG - I know me 0 
0    [main] DEBUG - I know me 1 
0    [main] DEBUG - I know me 2 
0    [main] DEBUG - who calls thee? 
Caller+0   at chapters.layouts.CallerEvaluatorExample.main(CallerEvaluatorExample.java:28)
0    [main] DEBUG - I know me 4

當發出日志請求時,會評價相應的日志事件。僅僅只有第三個日志事件會匹配到評價規則,所以它的調用者信息會被展示出來。對于其它的日志事件,由于沒有匹配到評價規則,調用者信息不會被打印。

可以通過更改表達式來應對真實的應用場景。舉個??,你可以結合 logger 名與日志級別,日志級別在 WARN 以上的日志請求被當作一個敏感的部分,在金融業務模塊中,我們可以這樣做來獲取調用者的信息。

重要:評價表達式true 時,通過 caller 轉換字符,可以輸出調用者的信息。

考慮這么一種情況,當日志請求中包含異常信息時,它們的堆棧信息也會輸出。但是,對于某些特定的異常信息,可能需要禁止輸出堆棧信息。

下面的代碼創建了三條日志請求,每一條都包含一個異常信息。第二條的異常信息跟其它的不一樣,它包含 "do not display this" 字符串,并且它的異常信息類型為 chapters.layouts.TestException。現在讓我們來阻止第二條日志的打印。

Example: ExceptionEvaluatorExample.java

package chapters.layouts;

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

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

public class ExceptionEvaluatorExample {

  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(ExceptionEvaluatorExample.class);
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();

    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(lc);
      lc.reset();
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
       // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

    for (int i = 0; i < 3; i++) {
      if (i == 1) {
        logger.debug("logging statement " + i, new TestException(
            "do not display this"));
      } else {
        logger.debug("logging statement " + i, new Exception("display"));
      }
    }
  }
}

下面的配置文件通過評價表達式來匹配包含 chapters.layouts.TextException 類型的日志事件,也就是我們之前說要禁止的異常類型。

Example: exceptionEvaluatorConfig.xml

<configuration>
  <!-- evaluator 需要在 appender 前面定義 -->
  <evaluator name="DISPLAY_EX_EVAL">
    <expression>throwable != null &amp;&amp; throwable instanceof  \
      chapters.layouts.TestException</expression>
  </evaluator>
        
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n%xEx{full, DISPLAY_EX_EVAL}</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

作者原文里面是 %ex,應該是筆誤

通過這個配置文件,每當日志請求中包含一個 chapters.layouts.TestException 時,堆棧信息不會被輸出。

通過如下命令啟動:

java chapters.layouts.ExceptionEvaluatorExample src/main/java/chapters/layouts/exceptionEvaluatorConfig.xml

將會輸出:

logging statement 0
java.lang.Exception: display
    at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:16)
logging statement 1
logging statement 2
java.lang.Exception: display
    at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:16)

作者原文還輸出了 jar 包的信息,是因為打包后通過命令行執行的 (I think ??)

第二條日志沒有堆棧信息,因為我們禁止 TextException 類型的堆棧信息。每條堆棧信息的最后用綜括號包裹起來的是具體的包信息

注意:%ex 轉換說明符中的評價表達式為 false 時,堆棧信息才會輸出。

自定義轉換說明符

我們可以在 PatternLayout 中使用內置的轉換字符。我們也可以使用自己新建的轉換字符。

新建一個自定義的轉換字符需要兩步。

第一步

首先,你必須繼承 ClassicConverter 類。ClassicConverter 對象負責從 ILoggingEvent 實例中抽取信息并輸出字符串。例如,%logger 對應的轉換器 LoggerConverter,可以從 ILoggingEvent 從抽取 logger 的名字,返回一個字符串。它可以縮寫 logger 的名字。

下面是一個自定義的轉換器,返回從創建開始經過的時間,單位為納秒。

Example: MySampleConverter

public class MySampleConverter extends ClassicConverter {

  long start = System.nanoTime();

  @Override
  public String convert(ILoggingEvent event) {
    long nowInNanos = System.nanoTime();
    return Long.toString(nowInNanos-start);
  }
}

這個實現非常簡單。MySampleConverter 繼承了 ClassicConverter 并實現了 convert 方法,返回從創建開始經過多少納秒。

第二步

第二步,我們必須讓 logback 知道這個新建的 Converter。所以我們需要在配置文件中進行聲明,如下:

Example: mySampleConverterConfig.xml

<configuration>

  <conversionRule conversionWord="nanos" 
                  converterClass="chapters.layouts.MySampleConverter" />
        
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%-6nanos [%thread] - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

執行命令如下:

java chapters.layouts.SampleLogging src/main/java/chapters/layouts/mySampleConverterConfig.xml 

輸出信息如下:

26113953 [main] - Everything's going well
26672034 [main] - maybe not quite...

可以看一下其它 Converter 的實現,例如 MDCConverter ,去定制更加復雜的功能,如可選處理。想創建自己的顏色主題,可以看一下 HighlightingCompositeConverter

HTMLLayout

HTMLLayout (包含在 logback-classic 中) 以 HTML 格式生成日志。HTMLLayout 通過 HTML 表格輸出日志,每一行對應一條日志事件。

下面是 HTMLLayout 通過默認的 CSS 樣式生成的。

[圖片上傳失敗...(image-740ad1-1547968731095)]

表格的列是通過轉換模式指定的。關于轉換模式的文檔請查看 PatternLayout。所以,你可以完全控制表格的內容以及格式。你可以選擇并且展示任何跟 PatternLayout 組合的轉換器。

一個值得注意的問題是使用 PatternLayout 中的 HTMLLayout 時,不要使用空格或者其它的字面量來分隔轉換說明符。轉換模式中的每個說明符都會被當做一個單獨的列。同樣的轉換模式中的每個文本塊也會被當作一個單獨的列,這會占用屏幕的空間。

下面的 HTMLLayout 相關的配置:

Example: htmlLayoutConfig1.xml

<configuration debug="true">
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="ch.qos.logback.classic.html.HTMLLayout">
        <pattern>%relative%thread%mdc%level%logger%msg</pattern>
      </layout>
    </encoder>
    <file>test.html</file>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

TrivialMain 包含一些消息以及一個結束異常。執行以下命令:

java chapters.layouts.TrivialMain src/main/java/chapters/layouts/htmlLayoutConfig1.xml

將會當前文件夾創建一個 test.html 文件。test.html 文件的內容與下面類似:

[圖片上傳失敗...(image-237110-1547968731096)]

堆棧信息

如果你使用 %ex 轉換字符去展示堆棧信息,那么將會創建一個列來展示堆棧信息。在大多數的情況下,列會為空,那么就會浪費屏幕的空間。而且,在單獨的列打印堆棧信息,輸出的結果閱讀起來有難度。但是,%ex 轉換字符不是唯一一個用來展示堆棧信息的。

原文第一個 %ex 為 %em

一個更好的解決辦法是通過實現 IThrowableRenderer 接口。實現的接口可以分配給 HTMLLayout 來管理相關的異常數據。默認情況下,會給每個 HTMLLayout 實例分配一個 DefaultThrowableRenderer。它將異常的堆棧信息寫入到表格新的一行,并且非常易讀,就跟上面展示的表格一樣。

如果在某些情況下,你仍然想要使用 %ex,那么你可以在配置文件中指定 NOPThrowableRenderer 來禁止在單獨一行展示堆棧信息。我們不理解為什么你要這樣做,但是你開心就好。

CSS

HTMLLayout 創建的 HTML 是通過 CSS 來控制樣式的。在缺少指定命令的情況下,HTMLLayout 會使用內部默認的樣式。但是,你可以告訴 HTMLLayout 去使用外部的 CSS 文件。通過在 <layout> 元素內置 <cssBuilder> 元素可以做到。如下所示:

<layout class="ch.qos.logback.classic.html.HTMLLayout">
  <pattern>%relative...%msg</pattern>
  <cssBuilder class="ch.qos.logback.classic.html.UrlCssBuilder">
    <!-- css 文件的路徑 -->
    <url>http://...</url>
  </cssBuilder> 
</layout>

HTMLLayout 通常與 SMTPAppender 配合使用,所以郵件可以被格式化成 HTML。

Log4j XMLLayout

XMLLayout (logback-classic 的一部分) 生成一個 log4j.dtd 格式的文件,用來與類似 Chainsaw 以及 Vigilog 這樣的工具進行交互操作,這些工具可以處理由 log4j XMLLayout 生成的文件。

跟 log4j 1.2.15 版本的 XMLLayout 一樣,logback-classic 中的 XMLLayout 接收兩個 boolean 屬性:locationInfoproperties。設置 locationInfo 的值為 true,可以在每個事件中開啟包含位置信息 (調用者的數據)。設置 properties 為 true,可以開啟包含 MDC 信息。默認情況下,兩個屬性都設置為 false。

下面是一個示例:

Example: log4jXMLLayout.xml

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>test.xml</file>
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="ch.qos.logback.classic.log4j.XMLLayout">
        <locationInfo>true</locationInfo>
      </layout>
    </encoder> 
  </appender> 

  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration> 

Logback access

大多數 logback-access 的 layout 僅僅只是 logback-classic 的 layout 的改編。logback-classic 與 logback-access 模塊定位不同的需求,但是都提供了類似的功能。

寫你自己的 layout

在 logback-access 中寫一個定制的 Layout 與在 logback-classic 的 Layout 中幾乎一致。

PatternLayout

配置 logback-access 中的 PatternLayout,與在 logback-classic 中配置相同。但是它添加了一些額外的轉換說明符來適應 HTTP 請求以及 HTTP 響應中特定信息位的記錄。

下表是 logback-access 中 PatternLayout 的相關轉換說明符。

轉換字符 效果
a / remoteIP 遠程 IP 地址
A / localIP 本地 IP 地址
b / B / bytesSent 響應內容的長度
h / clientHost 遠程 host
H / protocol 請求協議
l 遠程日志名,在 logback-access 中,轉換器總是返回 "-"
reqParameter{paramName} 響應參數。<br />這個轉換字符在花括號中接受一個參數,在請求中尋找相應的參數。<br /> %reqParameter{input_data} 展示相應的參數。
i{header} / header{header} 請求頭。<br /> %header{Referer} 顯示請求的來源。<br />如果沒有指定選項,將會展示所有可用的請求頭
m / requestMethod 請求方法
r / requestURL 請求 URL
s / statusCode 請求狀態碼
D / elapsedTime 請求所耗費的時間,單位為毫秒
T / elapsedSeconds 請求所耗費的時間,單位為秒
t / date 輸出日志事件的日期。日期說明符需要用花括號指定。日期格式來源 java.text.SimpleDateFormatISO8601 也是一個有效的值。<br />例如,%t{HH:mm:ss,SSS} 或者 %t{dd MMM yyyy ;HH:mm:ss,SSS}。如果沒有指定日期格式字符,那么會默認指定為 %t{dd/MMM/yyyy:HH:mm:ss Z}
u / user 遠程用戶
q / queryString 請求查詢字符串,前綴為 '?'
U / requestURI 請求 URI
S / sessionID Session ID.
v / server 服務器名
I / threadName 處理該條請求的線程
localPort 本地端口
reqAttribute{attributeName} 請求的屬性。<br />%reqAttribute{SOME_ATTRIBUTE} 展示相應的屬性。
reqCookie{cookie} 請求 cookie。<br />%cookie{COOKIE_NAME} 展示相應的 cookie。
responseHeader{header} 響應頭。<br />%header{Referer} 展示響應的來源。
requestContent 展示請求的內容,即請求的 InputStream。它與 TeeFilter 結合使用。一個使用 TeeHttpServletRequest 替代 HttpServletRequest 的 javax.servlet.Filter。前者可以多次訪問請求的 InputStream 而不會丟失內容。
fullRequest 請求的數據。包括所有的請求頭以及請求內容。
responseContent 展示響應的內容,也就是響應的 InputStream。 它與 TeeFilter 結合使用。一個使用 TeeHttpServletResponse 替代 HttpServletResponsejavax.servlet.Filter。前者可以多次訪問響應 (原文為請求) 的 InputStream 而不會丟失內容。
fullResponse 獲取響應所有可用的數據,包括所有的響應頭以及響應內容。

logback-access 的 PatternLayout 能夠識別三個關鍵字,有點類似快捷鍵。

關鍵字 相等的轉換模式
common or CLF %h %l %u [%t] "%r" %s %b
combined %h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"

關鍵字 common 對應 '%h %l %u [%t] "%r" %s %b',分別展示客戶端主機,遠程日志名,用戶,日期,請求 URL,狀態碼,以及響應內容的長度。

關鍵字 combined 對應 '%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"'。跟 common 有點類似,但是它還會再顯示兩個請求頭,referer 以及 user-agent。

HTMLLayout

logback-access 中的 HTMLLayout 與 logback-classic 中的 HTMLLayout 有點類似。

默認情況下,它會創建一個包含如下數據的表格:

  • 請求 IP (Remote IP)
  • 日期 (Date)
  • 請求 URL (Request URL)
  • 狀態碼 (Status code)
  • 內容長度 (Content Length)

下面是 logback-access 中的 HTMLLayout 輸出的一個例子:

[圖片上傳失敗...(image-75898-1547968731096)]

還有比真實的例子更好的例子嗎?我們自己的 log4j.properties 用于 logback 翻譯器,充分的利用了 logback-access 在線演示 RollingFileAppenderHTMLLayout 的輸出。

每一個新的用戶請求 翻譯器 這個網站,一個新的條目就會添加到訪問日志,你可以通過這個鏈接 查看。

掃碼關注公眾號:java之旅
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文地址為 https://github.com/logstash/logstash-logback-encode...
    飛來來閱讀 18,782評論 1 10
  • 在應用程序中添加日志記錄總的來說基于三個目的:監視代碼中變量的變化情況,周期性的記錄到文件中供其他應用進行統計分析...
    時待吾閱讀 5,030評論 0 6
  • 轉自:http://www.cnblogs.com/warking/p/5710303.html 一、logbac...
    chenzhong_閱讀 1,523評論 0 3
  • 在應用程序中添加日志記錄總的來說基于三個目的:監視代碼中變量的變化情況,周期性的記錄到文件中供其他應用進行統計分析...
    時待吾閱讀 5,084評論 1 13
  • 把我打開 看這衣裳下褶皺的肌膚 大地花多少年才長出山河 山又往上長 河又往下淌 而我數十載,快速輪回個體內的生命 ...
    復姜閱讀 416評論 1 3