第六章: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。為了去滿足這個需求,我們將一個包裹了我們自己定義的 MySampleLayout
的 LayoutWrappingEncoder
的實例傳遞給 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。下面是通過代碼配置 ConsoleAppender
與 PatternLayoutEncoder
使用的例子:
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:ss ,SSS 會被當作時區。如果你想在日期模式中使用逗號,那么你可以這樣使用,%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}
與 XXXX
給 replace
轉換字符。它將消息中 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") && \
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 的編碼規則,&
符號需要被轉義為 &
。
下面的類利用了配置文件中所提到的特性。
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 && 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 屬性:locationInfo
與 properties
。設置 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.SimpleDateFormat 。ISO8601 也是一個有效的值。<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 替代 HttpServletResponse 的 javax.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 在線演示 RollingFileAppender
與 HTMLLayout
的輸出。
每一個新的用戶請求 翻譯器 這個網站,一個新的條目就會添加到訪問日志,你可以通過這個鏈接 查看。