第6章 レイアウト
TCPの実装は頑健性の一般原則に従う。つまり、自分自身の行動は保守的に、他者から行われる行動については寛容さを持って受け入れるということだ。
-JON POSTEL、RFC 793
レイアウトとは何か
レイアウトといってもフロリダ州の大規模集合住宅とは何の関係もありません。レイアウトは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(); }
このインターフェイスは簡潔ですが、あらゆる書式化のニーズを十分に満たしています。Catch-22に登場するテキサス育ちの開拓者であるジョセフ・ヘラーならこう叫んでいるところです。「レイアウトを実装しようにもたったの5つしかメソッドがない!!?ナンデ?!」
Logback-classic モジュール
logback-classic モジュールはch.qos.logback.classic.spi.ILoggingEvent
だけを扱うようになっています。この節を読み進めていけば理由が明らかになります。
レイアウトを自作する
logback-classic モジュールで使うために、簡潔で十分な機能性のあるレイアウトを自作してみましょう。出力したいのは次のようなものです。アプリケーションが起動してから経過した時間、ロギングイベントのログレベル、ブラケットで囲んだ呼び出しスレッド名、ロガー名、ダッシュ(-のこと)とそれに続くロギングイベントのメッセージ、最後に改行文字も加えておきましょう。
出力例は次のようになります。
テキサスの開拓者が実装したレイアウトのコードを見てましょう。
例:自作レイアウトのサンプル実装(logback-examples/src/main/java/chapters/layouts/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
を継承しているのに気付きましたか。このクラスは全てのレイアウトのインスタンスに共通する内部状態を管理するものです。例えば、レイアウトが開始しているか、停止しているか、ヘッダーはあるか、フッターはあるか、コンテンツタイプはあるか、といったものです。おかげで、開発者は書式化の方法にだけ集中することができるのです。LayoutBase
はジェネリッククラスです。上記のコードでは、MySampleLayout
はLayoutBase<ILoggingEvent>
を継承しています。
doLayout(ILoggingEvent event)
メソッド(MySampleLayout
の実装する唯一のメソッドです)では、最初に空のStringBuffer
を生成して、ロギングイベントのパラメータを付け足していきます。開拓者らしく、慎重に書式化したようです。ロギング要求で1つ以上のパラメータが渡されることを考えればこれは大事なことです。
doLayout()
メソッドでは、StringBufferにいろいろな文字列を追加したあとで1つの文字列に変換して、その文字列を呼び出し元に返します。
上記のdoLayout
メソッドでは、ロギングイベントに含まれる可能性のあるあらゆる例外を無視します。実際のレイアウトの実装では、ほぼ確実に例外の内容を出力したいはずです。
自作レイアウトの設定
自作レイアウトは、他のコンポーネントと同じように設定されます。前述したようにFileAppender
とその派生クラスにはエンコーダーが必要です。FileAppender
のニーズを満たすため、LayoutWrappingEncoder
に自作したMySampleLayout
をラップしてみましょう。次のような設定になります。
例:MySampleLayoutの設定(logback-examples/src/main/java/chapters/layouts/sampleLayoutConfig.xml)
Groovyで表示<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
は、一つ目の引数で指定された設定ファイルを使ってlogbackを設定します。そして、デバッグメッセージとエラーメッセージを1つづつロギングします。
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
です。このマニュアル全体を通じて言えることですが、レイアウトでもlogbackの他のコンポーネントでも、単にセッターメソッドを設定するだけでプロパティを指定できるようになります。
MySampleLayout2
クラスには、2つのプロパティがあります。一つ目は、出力されるメッセージの先頭に追加する接頭辞です。二つ目は、ロギング要求を送信したスレッドの名前を表示するかどうかを選択する真偽値です。
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(); } }
プロパティのセッターメソッドを追加すれば設定ファイルから指定できるようになります。printThreadName
プロパティの型は真偽値(boolean)であって文字列(String
)ではないので注意してください。logbackのコンポーネントの設定については、設定に関する章で詳しく説明しています。Joranの章にはより詳細な説明があります。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-classic の配布物には、PatternLayout
と呼ばれる柔軟性のあるレイアウトが含まれています。他のレイアウトと同じく、PatternLayout
はロギングイベントを受け取って文字列
を返します。文字列
は、変換パターン文字列を調整してカスタマイズすることができます。
PatternLayout
の変換パターン文字列はC言語のprintf()
関数と非常によく似たもので、文字列リテラルと変換指定と呼ばれる書式制御式で構成されています。変換パターン文字列には、どんな文字列リテラルでも入れることができます。変換指定はパーセント記号"%"で始まり、オプションの書式修飾子、変換指定子、括弧で囲まれたパラメータが続いたものです。変換指定子には変換したいデータフィールドを指定します。例えばロガー名、レベル、日付、スレッド名などです。書式修飾子には、フィールド幅、パディング、左揃えや右揃えを指定します。
既に何度か述べたとおり、FileAppender
とその派生クラスにはエンコーダーが必要です。結局、FileAppender
やその派生クラスとPatternLayout
を一緒に使うには、エンコーダーでラップしなければなりません。FileAppender
とPatternLayout
を組み合わせることがあまりにも一般的になってしまったことを考慮して、logback の配布物にはPatternLayoutEncoder
を含めるようになりました。これは単にPatternLayout
をラップするだけのエンコーダーなので、エンコーダーであるにも関わらずPatternLayoutのように扱えるようになっています。プログラム的にConsoleAppender
とPatternLayoutEncoder
を設定する例を示します。
例:PatternLayoutの使用例(logback-examples/src/main/java/chapters/layouts/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] %msg%n"が設定されています。logbackに組み込みの変換指定子については後で簡単に説明します。PatternSample
アプリケーションを実行してみましょう。
java java chapters.layouts.PatternSample
コンソールにに次のように出力されます。
DEBUG [main]: Message 1 WARN [main]: Message 2
変換パターン文字列の"%-5level [%thread] %msg%n"には、文字列リテラルと変換指定子を明示的に区切る文字が無いことに気付きましたか。変換パターン文字列を解析する際、PatternLayout
は文字列リテラル(空白文字、括弧、コロンなど)と変換指定を分離することができます。上記の例では、変換指定"%-5level"は、ロギングイベントのレベルを5文字の幅に左揃えすることを意味します。書式指定子については後で説明します。
PatternLayout
は変換パターン文字列をグループ化するために括弧を使います。つまり '('と ')'には特別な意味があるので、リテラル文字列として使うときはエスケープしなければならないということです。 括弧の特殊な性質について詳しくは後で説明します。
前述のとおり、変換指定によっては括弧で囲んでオプションの引数を指定することができます。例えばこういう書き方です。%logger{10}
"logger" が変換指定子で、10が引数です。オプションの指定の仕方について詳しくは後で説明します。
利用できる変換指定とオプションを表にまとめました。1つのセルに複数の変換指定子が登場する場合、それらは別名という意味です。
変換指定子 | 効果 | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
c{length} lo{length} logger{length} |
ロギングイベントを生成する一番元になったロガーの名前を出力します。
この変換指定子に指定できるオプションは整数値だけです。ロガー名は省略アルゴリズムに従って、意味が通る程度に短縮されます。0を指定すると特別な振る舞いをします。ロガー名文字列の中で一番右端のドット(.)から右側だけを残すようになります。省略アルゴリズムの例を表にまとめました。
オプションで指定した長さを越えるとしても、一番右側のドット(.)から後ろは省略されないので気をつけてください。それ以外の部分は最短で1文字になりますが、削除することはありません。 |
||||||||||||||||||||||||
C{length} class{length} |
ロギング要求を生成した呼び出し元のクラスの完全名を出力します。 %loggerと同じように、引数の整数値に合わせて名前を短縮します。0には特別な意味があり、パッケージ名を除いた単純クラス名を出力するようになります。デフォルトでは、完全クラス名が出力されます。 送信側のクラス情報を生成するのはとても高速とは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。 |
||||||||||||||||||||||||
contextName cn |
ロギングイベントを生成したロガーの割り当てられたロギングコンテキストの名前。 | ||||||||||||||||||||||||
d{pattern} date{pattern} d{pattern, timezone} date{pattern, timezone} |
ロギングイベントの日時を出力するために使います。引数として日時パターン文字列を指定することができます。日時パターン文字列は 引数に"ISO8601"と指定すると ISO8601 の書式になります。日時パターン文字列が指定されなかった場合、デフォルトはISO8601書式になるので注意してください。 いくつかサンプルを見てください。ここでは日時が2006年10月20日(金曜日)であることを想定しています。これはこのマニュアルを書いた人が昼食から戻ってきた日時です。
二番目の引数にはタイムゾーンを指定します。たとえば、'%date{HH:mm:ss.SSS, Australia/Perth}' と指定すると、世界中で一番孤立しているオーストラリアのパースの時刻を出力するようになります。タイムゾーンが指定されなかった場合、JVMのタイムゾーンが使用されます。指定したタイムゾーンが未知のものであったりタイプミスだった場合、TimeZone.getTimeZone(String)メソッドの仕様に基づいてGMTが指定されたものとして解釈します。 よくある間違いは、カンマ(,)が引数の区切り文字として解釈されてしまうことです。 |
||||||||||||||||||||||||
F file |
ロギング要求を発行したクラスのソースコードファイル名を出力します。 ファイル情報を生成するのはとても高速であるとは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。 |
||||||||||||||||||||||||
caller{depth} caller{depth, evaluator-1, ... evaluator-n} |
ロギングイベントを生成した呼び出し元の位置情報(スタックの深さ、ソースコードファイルの行番号)。 位置情報の中身はJVM実装によって変わりますが、普通ならロギングイベントを生成したメソッドの定義されたクラスの完全名、ソースコードファイル名、行番号が含まれます。 表示されるメソッド呼び出しの深さを指定するため、オプションとして整数値を指定できます。 例えば、%caller{2}と書いたら次のように出力されます。 0 [main] DEBUG - logging statement Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) そして%caller{3}と書いたら次のように出力されます。 16 [main] DEBUG - logging statement Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) Caller+2 at mainPackage.ConfigTester.main(ConfigTester.java:38) ロギングイベントの送信元情報を計算するかどうかを判定するため、オプションとして評価器を指定できるようになっています。たとえば、%caller{3,CALLER_DISPLAY_EVAL}と指定すると評価器のCALLER_DISPLAY_EVALが真を返す場合にだけ、3行分のスタックトレースを出力することになります。 評価器の種類は後で紹介します。 |
||||||||||||||||||||||||
L line |
ロギング要求が生成されたソースコードファイル中の行番号を出力します。 行番号を算出するのは決して高速とは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。 |
||||||||||||||||||||||||
m msg message |
アプリケーションがロギングイベントに関連付けたメッセージを出力します。 |
||||||||||||||||||||||||
M method |
ロギング要求が生成されたメソッド名を出力します。 メソッド名を生成するのは決して高速とは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。 |
||||||||||||||||||||||||
n |
プラットフォーム依存の行区切り文字を出力します。 この変換指定子を使っても、行区切り文字として可搬性の無い"\n" や"\r\n" を指定した場合と性能は変わりません。したがって、どんなときでも行区切り文字そのものではなく、この変換指定子を使うべきです。 |
||||||||||||||||||||||||
p le level |
ロギングイベントのレベルを出力します。 | ||||||||||||||||||||||||
r relative |
アプリケーションが開始してから、ロギングイベントを生成するまでの経過時間をミリ秒単位で出力します。 | ||||||||||||||||||||||||
t thread |
ロギングイベントを生成したスレッドの名前を出力します。 | ||||||||||||||||||||||||
X{key:-defaultVal} mdc{key:-defaultVal} |
ロギングイベントを生成したスレッドに関連付けられていたMDC(診断コンテキスト)の値を出力します。 %mdc{uesrid}のようにmdcオプションにキーが指定されている場合、対応するMDCの値が出力されます。MDCの値がnullの場合、 :-演算子で指定したデフォルト値が出力されます。デフォルト値が無かったら空文字列が出力されます。 キーが未指定のときは、MDCの内容がすべて出力されます。そのときの書式は"key1=val1,key2=val2"のようになります。 詳しくはMDCの章を参照してください。 |
||||||||||||||||||||||||
ex{depth} exception{depth} throwable{depth} ex{depth, evaluator-1, ..., evaluator-n} exception{depth, evaluator-1, ..., evaluator-n} throwable{depth, evaluator-1, ..., evaluator-n} |
ロギングイベントに例外オブジェクトが関連付けられていたら、その例外オブジェクトのスタックトレースを出力します。デフォルトでは完全なスタックトレースを出力します。 throwable変換指定子に指定できるオプションは次のとおりです。
いくつか例を見てみましょう。
スタックトレースを出力するかどうかを判定するため、オプションとして評価器を指定できるようになっています。例えば、%ex{full,EX_DISPLAY_EVAL}と指定した場合、評価器EX_DISPLAY_EVALが偽を返すときだけ例外オブジェクトの完全なスタックトレースが出力されます。評価器についてはこのドキュメントの後のほうで説明しています。 |
||||||||||||||||||||||||
xEx{depth} xException{depth} xThrowable{depth} xEx{depth, evaluator-1, ..., evaluator-n} xException{depth, evaluator-1, ..., evaluator-n} xThrowable{depth, evaluator-1, ..., evaluator-n} |
クラスのパッケージングに関する情報が追加されていること以外は%throwableと同様です。 変換パターン文字列に%xThrowableや他のthrowable関連の変換指定子を指定しなかった場合、 例外のスタックトレースの各行の終わりに、そのクラスが含まれるjarファイル名とMANIFEST.MFに書かれた"Implementation-Version"が追加されます。この画期的なテクニックを考案したのはJames Strachanです。パッケージングの情報が不確かな場合、先頭にチルダ(~)が付きます。 例を見てみましょう。 java.lang.NullPointerException at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3] at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0_06] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.5.0_06] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.5.0_06] at java.lang.reflect.Method.invoke(Method.java:585) ~[na:1.5.0_06] at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) [junit-4.4.jar:na] at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) [junit-4.4.jar:na] ...etc logbackはパッケージング情報が正しく出力されるよう、適切な幅を確保します。どれだけクラスローダーの階層が複雑になっていても頑張ります。残念ながら正確性が担保できないときは先頭にチルダ(~)を付けます。従って、理屈の上では実際のパッケージング情報とは異なる内容を出力させることも可能です。ですから、前の例で Wombat クラスのパッケージング情報の先頭にはチルダ(~)があるので、本当は [wombat.jar:1.7] となるはずだったかもしれないのです。 利用者からのフィードバックによると、NetBeansはパッケージング情報を縮めてしまうそうです。Netbeansのユーザーは、変換パターン文字列の最後に"%ex"を追加して、スタックトレースにパッケージング情報が出てこないようにしたほうが良いでしょう。たとえば、"%d %logger - %m%n" という変換パターン文字列を使っているなら、"%d %logger - %m%n%ex" と書き換えればよいのです。 |
||||||||||||||||||||||||
nopex nopexception |
スタックトレースの情報を扱うように見えますが、実際は何も出力しません。つまり、うまく例外を無視できるのです。 %nopex変換指定子を使うと、 |
||||||||||||||||||||||||
marker |
ロギング要求に関連付けられたマーカーを出力します。 マーカーに子マーカーがある場合、次のような書式で両方のマーカーを出力します。 parentName [ child1, child2 ] |
||||||||||||||||||||||||
property{key} | キーという名前のプロパティに設定された値を出力します。関連するドキュメントは変数の定義と変数のスコープです。 キーがロガーコンテキストのプロパティではなかったら、システムプロパティを探します。 キーに対するデフォルト値はありません。プロパティが見つからなかったら、"Property_HAS_NO_KEY"という文字列が値になります。エラーであることがすぐにわかりますね。 |
||||||||||||||||||||||||
replace(p){r, t} |
変換パターン文字列"p"について、正規表現'r'にマッチした部分を文字列't'で置き換えます。たとえば、"%replace(%msg){'\s',''}" とすると、ロギングイベントに設定されたメッセージに含まれるすべての空白文字を削除します。 変換パターン文字列"p"はどれだけ複雑になってもいいですし、複数の変換指定を含めることもできます。たとえば、"%replace(%logger %msg)'\.','/'}" とすると、ロガー名とメッセージに含まれる全てのドット(.)をスラッシュ(/)で置き換えます。 |
||||||||||||||||||||||||
rEx{depth} rootException{depth} rEx{depth, evaluator-1, ..., evaluator-n} rootException{depth, evaluator-1, ..., evaluator-n} |
ロギングイベントに例外オブジェクトが関連付けられていたら、その例外オブジェクトのスタックトレースを出力します。 標準とは逆に、例外の発生元から順番にスタックトレースを出力します。こんな出力になります(サンプルなのでだいぶ削っています)。 java.lang.NullPointerException at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3] at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3] Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat': at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) [spring-2.0.jar:2.0] at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) [spring-2.0.jar:2.0] at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) [tomcat-6.0.26.jar:6.0.26] %rootExceptionには、%xExceptionと同じオプションを指定できます。それに、パッケージング情報も出力します。簡単に言うと、%rootException は %xException とほとんど変わりませんが、スタックトレースの出力順だけが逆になっているのです。 %のrootExceptionの作者であるTomasz Nurkiewiczは、自身のブログエントリ"Logging eceptions root cause first"で解説しています。 |
%文字の特別な意味
変換パターン文字列というコンテキストにおいて、%文字には特別な意味があります。文字列リテラルとして使用するにはバックスラッシュ(\)を前につけてエスケープしなければなりません。たとえば "%d %p \%%m%n" といったようにします。
変換指定子の直後の文字列リテラルの制限
ほとんどの場合文字列リテラルにはスペースや他の区切り文字が含まれるので、変換指定子と混同されることはありません。例えば、変換パターン文字列"%level [%thread] - %mmessage%n" には文字列リテラル " ["
と"] - "
が含まれています。しかし、Javaの識別子に使える文字リテラルが変換指定子の直後に現れると、logbackの文字列パターン解析器はその文字リテラルも変換指定子の一部だと勘違いしてしまいます。例えば、変換パターン文字列 "%date%nHello" は、%date と %nHello という二つの変換指定子として解釈されてしまいます。もちろん、%nHello は変換指定子に存在しないため、logback は %PARSER_ERROR[nHello] というエラーを出力するでしょう。%nのすぐ後に文字列リテラル "Hello" を出したければ、%nに空の引数リストを指定すればよいでしょう。たとえば、"%date%n{}Hello" とすれば、%date %n 文字列リテラル "Hello" という順に解釈させることができるでしょう。
書式修飾子
デフォルトでは、関連する情報はそのまま出力されます。しかし、書式修飾子を使えばそれぞれのデータを出力する幅の最小値と最大値、それに文字揃えを変えることが出来ます。
書式修飾子のオプションは%記号と修飾文字の間に入れます。
最初に紹介する書式修飾子のオプションは左揃えです。これはただのマイナス(-)を指定します。次は最小幅です。整数値で出力する文字数を指定します。出力する文字列長が指定した文字数より小さい時は、最小幅を全てうめるまで右側か左側のどちらかをパディングします。デフォルトは右揃えなので左側にパディングしますが、左揃えにして右側にパディングさせることもできます。パディング文字は半角スペースです。出力する文字列長が指定した文字数より大きい時は、全て出力できるように幅を広げます。後ろを切り捨てることはありません。
この振る舞いは最大幅を指定すれば変えられます。最大幅はドット(.)の後に整数値で指定します。出力する文字列長が指定した文字数より大きい時は、溢れた分だけ出力する文字列の先頭から削除されます。たとえば、最大幅を8にしたとき、出力する文字列長が10だったら、先頭の2文字が削除されることになります。C言語のprintf関数なら文字列の後ろのほうが削除されるので、逆の振る舞いをすることになります。
ドット(.)に続けてマイナス文字(-)を指定すれば、後ろから削除することもできます。その場合、最大幅が8として、出力する文字列長が10だったら、末尾の2文字が削除されることになります。
書式修飾子の例をいくつか見ていきましょう。
書式修飾子 | 左寄せ | 最小幅 | 最大幅 | コメント |
---|---|---|---|---|
%20logger | しない | 20 | なし | ロガー名が20文字未満であれば半角スペースで左側にパディングします。 |
%-20logger | する | 20 | なし | ロガー名が20文字未満であれば半角スペースで右側にパディングします。 |
%.30logger | 指定できない | なし | 30 | ロガー名が30文字を超える場合、先頭から切り捨てます。 |
%20.30logger | しない | 20 | 30 | ロガー名が20文字未満であれば半角スペースで左側にパディングします。ロガー名が30文字を超える場合、先頭から切り捨てます。 |
%-20.30logger | する | 20 | 30 | ロガー名が20文字未満であれば半角スペースで右側にパディングします。ロガー名が30文字を超える場合、先頭から切り捨てます。 |
%.-30logger | 指定できない | なし | 30 | ロガー名が30文字を超える場合、末尾から切り捨てます。 |
書式修飾子による文字列の切り捨ての例を表にまとめました。角括弧"[]"は出力される文字列ではなく、出力幅を示しているだけなので注意してください。
書式修飾子 | ロガー名 | 結果 |
---|---|---|
[%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] |
レベルを1文字で出力する
ロギングレベルをTRACE、DEBUG、WARN、INFO、ERRORのような文字列として出力するのではなく、T、D、W、I、Eのように一文字だけ出力したい場合もあるでしょう。カスタムコンバーターを実装してもできるでしょうし、書式修飾子を使うこともできます。書式修飾子を使うなら"%.-1level"
とすればよいでしょう。
変換指定子のオプション
変換指定子にはオプションを指定することができます。オプションは必ず中括弧で囲むようにします。オプションで何ができるのか、すでに目にしてきたものがあります。たとえば、%mdc{someKey}のようにMDC変換指定子と組み合わせることができます。
変換指定子のオプションは複数になるかもしれません。たとえば、評価器を使う変換指定子があります。すぐ後で説明しますが、こんな風に評価器の名前を複数並べることがあるかもしれません。
<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>
この例では、replace
変換指定子にオプションとして\d{14,16}
とXXXX
を指定しています。これは、メッセージ中に14桁から16桁の数字文字列があったらそれをXXXXで置き換えるものです。クレジットカード番号をマスクするのに役立ちます。"\d"は一桁の数字を表す正規表現の省略形です。後ろに"{14,16}"をつけているので、一つ前の文字、つまり一桁の数字が14個から16個繰り返される場合にマッチすることになります。
括弧は特別扱い
logbackでは、パターン文字列の中の括弧は、トークンをグループ化するものとして扱います。それぞれのグループはそれ自体を1つのパターン文字列として扱うことができます。logback0.9.27から、%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]\)"
カラー化
括弧でグループ化した部分パターン文字列には色を指定することができます。logback1.0.5からPatternLayout
で指定できるようになった色付け用の変換指定子は次のとおりです。
- "%black"
- "%red"
- "%green"
- "%yellow"
- "%blue"
- "%magenta"
- "%cyan"
- "%white"
- "%gray"
- "%boldRed"
- "%boldGreen"
- "%boldYellow"
- "%boldBlue"
- "%boldMagenta"
- "%boldCyan"
- "%boldWhite"
- "%highlight"
色付けをわかりやすく説明する設定ファイルを次に示します。%cyan変換指定子には部分パターン文字列として"%logger{15}"を指定してあるのがわかりますか。こうすると、ロガー名は15文字に短縮されるだけでなく、文字色がシアンになります。%highlight変換指定子を使うと、部分パターン文字列の文字色が、ログレベルがERRORなら赤太字、ログレベルがWARNなら赤字、ログレベルがINFOなら青字、それ以外のログレベルならデフォルトの色になります。
例:ログレベルに応じた強調表示(logback-examples/src/main/java/chapters/layouts/highlighed.xml)
Groovyとして表示<configuration debug="true"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- On Windows machines setting withJansi to true enables ANSI color code interpretation by the Jansi library. This requires org.fusesource.jansi:jansi:1.8 on the class path. Note that Unix-based operating systems such as Linux and Mac OS X support ANSI color codes by default. --> <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
色付け用変換指定子を使うとパターン文字列が複数行になってしまうこともあります。変換指定を自作するでは設定ファイルで使用できる変換指定子を登録する手順について議論しています。
評価器
前述したように、オプションをリストで指定できると、変換指定子にEventEvaluatorに基づく動的な振る舞いを複数指定できる必要があるときは便利です。
EventEvaluator
の役割は、ロギングイベントが基準に合致するかどうかを判定することです。
早速EventEvaluator
の例を見てみましょう。次の設定ファイルは、ロギングイベントの日付、スレッド、レベル、メッセージと送信者情報をコンソールに出力するものです。ロギングイベントの送信者情報を抽出するのはとてもコストが高いので、特定のロガーが生成したロギング要求の場合だけに限定しています。さらに、メッセージに特定の文字列が含まれている場合だけにしています。ですので、特定のロギングイベントについてだけ、送信者情報の生成と出力を行うことになります。そうすれば、送信者情報のいらないケースではアプリケーションの性能ペナルティが発生しません。
評価器と具体的な評価式については、フィルタの章の独立したセクションで紹介しています。もし評価器を実戦投入しようとしているなら必ず読んでおいてください。この後で紹介する例では、明示的に書いていませんがJaninoEventEvaluator
を使用しています。ですので実行するにはJainoライブラリが必要になります。設定方法をまとめたドキュメントの該当するセクションを読んでください。
例:EventEvaluatorsの使用例(logback-examples/src/main/java/chapters/callerEvaluatorConfig.xml)
Groovyとして表示<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" が含まれており、かつ、メッセージに"who clals thee" が含まれているロギングイベントにマッチするものです。XMLのエンコーディングルールに従うため、&文字は&のようにエスケープしなければなりません。
この設定ファイルを使用するクラスを見てみましょう。
例:EventEvaluatorsの使用例(logback-examples/src/main/java/chapters/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); } } } }
このアプリケーションは特別何もしません。forループでロギング要求を5回生成します。三回目だけ "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
ロギング要求が生成されたら、そのロギングイベントが評価されます。三回目のロギングイベントだけが評価条件にマッチするので、送信者情報が出力されます。他のロギングイベントは評価基準とマッチしないので送信者情報が出力されていません。
現実世界のシナリオに対応できるように評価式を変更することにしましょう。例えばロガー名とロギング要求のレベルを組み合わせることもできます。そうすれば、ロギング要求のレベルがWARNより高くて、かつ、アプリケーションの重要部品、例えばお金に絡むトランザクションを処理するモジュールで生成された場合にだけ送信者情報を出力する、といったことができるでしょう。
重要: caller変換指定によって送信者情報が出力されるのは、評価式がtrueと評価されたときだけです。
別の状況を考えてみましょう。ロギング要求に例外オブジェクトが設定されていると、スタックトレースも出力されます。しかし、特定の例外オブジェクトのスタックトレースは出力したくないこともあります。
次のコードでは、例外オブジェクトを指定したロギング要求を3つ生成しています。二つ目の例外オブジェクトは他のものと違ってメッセージに "do not display this" という文字列が含まれていますし、例外クラスが chapters.layouts.TestException
になっています。このメッセージを指示とみなして、二つ目の例外オブジェクトのスタックトレースが出力されないようにしてみましょう。
例:ExceptionEventEvaluatorsの使用例(logback-examples/src/main/java/chapters/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
の例外オブジェクトが設定されたロギングイベントにマッチします。この例外型はスタックトレースの出力を抑止したい型そのものです。
例:ExceptionEventEvaluatorsの使用例(logback-examples/src/main/java/chapters/exceptionEvaluatorConfig.xml)
<configuration> <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%ex{full, DISPLAY_EX_EVAL}</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration>
この設定は、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:43) [logback-examples-0.9.19.jar:na] logging statement 1 logging statement 2 java.lang.Exception: display at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]
二つ目のロギング要求による出力にはスタックトレースが含まれていないことがわかりますか。うまくTestException
のスタックトレースを出力しないようにできました。スタックトレースの各行の最後にある角括弧で囲まれた部分はパッケージング情報です。
%ex変換指定があるので、評価式の結果がfalseになったときだけスタックトレースが出力されるようになっています。
変換指定子を自作する
ここまでに、PatternLayout
に組み込みの変換指定子を見てきました。ですが、変換指定子を自作することもできるのです。
カスタム変換指定子を作るには2つの手順を踏みます。
ステップ1
まず、ClassicConverter
を継承したクラスを作ります。ClassicConverter
の役割は、ILoggingEvent
から情報を抽出して出力する文字列を生成することです。たとえば、 %logger変換指定子を実装したLoggerConverter
は、ILoggingEvent
から抽出したロガーの名前を文字列として返します。ロガー名の省略はこの手順で行います。
アプリケーションが起動してからの経過時間をナノ秒単位で返すカスタム変換指定子の実装を見てみましょう。
例:自作コンバーターの例(logback-examples/src/main/java/chapters/MySampleConverter.java)
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(ILoggingEvent)
メソッドはアプリケーションが起動してからの経過時間をナノ秒単位で計算して、それを文字列化しているだけです。
ステップ2
logback に自作したConverter
のことを教えなければいけません。そのためには、設定ファイルで新しい変換指定子を宣言する必要があります。
例:自作コンバーターの設定例(logback-examples/src/main/java/chapters/mySampleConverterConfig.xml)
Groovyとして表示<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>
設定ファイルで宣言した新しい変換指定子は、PatternLayout
に指定する変換パターン文字列の中で他の変換指定と同じように使うことが出来ます。
実行してみましょう。
次のような出力になるはずです。
4868695 [main] DEBUG - Everything's going well 5758748 [main] ERROR - maybe not quite...
オプションの扱い方や、もっと複雑な振る舞いをさせるやり方を知りたければ、MDCConverter
などの既存のConverter
実装を見てみるとよいでしょう。独自の色付けをしたければHighlightingCompositeConverter
を参考にするとよいでしょう。
HTMLLayout
HTMLLayout
はHTML形式のログを出力します(logback-classicに含まれています)。HTMLLayout
はHTMLのテーブルを出力します。それぞれの行がロギングイベントに対応しています。
デフォルトのCSSを使ったHTMLLayout
の出力例を見てください。
テーブルの列は変換パターン文字列で指定します。PatternLayout
のドキュメントに書かれた変換パターン文字列の説明を見てください。テーブルの形も内容も全て制御することができます。PatternLayout
で使えるあらゆるコンバーターを使うことができます。
自由に使えるとはいえ一つだけ例外的な制限があります。PatternLayout
とHTMLLayout
を使うときは、変換パターン文字列中で変換指定を半角スペースで区切ってはいけません。もっというと文字列リテラルを使わないようにしてください。変換パターン文字列中の変換指定はそれぞれがテーブルの列になるからです。同様に、変換指定を区切るつもりの文字列リテラルはそれぞれが列になってしまいます。下手をすると、横に超長いテーブルが出力されてしまうかもしれません。
HTMLLayout
の使い方を説明する設定ファイルを見てください。
例:HTMLLayoutの設定例(logback-examples/src/main/java/chapters/htmlLayoutConfig1.xml)
Groovyとして表示<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をブラウザで見ると次のようになっているでしょう。
スタックトレース
スタックトレースを出力するには%ex変換指定を指定します。そうすると専用の列が追加されます。ほとんどの場合スタックトレースの列は空なので、無駄に画面を専有することでしょう。さらに、スタックトレースを専用の列に出力したところで読みやすくはならないです。なお、スタックトレースを出力するには、%ex変換指定を指定する以外の方法があります。
よりよい解決策は、自分でIThrowableRenderer
インターフェイスを実装することです。実装したクラスは、例外オブジェクトに関係する情報を出力するため、HTMLLayout
に割り当てることができます。デフォルトでは、HTMLLayout
インスタンスにDefaultThrowableRenderer
が割り当てられています。これは例外オブジェクトのスタックトレースを新しい行に出力します。前の図を見るとわかりますが多少読みやすいです。
それでも理由があって%exを使いたいときは、設定ファイルにNOPThrowableRenderer
を指定すればスタックトレースを別々の行に出力しないようにできます。あなたの望み通りに全てが上手くいくようなアイデアはありません。それでも、こうしたいという気持ちがあればきっと実現できます。
CSS
HTMLLayout
で生成したHTMLの見た目はCSSで調整することができます。特別な指定がなければ、HTMLLayout
は組み込みのCSSを使います。しかし、外部のCSSを使うようにも指定できます。そのためには、layout要素
にcssBuilder要素
をネストさせればよいです。
<layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%relative...%msg</pattern> <cssBuilder class="ch.qos.logback.classic.html.UrlCssBuilder"> <!-- url where the css file is located --> <url>http://...</url> </cssBuilder> </layout>
HTMLLayout
はSMTPAppender
と組み合わせて使われることがあります。そうするとメールの内容がHTMLで綺麗に飾られたものになります。
Log4jのXMLLayout
XMLLayout (logback-classicに含まれています)を使うとlog4j.dtdに準拠した書式で出力できるようになります。つまり、ChainsawやVigilogなどの、log4jのXML書式を入力とするツールと相互運用できるのです。
オリジナルのXMLLayoutはlog4j1.2.15で導入されたものです。logback-classic の XMLLayoutにはlocationInfoとpropertiesという二つのプロパティが指定できます。locationInfoプロパティにtrueを指定すると、ロギングイベントの位置情報(送信者情報)を含められるようになります。propertyプロパティにtrueを指定すると、MDCの情報を含められるようになります。どちらのプロパティもデフォルトはfalseです。
設定例を見てみましょう。
例:Log4jXMLLayoutの例(logback-examples/src/main/java/chapters/log4jXMLLayout.xml)
Groovyとして表示<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 モジュールのほとんどのレイアウトは、logback-classic モジュールのレイアウトをそのまま移植しただけです。logback-classic モジュールと logback-access モジュールは用途が違うのですが、だいたい同じ機能を提供しています。
レイアウトを自作する
logback-access 用のレイアウト
を自作するのは、logback-classic のレイアウト
を自作するのとほとんど変わりありません。
PatternLayout
logback-access のPatternLayout
は、logback-classic とほとんど同じように設定することができます。ですが、HTTPリクエストやHTTPレスポンスにしか無い、ロギングに適した情報のための変換指定が追加されています。
logback-accessのPatternLayout
で使える変換指定を表にまとめました。
変換指定 | 効果 |
---|---|
a remoteIP |
リモートIPアドレス。 |
A localIP |
ローカルIPアドレス。 |
b B bytesSent |
レスポンスのコンテンツ長。 |
h clientHost |
リモートホスト名。 |
H protocol |
リクエストプロトコル。 |
l |
リモートログ名。logback-accessではこのコンバーターの値は常に "-" です。 |
reqParameter{paramName} |
リクエストパラメータ。 この変換指定子は、オプションで指定されたリクエストパラメータを探します。 %reqParameter{input_data}とすると、リクエストパラメータのinput_dataの値を出力します。 |
i{header} header{header} |
リクエストヘッダ。 この変換指定子はオプションで指定されたリクエストヘッダを探します。 %header{Referer}とすると、リクエストヘッダのRefererの値を出力します。 オプションを指定しなかったら、利用可能なヘッダを全て出力します。 |
m requestMethod |
リクエストメソッド。 |
r requestURL |
要求されたURL。 |
s statusCode |
レスポンスのステータスコード。 |
D elapsedTime |
リクエストを処理するのにかかった時間。ミリ秒単位です。 |
t date |
ロギングイベントの日時を出力します。オプションとして、 具体的には、%t {HH:mm:ss,SSS} や、%t{dd MMM yyyy;HH:mm:ss,SSS}などです。日時パターン文字列が指定されなかったら、共通ログ書式の日時パターン文字列であるT6{dd/MMM/yyyy:HH:mm:ss Z}が指定されたものとして扱います。 |
u user |
リモートユーザー。 |
U requestURI |
要求されたURI。 |
v server |
サーバー名。 |
localPort |
ローカルポート番号。 |
reqAttribute{attributeName} |
リクエストの属性。 この変換指定子はオプションで指定された属性を探します。 %reqAttribute{SOME_ATTRIBUTE}とすると、リクエスト属性SOME_ATTRIBUTEの値を出力します。 |
reqCookie{cookie} |
リクエストクッキー。 この変換指定子はオプションで指定されたリクエストクッキーを探します。 %cookie{COOKIE_NAME}とすると、クッキーCOOKIE_NAMEの値を出力します。 |
responseHeader{header} |
レスポンスヘッダー。 この変換指定子はオプションで指定されたレスポンスヘッダを探します。 %responseHeader{Server}とすると、レスポンスヘッダServerの値を出力します。 |
requestContent |
この変換指定はリクエストの中身、つまり、リクエストの |
fullRequest |
リクエストに関連するヘッダ、コンテンツを全て出力します。 |
responseContent |
レスポンスの中身、つまり、レsポンスの |
fullResponse |
レスポンスに関連するヘッダ、コンテンツを全て出力します。 |
logback-accessのPatternLayout
ではさらに3つのキーワードが利用できます。これはいわゆるショートカットです。
キーワード | 等価な変換パターン文字列 |
---|---|
common 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
- 日付
- リクエストURL
- レスポンスのステータスコード
- コンテンツ長
logack-accessのHTMLLayout
による出力例を見てください。
実際に使うと上でもっと良くするにはどうしたらいいでしょうか?log4j.properties for logback translatorは、logback-access のRollingFileAppender
とHTMLLayout
を使ってリアルタイム出力をするようになっています。
Webアプリケーションのtranslatorに対するユーザーからのリクエストに対して、都度新しいアクセスログが出力されます。このリンク先でそれを見ることが出来ます。