第4章 アペンダー
西部について教えたいことが多すぎるのでどこから始めたらいいのかわからないよ。1つを選べば残りの100を捨てることになってしまう。最初の1つを決めるにはどうしたいいんだろう?
—JOHN STEINBECK, East of Eden
アペンダーについて
logback はロギングイベントを出力する仕事を、アペンダーと呼ばれるコンポーネントに任せています。ch.qos.logback.core.Appender
インターフェイスを実装したものがアペンダーとして利用できます。このインターフェイスに宣言された重要なメソッドは次のとおりです。
package ch.qos.logback.core; import ch.qos.logback.core.spi.ContextAware; import ch.qos.logback.core.spi.FilterAttachable; import ch.qos.logback.core.spi.LifeCycle; public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable { public String getName(); public void setName(String name); void doAppend(E event); }
Appender
インターフェイスのほとんどのメソッドはゲッター、あるいはセッターです。doAppend()
メソッドだけは特別で、唯一の引数として型Eのオブジェクトを取ります。Eの実際の型は、logbackモジュールによって異なります。logback-classic モジュールの場合、EはILoggingEventになるでしょうし、logback-access モジュールではAccessEventになるでしょう。doAppend()
メソッドは、おそらくlogbackフレームワークの中で最も重要なメソッドです。ロギングイベントを、適切な書式で、適切な出力デバイスに出力する役目があります。
アペンダーには名前を付けられます。設定ファイル中で参照しやすいようにするためです。Appender
インターフェイスはFilterAttachable
インターフェイスを継承しています。つまり、アペンダーのインスタンスには一つ以上のフィルターを割り当てられるのです。フィルターについては、後の章で詳しく説明します。
アペンダーには、ロギングイベントを出力することについて最終的な責任があります。しかし、アペンダー自体が書式化をするのではなく、Layout
あるいはEncoder
オブジェクトに処理を委譲します。レイアウトやエンコーダーは、ただ一つのアペンダーにだけ関連付けられ、そのアペンダーだけが参照することになります。アペンダーによっては、組み込みの、あるいは固定の書式が指定されています。そういうアペンダーには、レイアウトもエンコーダーも不要です。たとえば、 SocketAppender
は、ロギングイベントを接続したリモートホストに転送する前に単純にシリアライズするだけです。
AppenderBase
ch.qos.logback.core.AppenderBase
は、Appender
インターフェイスを実装した抽象クラスです。このクラスは、どんなアペンダーにも必要な共通の機能として、名前や活性化状態やレイアウト、フィルターに対するゲッターおよびセッターメソッドが実装されています。logback の配布物に含まれるアペンダーはすべてこのクラスの派生クラスです。AppenderBase
は抽象クラスですが、Append
インターフェイスのdoAppend()
メソッドを実装しています。AppenderBase
クラスの話は、実際にソースコードの抜粋を見ながら進めるのが一番わかりやすいと思います。
public synchronized void doAppend(E eventObject) { // prevent re-entry. if (guard) { return; } try { guard = true; if (!this.started) { if (statusRepeatCount++ < ALLOWED_REPEATS) { addStatus(new WarnStatus( "Attempted to append to non started appender [" + name + "].",this)); } return; } if (getFilterChainDecision(eventObject) == FilterReply.DENY) { return; } // ok, we now invoke the derived class's implementation of append this.append(eventObject); } finally { guard = false; } }
doAppend()
メソッドはsynchronizedメソッドになっています。つまり、別のスレッドから同じアペンダーに安全にロギングできるのです。スレッドTがdoAppend()
メソッドを実行している間は、Tだけが排他的にアペンダーにアクセスできるので、他のスレッドからの呼び出しはキューイングされます。
synchronized が不適切な場合もあります。そういうときのために、logback の配布物にはAppenderBase
とよく似たch.qos.logback.core.UnsynchronizedAppenderBase
も含まれています。ややこしくなるので、UnsynchronizedAppenderBase
については本章の後半で説明します。
doAppend()
メソッドでは、まず最初にguard変数にtrueが設定されているかどうかをチェックします。trueだったら、ただちに終了します。そうでなければ、まずguard変数にtrueを設定してから次の処理に進みます。guard変数によって、doAppend()
メソッドを再帰的に呼び出してしまうことを防いでいるのです。あるコンポーネントが、ログを取得するためにappend()
というメソッドを呼び出したとしましょう。そうすると、今呼び出したアペンダーと同じアペンダーを直接呼び出すことになってしまうので、無限ループした結果スタックオーバーフローが発生してしまいます。
次の式ではstarted
フィールドにtrueが設定されているかどうかをチェックします。trueでなければ、警告メッセージを出力してdoAppend()
メソッドは終了します。これは、クローズされたアペンダーには何も書き込めなくなるということです。Appender
オブジェクトはLifeCycle
インターフェイスを実装しているので、つまりstart()
メソッド、stop()
メソッド、isStarted()
メソッドを実装しています。Joran設定フレームワークは、アペンダーの全てのプロパティを設定してから、アペンダーを開始して活性化状態をアクティブにするためstart()
メソッドを呼び出します。アペンダーにもよりますが、指定されていないプロパティがあったり、プロパティに指定した値の影響で、開始できないことがあります。たとえば、FIleAppender
の場合ファイルを作成するかどうかは指定された切り捨てモードに依存しています。ですので、File
オプションが指定されていたとしても、Appendオプションに正しい値が指定されていなければ、正常に動作しません。開始するまでの処理順序が明確なので、アペンダーは自身のプロパティがちゃんと設定されていることを前提として動作することができます。
アペンダーが開始できなかったとき、あるいは、停止してしまったときは、logback の内部状態管理システムによって警告メッセージが出力されます。何度か(ロギングを?)試みたあと、同じ内容の警告メッセージで溢れかえってしまうのを避けるため、doAppend()
メソッドは警告メッセージを出力しないようになります。
その次のif
文では割り当てられたフィルターの応答をチェックします。フィルターチェインの応答によって、ロギングイベントを拒否するか受け入れるかが決まります。フィルターチェインが何も決めなかったら、デフォルトでロギングイベントは受け入れられるようになっています。
その後は、派生クラスで実装されてたappend()
メソッドを呼び出します。このメソッドは、実際に適切なデバイスへロギングイベントを出力します。
最後にguard変数が開放され、キューイングされたdoAppend()
メソッドの呼び出しが処理できるようになります。
ここの説明では "プロパティ" という言葉の代わりに "オプション" という言葉を使いました。これは、JavaBeanの規約に従ったゲッターおよびセッターメソッドが用意された属性のことです。
logback-coreモジュール
logback-coreモジュールは、他のモジュールを構築する基盤となるモジュールです。一般的に、logbac-core モジュールのコンポーネントは、ある程度の(少なくとも最小限の)カスタマイズが必要です。以降の節では、カスタマイズしないでもすぐに利用できるアペンダーを紹介していきます。
OutputStreamAppender
OutputStreamAppender
はjava.io.OutputStream
にロギングイベントを出力します。このクラスは他のアペンダーを構築するための基本的なサービスを提供するものです。普通なら、利用者がOutputStreamAppender
を直接インスタンス化することはありません。java.io.OutputStream
を文字列で表現することはできないので、設定ファイルからOutputStream
オブジェクトを指定することはできないのです。簡単に言うと、設定ファイルではOutputStreamAppender
を設定することはできません。しかし、OutputStreamAppender
に設定できるプロパティが無いという意味ではありません。次のようなプロパティがあります。
プロパティ名 | 型 | 説明 |
---|---|---|
encoder | Encoder |
OutputStreamAppender への出力を書式化する。エンコーダについては別の章で説明しています。
|
OutputStreamAppender
はConsoleAppender
、 FileAppender
の基底クラスです。RollingFileAppender
の基底クラスはFileAppenderなので、これも含めておきます。次の図はOutputStreamAppender
とそのサブクラスの関係を図示したものです。
ConsoleAppender
ConsoleAppender
は名前のとおり、ロギングイベントをコンソールに出力します。正確に言うと、System.outあるいはSystem.errに出力します。デフォルトではSystem.outが使われます。ConsoleAppender
は、利用者が指定したエンコーダーを使ってロギングイベントを書式化します。エンコーダーについては別の章で説明します。System.outとSystem.errの型はjava.io.PrintStream
です。つまり、いずれもバッファIO操作のためのOutputStreamWriter
にラップされているということです。
プロパティ名 | 型 | 説明 |
---|---|---|
encoder |
Encoder
|
OutputStreamAppender と同じです。 |
target | String |
文字列で、System.outかSystem.errを指定します。デフォルトはSystem.outにです。 |
withJansi | boolean |
withJansiプロパティの値にはデフォルトでfalse が設定されています。withJansiプロパティにtrue を指定すると、Jansiライブラリが有効化され、Windowsマシン上でANSIカラーコードがサポートされるようになります。Windows のホスト上でこのプロパティにtrueを指定する場合、クラスパス上にjansiライブラリのjarファイルを置かなければなりません。なお、UnixベースのOSであるLinuxやMac OS Xのターミナルは、デフォルトでANSIカラーコードをサポートしています。
Eclipse IDEから利用する場合は、ANSI in Eclipse Consoleプラグインをインストールしてみましょう。 |
ConsoleAppender
の設定サンプルを見てください。
例:ConsoleAppenderの設定(logback-examples/src/main/java/chapters/appenders/conf/logback-Console.xml)
Groovyで表示<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
logback-examplesディレクトリに移動して、クラスパスを設定すれば、次のコマンドで上の設定ファイルを使って実行できます。
java chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-Console.xml
FileAppender
FileAppender
はOutputStreamAppender
の派生クラスで、ファイルにロギングイベントを出力します。fileオプションで対象のファイルを指定します。既にファイルが存在するとき、ファイル内容を切り捨てるか、追加するかどうかは、appendプロパティの値に応じて決まります。
プロパティ名 | 型 | 説明 |
---|---|---|
append | boolean |
trueの場合、既存のファイルの末尾にロギングイベントを追加します。appendプロパティがfalseの場合、既存のファイルの内容は捨てられてしまいます。デフォルトはtrueです。 |
encoder |
Encoder
|
OutputStreamAppender と同じです。 |
file | String |
書き込み先のファイル名です。ファイルが存在しない場合、新しく作成します。Windowsプラットフォームの利用者は、バックスラッシュをエスケープするの忘れがちなので注意してください。たとえば、c:\temp\test.logという文字列は意図したように解釈されません。'\t'はエスケープシーケンスのタブ文字{\u0009}として解釈されるでしょう。c:/temp/test.logと書くか、c:\\temp\\test.logのように書けばよいでしょう。デフォルト値はありません。
指定したファイルの親ディレクトリが存在しない場合、 |
prudent | boolean |
prudentモードでは、指定されたファイルに安全に書き込むようになります。同じファイルを対象にした他のFileAppender がいるとしても、それが別のJVMで動いているとしても、ましてや別のホストで動いているとしても、です。デフォルト値はfalse です。
prudent モードは、多少の制限はあるものの、 prudent モードがtrueなら、appendプロパティも自動的にtrueになります。 prudentモードは排他的なファイルロックを使用します。ファイルロックを使うと、ロギングイベントの出力にかかるコストがおよそ3倍程度になることが分かっています。標準的なPCで、ローカルのハードディスク上のファイルへ一つロギングイベントを出力をするとき、prudentモードがオフの場合はおよそ10マイクロ秒かかります。prudentモードをオンにすると、30マイクロ秒になります。言い換えると、prudentモードがオフの場合は毎秒100,000回のロギングイベントが処理できるのに対して、prudentモードがオンの場合は毎秒33,000回しか処理できなくなるということです。 prudentモードでは、複数のJVMから同じファイルに行うIO操作を効果的にシリアライズします。したがって、JVMの数が多くなればなるほど、IO操作ごとの待ち時間が長くなります。IO操作の合計時間が、毎秒20回オーダーのロギング要求が処理できる程度であれば、性能への影響は無視してもよいでしょう。アプリケーションが毎秒100回以上のIO操作をするようであれば、きっと性能影響があるのでprudentモードを使うのはやめてください。 ネットワークファイルロックネットワークファイルシステム上のログファイルを対象にすると非常にコストが高くなります。ネットワークファイルシステム越しのファイルロックは、プロセスがすでに所有しているロックをリリースする前に再取得する、という振る舞いに強く依存していることも同じくらい重要です。したがって1つのプロセスがログファイルを占有していると、他のプロセスからはデッドロックしているように見えるので、ロックを待ち続けることになってしまいます。 prudentモードは、ネットワークの速度と同じくらいの影響をOSの実装からも受けます。私たちの提供しているとても小さいアプリケーションのFileLockSimulatorを使えば、あなたの環境でprudentモードがどのように振る舞うかシミュレートできます。 |
即時フラッシュ
デフォルトでは、それぞれのロギングイベントは、最終的な出力ストリームに即時にフラッシュされます。これは、あなたのアプリケーションがアペンダーをちゃんとクローズしていない場合、ロギングイベントが失われないようにするためのより安全な方法です。しかし、ロギング要求のスループットが大幅に増加してしまう場合には、Encorder
のimmediateFlushプロパティにfalse
を指定することもできます。エンコーダー、特にLayoutWrappingEncoder
については別の章で説明します。
FileAppender
の設定例を見てください。
例:FileAppenderの設定(logback-examples/src/main/java/chapters/appenders/conf/logback-fileAppender.xml)
Groovyとして表示<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>testFile.log</file> <append>true</append> <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
logback-examplesディレクトリに移動すれば、次のコマンドで上の設定ファイルを使って実行できます。
java chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-fileAppender.xml
ファイル名を一意にする(タイムスタンプを使う)
アプリケーションの開発フェーズの間や、アプリケーションを実行している時間が短い(たとえば、バッチアプリケーション)場合は、アプリケーションを実行するたびに新しいログファイルを作るほうがよいでしょう。timestamp要素
を使えば簡単に実現できます。以下に例を示します。
例:タイムスタンプを使ってファイル名を一意にするFileAppenderの設定(logback-examples/src/main/java/chapters/appenders/conf/logback-timestamp.xml)
Groovyとして表示<configuration> <!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under the key "bySecond" into the logger context. This value will be available to all subsequent configuration elements. --> <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <!-- use the previously created timestamp to create a uniquely named log file --> <file>log-${bySecond}.txt</file> <encoder> <pattern>%logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
timestamp要素には、key属性およびdatePattern属性という2つの必須属性と、timeReference属性という任意属性があります。key属性には、変数と同じく、他の設定要素からタイムスタンプを参照するときの名前を指定します。datePattern属性には、設定ファイルを解釈した時点の日時を文字列に変換するための日付パターン文字列を指定します。日付パターン文字列に指定できるのは、SimpleDateFormatで利用できるものと同じです。timeReference属性には、タイムスタンプの基準時間を指定します。デフォルトでは、現在の日時、つまり、設定ファイルを解析、解釈した時点の日時になります。ですが、コンテキストを生成した時間を基準時間としたほうが便利な場合もあるでしょう。そういう場合は、timeReference属性に"contextBirth"
を指定すればよいです。
次のコマンドを実行して、timestamp要素
がどうなるのか試してみましょう。
java chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-timestamp.xml
ロガーコンテキストを生成した日時を基準時間として使う場合、timeReference属性に"contextBirth"を指定します。
例:タイムスタンプの基準時間にロガーコンテキストを生成した日時を使用する(logback-examples/src/main/java/chapters/appenders/conf/blogback-timestamp-contextBirth.xml)
Groovyとして表示<configuration> <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" timeReference="contextBirth"/> ... </configuration>
RollingFileAppender
RollingFileAppender
はFileAppender
を拡張して、ログファイルを切り替えられるようにしたものです。たとえば、RollingFileAppender
では、log.txtという名前のファイルにログを出力するようにした上で、一定の条件が満たされたら、出力先を別のファイルに変えることができます。
RollingFileAppender
と連携する2つの重要なサブコンポーネントがあります。一つはRollingPolicy
です。これはファイルを切り替えるために必要な処理を行います。詳しくは後述します。もう一つはTriggeringPolicy
です。これはいつ切り替えるのか決定するものです。こちらも詳しくは後述します。つまり、RollingPolicy
がwhatを、TriggeringPolicy
がwhenを表しているのです。
どんな使い方をするにしても、RollingFileAppender
にはRollingPolicy
とTriggeringPolicy
の両方を設定しなければなりません。RollingPolicy
はTriggeringPolicy
インターフェイスを実装しているので、少なくともRollingPolicyだけは明示的に設定しなければなりません。
RollingFileAppender
で利用出来るプロパティは次のとおりです。
プロパティ名 | 型 | 説明 |
---|---|---|
file | String |
FileAppender と同じです。 |
append | boolean |
FileAppender と同じです。 |
encoder |
Encoder
|
OutputStreamAppender と同じです。 |
rollingPolicy | RollingPolicy |
このオプションに指定するのは、RollingFileAppender がファイルを切り替える際に処理を委譲するコンポーネントです。詳細は後述します。
|
triggeringPolicy | TriggeringPolicy |
このオプションに指定するのは、RollingFileAppender にファイルを切り替えるタイミングを通知するコンポーネントです。詳細は後述します。
|
prudent | boolean |
prudentモードでは、FixedWindowRollingPolicy はサポートされていません。
FileAppender の説明を参照してください。
|
ローリングポリシーの概要
RollingPolicy
は、ファイルの切り替えに伴う移動や名前の変更を行います。
RollingPolicy
インターフェイスは次のようなものです。
package ch.qos.logback.core.rolling; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.spi.LifeCycle; public interface RollingPolicy extends LifeCycle { public void rollover() throws RolloverFailure; public String getActiveFileName(); public CompressionMode getCompressionMode(); public void setParent(FileAppender appender); }
rollover
メソッドによって、現在のログファイルのアーカイブ処理を行います。getActiveFileName()
メソッドは、今まさにログを出力しているはずのログファイル名を算出するために呼び出されます。getCompressionMode
メソッドの名前が示すとおり、RollingPolicyにはファイル圧縮モードを決める役割もあります。RollingPolicy
から親となるアペンダーへの参照は、setParent()
メソッドによって設定します。
TimeBasedRollingPolicy
TimeBasedRollingPolicy
はおそらく一番人気のあるポリシーです。これは、ファイル切り替えポリシーを日付や月などの日時に基いて定義するものです。
TimeBasedRollingPolicy
にはファイルを切り替えるタイミングを通知する役割もあります。実際に、 TimeBasedTriggeringPolicy
はRollingPolicy
とTriggeringPolicy
の両方のインターフェイスを実装しています。
TimeBasedRollingPolicy
には、必須プロパティのfileNamePatternと、いくつかの任意のプロパティが設定できます。
プロパティ名 | 型 | 説明 |
---|---|---|
fileNamePattern | String |
fileNamePatternプロパティは必須プロパティで、切り替えるときのログファイル名を指定します。指定する値には、ファイル名と%d変換指示子が含まれます。%d変換指示子には、java.text.SimpleDateFormat クラスで定義された日付時刻のパターン文字列を指定します。日付時刻パターンが省略された場合、パターン文字列としてyyyy-MM-ddが使われます。ファイルの切り替え周期は、fileNamePatternに指定された値から推測されます。
%d{}変換指示子の内側に書かれる日付と時刻のパターン文字列は、java.text.SimpleDateFormatの規約にしたがったものです。fileNamePatternプロパティの中であれば、日付と時刻のパターン文字列の中でも外でも、スラッシュ'/'およびバックスラッシュ'\'はディレクトリの区切り文字として扱われます。 %dトークンはいくつでも指定することができますが、ファイル切り替え周期を推測するために使われるのは最初のものだけです。他のすべてのトークンには、'aux'パラメータを指定して補助であるという印を付けなければなりません。 |
maxHistory | int | maxHistoryプロパティは、任意プロパティです。削除せずに保持しておくアーカイブファイルの最大数を指定します。たとえば毎月切り替えるつもりでmaxHistoryに6を指定した場合、過去6ヶ月以内のアーカイブファイルは保持され、過去6ヶ月より古いファイルは削除されるでしょう。古くなったファイルだけが削除されるので、logbackが作成したディレクトリは削除されないので注意してください。 |
cleanHistoryOnStart | boolean |
trueを指定した場合、アペンダーの開始時に古いアーカイブを削除します。デフォルトではfalseが設定されています。 通常は、ファイルを切り替えるタイミングで古いアーカイブも削除されます。しかし、アプリケーションによっては切り替えのタイミングが来るまで実行し続けないことがあります。そういう短命なアプリケーションでは、古いアーカイブを削除するタイミングが来ない可能性があるのです。cleanHistoryOnStartプロパティにtrueを指定しておけば、アペンダーの開始時に古いアーカイブを削除することができます。 |
fileNamePattern
の値とその効果について具体例を示します。
fileNamePattern | 切り替えタイミング | 具体例 |
---|---|---|
/wombat/foo.%d | 毎日深夜に切り替え。%d変換指示子に日付時刻パターンが省略されているので、デフォルトのyyyy-MM-ddが指定されたことになります。このパターン文字列の場合は、毎日切り替えることになります。 |
fileプロパティを指定しない場合:2006年11月23日中は/wombat/foo.2006-11-23に出力されます。23日の24時(24日の0時)以降、24日中は/wombat/foo.2006-11-24に出力されます。 fileプロパティに/wombat/foo.txtを指定した場合:2006年11月23日中は/wombat/foo.txtに出力されます。23日24時(24日0時)にfoo.txtは/wombat/foo.2006-11-23に変更されます。24日中は、新しく作成された/wombat/foo.txtに出力されます。 |
/wombat/%d{yyyy/MM}/foo.txt | 月初めに切り替え。 |
fileプロパティを指定しない場合:2006年10月中は、/wombat/2006/10/foo.txtに出力されます。10月31日の24時(つまり11月1日の0時)以降、11月中は/wombat/2006/11/foo.txtに出力されます。 fileプロパティに/wombat/foo.txtを指定した場合:出力先は常に/wombat/foo.txtです。2006年10月中は、/wombat/foo.txtに出力されます。10月31日の24時(つまり11月1日の0時)に/wombat/foo.txtは/wombat/2006/10/foo.txtに変更されます。11月中は、新しく作成された/wombat/foo.txtに出力されます。11月30日の24時(つまり12月1日の0時)に/wombat/foo.txtは/wombat/2006/11/foo.txtに変更されます。 |
/wombat/foo.%d{yyyy-ww}.log | 週初めに切り替え。週の最初の日は、ロケールに依存するので注意してください。 | 前の例と同じですが、切り替えが発生するのは週の初日です。 |
/wombat/foo%d{yyyy-MM-dd_HH}.log | 毎時0分に切り替え。 | 前の例と同じですが、切り替えが発生するのは毎時0分です。 |
/wombat/foo%d{yyyy-MM-dd_HH-mm}.log | 毎分0秒に切り替え。 | 前の例と同じですが、切り替えが発生するのは毎分0秒です。 |
/foo/%d{yyyy-MM,aux}/%d.log | 毎日深夜に切り替え。年と月からなる名前のフォルダにアーカイブを作成する。 | この例では、最初の%dトークンは補助(auxiliary)であるという印が付いています。したがって、日付時刻パターンの省略された二つ目の%dトークンが最初のものとして使われます。したがって、切り替えタイミングは日次(%dのデフォルトパターン)になり、アーカイブするフォルダ名はそのときの年と月になります。例えば、2006年11月中にアーカイブされたファイルはすべて/foo/2006-11というフォルダに置かれます。例えば/foo/2006-11/2006-11-14.logのようになります。 |
スラッシュまたはバックスラッシュ文字はフォルダ(ディレクトリ)の区切り文字として扱われます。存在しないフォルダは必要に応じて作成されるので、別々のフォルダにログファイルを作るのは簡単です。
TimeBasedRollingPolicy
は、FixedWindowRollingPolicy
のように自動ファイル圧縮をサポートしています。fileNamePatternオプションの値が.gzまたは.zipで終わっている場合、このフィーチャが有効になります。
fileNamePattern | 切り替えタイミング | 具体例 |
---|---|---|
/wombat/foo.%d.gz | 毎日深夜に切り替え。アーカイブファイルは自動的にgz圧縮する。 |
fileプロパティを指定しない場合:2009年11月23日中は/wombat/foo.2009-11-23に出力します。23日24時(24日0時)になったら、それまでログを出力していたファイルはgz圧縮されて/wombat/foo.2009-11-23.gzになります。11月24日中は/wombat/folder/foo.2009-11-24に出力されます。 fileプロパティに/wombat/foo.txtを指定した場合:2009年11月23中は/wombat/foo.txtに出力されます。23日24時(24日0時)にgz圧縮されて/wombat/foo.2009-11-23.gzになります。11月24日中は新しく作られた/wombat/foo.txtに出力されます。24日24時(25日0時)に/wombat/foo.txtはgz圧縮されて/wombat/foo.2009-11-24.gzになります。 |
fileNamePatternには2つの目的があります。1つは、パターン文字列を処理して、logbackに切り替えタイミングを指示することです。もう1つはアーカイブファイル名を決めることです。パターン文字列の形が違っても切り替えタイミングが同じになることがあるので気をつけてください。パターン文字列がyyyy@MMでもyyyy-MMでも、切り替えタイミングは毎月ですが、アーカイブファイル名は異なります。
fileプロパティを設定すると、アクティブなログファイルの場所とアーカイブファイルの場所を別々にすることができます。ログはfileプロパティで指定したファイルに出力されます。つまり、アクティブなログファイルの名前は常に同じになるのです。ただし、fileプロパティを省略した場合、アクティブなログファイルの名前はfileNamePatternに基づいたものになります。fileオプションを設定しなければ、ログファイルの名前変更エラーを割けられます。これは外部からログファイルを参照している間に切り替えようとすると発生するエラーです。存在するときに発生する。
maxHistoryプロパティは、削除せずに保持しておくアーカイブファイルの最大数を指定します。たとえば、切り替えタイミングが毎月で、maxHistoryに6を指定していたら、6ヶ月以内のアーカイブは保持されますが、6ヶ月を超える古いアーカイブは削除されます。古いアーカイブは削除されてしまうので注意しましょう。logbackによって作られたフォルダも必要に応じて削除されてしまいます。
技術的な都合により、ファイルの切り替えは時間ベースではなくロギングイベントの到着ベースで行われます。例えば、2002年3月8日時点でfileNamePatternにYYYY-MM-DD(毎日切り替え)が指定されていることにしましょう。すると、8日24時(9日0時)を過ぎてから最初のロギングイベントが到着した際にファイルの切り替えが行われます。しばらくロギングイベントが発生しなかった場合、たとえば0時を過ぎてから23分47秒後にロギングイベントが発生した場合、3月9日0時0分ではなく、0時23分47秒にファイルの切り替えが行われることになります。したがって、ファイル切り替えにはロギングイベントの頻度に応じた遅延が伴います。どれくらいの遅延があろうとも、切り替えアルゴリズムは常に正しく動作します。したがってあらゆるロギングイベントはそれが発生した時刻に基いて適切なファイルに出力されます。
RollingFileAppender
とTimeBasedRollingPolicy
の設定例を見てください。
例:RollingFileAppender
とTimeBasedRollingPolicy
の設定(logback-examples/src/main/java/chapters/appenders/conf/logback-RollingTimeBased.xml)
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logFile.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- daily rollover --> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- keep 30 days' worth of history --> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
次の例はprudentモードの例です。
例:prudentモードにしたRollingFileAppender
とTimeBasedRollingPolicy
の設定(logback-examples/src/main/java/chapters/appenders/conf/logback-PrudentTimeBasedRolling.xml)
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- Support multiple-JVM writing to the same log file --> <prudent>true</prudent> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
FixedWindowRollingPolicy
FixedWindowRollingPolicy
を使うと、固定幅アルゴリズムに従ってファイルの名前を変更することができます。
fileNamePatternオプションには、アーカイブファイル名のパターンを指定します。このオプションは必須で、パターン文字列中のどこかに整数トークンの%iを含めなければなりません。
FixedWindowRollingPolicy
のプロパティは次のとおりです。
プロパティ名 | 型 | 説明 |
---|---|---|
minIndex | int |
このオプションには固定幅の添字の下限値を指定します。 |
maxIndex | int |
このオプションには固定幅の添字の上限値を指定します。 |
fileNamePattern | String |
このオプションには 例えば、パターン文字列がMyLogFile%i.log、minIndexが1、maxIndexが3だとしたら、アーカイブファイル名はMyLogFile1.log、MyLogFile2.log、MyLogFile3.logのいずれかになります。 自動ファイル圧縮を有効にする場合もこのプロパティで指定することになります。例えば、fileNamePatternにMyLogFile%i.log.zipが指定されている場合、アーカイブされたファイルはzip形式で圧縮されることになります。gz形式も有効です。 |
固定幅切り替えポリシーでは、指定した窓の大きさによってはたくさんのファイル名を変更しなければなりません。ですので、あまりに大きな値を指定することは極力避けるようにしてください。利用者が指定した窓が大きすぎる場合、現在の実装は窓の大きさを自動的に20にしてします。
固定幅切り替えポリシーの具体的な例を見ていきましょう。それぞれ、minIndexが1、maxIndexが3、fileNamePatternがfoo%i.log、fileプロパティがfoo.logとなっていることを想定しています。;
切り替え回数 | 現在の出力対象 | アーカイブファイル | 説明 |
---|---|---|---|
0 | foo.log | - | まだ切り替えは発生していません。logbackは最初に指定されたファイルにログを出力しています。 |
1 | foo.log | foo1.log | 切り替えが発生しました。foo.logがfoo1.logという名前に変更されました。新しくfoo.logが作成され、現在の出力対象になりました。 |
2 | foo.log | foo1.log、foo2.log | 切り替えが発生しました。foo1.logがfoo2.logという名前に変更され、foo.logがfoo1.logという名前に変更されました。新しくfoo.logが作成され、現在の出力対象になりました。 |
3 | foo.log | foo1.log、foo2.log、foo3.log | 切り替えが発生しました。foo2.logがfoo3.logという名前に変更され、foo1.logがfoo2.logという名前に変更され、foo.logがfoo1.logという名前に変更されました。新しくfoo.logが作成され、現在の出力対象になりました。 |
4 | foo.log | foo1.log、foo2.log、foo3.log | 以降の切り替えでは、最初にアーカイブfoo3.logを削除します。その他のファイルは、前のステップと同じように、添字を増やした名前に変更されます。以降は、常に1つのログファイル、3つのアーカイブファイルが存在することになります。 |
RollingFileAppender
とFixedWindowRollingPolicy
の設定例を次に示します。fileNamePatternオプションに同じ情報が含まれているとはいえ、 Fileオプションは必須なので注意してください。
例:RollingFileAppender
とFixedWindowRollingPolicy
の設定(logback-examples/src/main/java/chapters/appenders/conf/logback-RollingFixedWindow.xml)
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>test.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>tests.%i.log.zip</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
日時とサイズに基づいたログファイルのアーカイブ
基本的には日付でファイルをアーカイブしたいけど、後続処理ツールに指定できるログファイルにサイズ制限があるので、ログファイルのサイズも制限したいことがあるでしょう。そんなときは、logbackの配布物に含まれるTimeBasedRollingPolicy
のサブコンポーネントであるSizeAndTimeBasedFNATP
を使うとよいでしょう。FNATP は File Naming And Triggering Policy の略です。
日時とサイズに基づいてログファイルをアーカイブする設定ファイルの例を示します。
例:SizeAndTimeBasedFNATP
の設定(logback-examples/src/main/java/chapters/appenders/conf/logback-sizeAndTime.xml)
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 100MB -->
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
"%d"トークンに加えて、"%i"トークンがあることに気づきましたか。次の切り替えタイミングがくるまでの間に、現在のログファイルがmaxFileSizeに指定したサイズを越えたら、添字を加算してアーカイブします。添字は0から始まります。
日時とサイズに基づいたログファイルのアーカイブをしていても、古いアーカイブを削除することができます。残しておくアーカイブの数をmaxHistoryプロパティで指定しなければなりません。アプリケーションが停止してその後再起動した場合でも、正しい添字のファイルにログを出力します。
トリガーポリシーについて
RollingFileAppender
にファイルを切り替えるタイミングを通知するのがTriggeringPolicy
です。
TriggeringPolicy
インターフェイスには1つだけメソッドが宣言されています。
package ch.qos.logback.core.rolling; import java.io.File; import ch.qos.logback.core.spi.LifeCycle; public interface TriggeringPolicy<E> extends LifeCycle { public boolean isTriggeringEvent(final File activeFile, final <E> event); }
isTriggeringEvent()
メソッドには、二つの引数があります。1つは現在有効なファイル、もう1つは処理中のロギングイベントです。これらのパラメータに基づいて、ファイルの切り替えをするべきかどうかを判断します。
最もよく使われているトリガーポリシーはTimeBasedRollingPolicy
です。これはローリングポリシーの別名です。他のローリングポリシーの説明と合わせて説明したとおりです。
SizeBasedTriggeringPolicy
SizeBasedTriggeringPolicy
は現在有効なファイルのサイズを判断します。指定したサイズより大きくなった場合、RollingFileAppender
に現在のファイルを切り替えるよう通知します。
SizeBasedTriggeringPolicy
はmaxFileSizeパラメータだけを受け付けます。デフォルトは10MBです。
maxFileSizeオプションはバイト、キロバイト、メガバイト、ギガバイト単位で指定できます。それぞれKB、MB、GBという接尾辞を使うこともできます。たとえば、5000000、5000KB、5MB、2GBはすべて正しい値です。最初の三つは同じ値になります。
ファイルサイズが5MBを越えたら切り替える場合の
RollingFileAppender
とSizeBasedTriggeringPolicy
の設定例を見てみましょう。
例:RollingFileAppender
とSizeBasedTriggeringPolicy
の設定(logback-examples/src/main/java/chapters/appenders/conf/logback-RollingSizeBased.xml)
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>test.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>test.%i.log.zip</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
logback-classicモジュール
ロギングイベントはlogback-coreモジュールの中ではジェネリック型ですが、logback-classicではILoggingEvent
のインスタンスになります。logback-classicでは、ILoggingEvent
のインスタンスの具体的なパイプライン処理を行います。
SocketAppenderとSSLSocketAppender
ここまでに紹介してきたアペンダーは、ローカルリソースだけにログを出力するものでした。対照的に、 SocketAppender
はログをシリアライズしたILoggingEvent
のインスタンスとしてリモートホストに送信するものとして設計されています。SocketAppender
では、ロギングイベントを平文で送信します。SSLSocketAppender
では、暗号化された安全なチャネルを介してロギングイベントを送信します。
シリアライズされたロギングイベントの実際の型は、ILoggingEvent
インターフェイスを実装したLoggingEventVO
です。ロギングイベントがどれだけ心配なのはわかりますが、リモートロギングが盗聴されることはありません。★受信したロギングイベントをデシリアライズしたら、ローカルで生成されたものとまったく同じように扱われます。それぞれのマシンで実行されているSocketAppender
のインスタンスの出力書式がどれも同じになっていれば、中央ログサーバに直接ロギング出力を送りつけることができます。SocketAppender
に割り当てられたレイアウトをリモートサーバに送ることはありません。シリアライズされたイベントを送信するからです。SocketAppender
はTransmission Control Protocol(TCP)レイヤーで動作します。TCPレイヤーは、信頼性、順序性、フロー制御を備えた端末間のオクテットストリームによる通信を提供するものです。つまり、リモートサーバがネットワークから到達可能な場合、ロギングイベントは確実に到着します。リモートサーバーがダウンしているか到達不能である場合、ロギングイベントは単純に破棄されてしまいます。リモートサーバが復旧した場合、ロギングイベントの送信は透過的に再開されます。この透過的な再接続は、定期的にサーバへの接続を試みるコネクタースレッドによって行われます。
ロギングイベントはネイティブのTCP実装によって自動的にバッファへ保存されます。つまり、リモートサーバへの通信速度がクライアントでロギングイベントを生成する速度より遅かったとしても、クライアントアプリケーションが通信速度に影響を受けることはない、ということです。通信速度がロギングイベントを生成する速度よりも遅ければ、クライアントは通信速度に合わせた速度でしか処理を進めることができません。極端な具体例ですが、リモートサーバへのネットワーク接続がダウンしている場合、最終的にクライアントはブロックしてしまいます。また、ネットワーク接続は生きててもリモートサーバがダウンしている場合、ロギングイベントはサーバが利用できないために破棄されてしまいますが、クライアントがブロックされることはありません。
あらゆるロガーにSocketAppender
が割り当てられていなかったとしても、コネクタースレッドが生きているかぎりGCに回収されることはありません。コネクタースレッドが死ぬのは、リモートサーバへの接続がダウンしたときだけです。このGCに伴う問題を回避するには、SocketAppender
を明示的にクローズしなければなりません。長時間稼働するアプリケーションでは、非常に多くのSocketAppender
のインスタンスを生成、破棄することになるので、このGCに伴う問題には注意してください。ほとんどのアプリケーションでは無視してもたいして問題になりません。SocketAppender
をホストしているJVMが、SocketAppender
をクローズする前に終了してしまうとしたら、それが明示的に行われるとしても、あるいは、GCの後であろうとも、通信用のパイプの中に残っていた未送信のデータは失われてしまうでしょう。これはWindowsベースのシステムに共通する問題です。データの消失を避けるには、普通ならアプリケーションを終了する前にSocketAppender
のclose()
メソッドを明示的に呼び出すか、LoggerContext
のstop()
メソッドを介して呼び出すようにするだけで十分です。
リモートサーバはremotehostプロパティとportプロパティで指定します。
SocketAppender
のプロパティを表にまとめました。SSLSocketAppender
にはこれよりも多くのプロパティが追加されています。詳しくはSSLを使用するの章を参照してください。
プロパティ名 | 型 | 説明 |
---|---|---|
includeCallerData | boolean |
includeCallerDataオプションには真偽値を指定します。trueの場合、リモートホストでログの送信者情報が利用できるようになります。デフォルトでは発信者情報をサーバに送信しません。 |
port | int |
リモートサーバのポート番号です。 |
reconnectionDelay | Duration |
reconnectionDelayオプションには、待ち時間を表す文字列を指定します。たとえば、"10 seconds"と指定した場合、サーバへの接続が失敗するたびに10秒間待ってから、再接続を試みます。このオプションのデフォルト値は30秒です。0を指定すると再接続機能が無効になります。サーバへの接続が成功した場合、コネクタースレッドは存在しないはずなので注意してください。 |
queueSize | int |
queueSizeプロパティには、受信側に配信するために蓄えておくロギングイベントの数を、非負の整数で指定します。キューサイズが0の場合、イベントの配信は同期になります。キューサイズが0より大きい場合、キューに空きがあれば新しいロギングイベントはキューに入れられるようになります。キューの長さを0以外にすると、一時的なネットワークの遅延によって発生する配信の遅れを排除することができるので、性能が向上します。 eventDelayLimitプロパティのことも参照してください。 |
eventDelayLimit | Duration |
eventDelayLimitオプションには、"10 seconds"のような待ち時間を表す文字列を指定します。これは、ローカルキューが満杯になった際、蓄積されたロギングイベントを削除する前に待機する時間を表しています。リモートホストがロギングイベントを受信するのが継続的に遅い場合に発生する可能性があります。このオプションのデフォルト値は100ミリ秒です。 |
remoteHost | String |
リモートサーバのホスト名です。 |
ssl | SSLConfiguration |
SSLSocketAppender だけがサポートするプロパティです。アペンダーから利用されるSSL設定を指定します。詳しくはSSLを使用するを参照してください。 |
サーバ用のオプション
標準的なlogback-classicの配布物には、SocketAppender
あるいはSSLSocketAppender
からロギングイベントを受け取るための2つのサーバ用オプションが含まれています。
ServerSocketReceiver
とそのSSL対応版のSSLServerSocketReceiver
は、リモートホストのソケットアペンダーからロギングイベントを受け取るための受信用コンポーネントです。logback.xmlで設定できるようになっています。設定内容については使用例とレシーバーの章を参照してください。SimpleSocketServer
とそのSSL対応版のSimpleSSLSocketServer
は、スタンドアローンJavaアプリケーションから簡単に使うために用意されたものです。UnixシェルのCUI(コマンドラインインターフェイス)で設定できるようになっています。これらのアペンダーを設定したアプリケーションは、単純にSocketAppender
またはSSLSocketAppender
のクライアントからロギングイベントが送信されてくるのを待ちます。受信したロギングイベントは、自身の設定に従ってロギングされます。使用例は次のとおりです。
SimpleSocketServerの使い方
SimpleSocketServer
アプリケーションはコマンドライン引数を2つ取ります。portとconfigFileです。 portには待ち受けるポート番号を、configFileにはXML形式の設定ファイルを指定します。
logback-examplesディレクトリに移動してから、次のコマンドを実行するとSimpleSocketServer
を開始できます。
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ src/main/java/chapters/appenders/socket/server1.xml
待ち受けるポート番号として6000、設定ファイルとしてserver1.xmlを指定しています。この設定ファイルでは、ルートロガーにConsoleAppender
とRollingFileAppender
を割り当てています。SimpleSocketServer
を開始すれば、複数のロギングクライアントからSocketAppender
を使ってロギングイベントを送信できるようになります。このマニュアルでは2つのクライアントを用意しています。chapters.appenders.SocketClient1
とchapters.appenders.SocketClient2
です。どちらもコンソールで利用者が何かキー入力するのを待つようになっています。利用者がキー入力したテキストはログレベルがDEBUGのロギングイベントに包まれてリモートサーバに送信されます。それぞれのクライアントのSocketAppender
の設定は異なります。SocketClient1
がプログラムでアペンダーを設定しているのに対して、SocketClient2
には設定ファイルが必要です。
SimpleSocketServer
をローカルホスト上で実行しているなら、次のようなコマンドで接続することができます。
java chapters.appenders.socket.SocketClient1 localhost 6000
クライアントのコンソールに入力したテキストは、前の手順で開始したSimpleSocketServer
のコンソールに1行ずつ出力されます。SimpleSocketServer
を一旦停止してから再び開始しても、クライアント側では何もなかったかのように透過的に新しいサーバインスタンスに再接続します。しかし、切断中に発生したロギングイベントは単純に破棄されてしまい、取り返すことができません。
SocketClient1
と違って、SocketClient2
ではアプリケーション自体でlogbackを設定していません。XML形式の設定ファイルが必要です。設定ファイルclient1.xmlの内容は次のとおりです。SocketAppender
を定義して、ルートロガーに割り当てています。
例:SocketAppenderの設定(logback-examples/src/main/java/chapters/appenders/socket/client1.xml)
Groovyとして表示<configuration> <appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"> <remoteHost>${host}</remoteHost> <port>${port}</port> <reconnectionDelay>10000</reconnectionDelay> <includeCallerData>${includeCallerData}</includeCallerData> </appender> <root level="DEBUG"> <appender-ref ref="SOCKET" /> </root> </configuration>
上記の設定スクリプトでは、remoteHost, port、includeCallerDataの値が変数で指定されているのがわかりますか。これらの変数の値は、システムプロパティとして指定することができます。
java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \ chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml
このコマンドを実行すると、前のSocketClient1
と同じ設定になります。
ロギングイベントのシリアライズは非侵入型であることをもう一度アピールさせていただきます。デシリアライズされたロギングイベントは、普通のロギングイベントと全く同じ情報を持っています。したがって、自分が生成したロギングイベントであるかのように扱うことができるのです。ただし、デフォルトではシリアライズされたロギングイベントには送信者情報が含まれていません。この点を説明する例を示します。まずSimpleSocketServer
を準備しましょう。
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ src/main/java/chapters/appenders/socket/server2.xml
設定ファイルserver2.xmlではConsoleAppender
を定義しています。レイアウトには、他の情報と合わせて送信元のファイル名と行番号を指定しています。前のように設定ファイルclient1.xmlを引数としてSocketClient2
を起動します。サーバ側のコンソールに出力されたログには、送信元のファイル名と行番号の代わりに2つのクエスチョンマークが出力されているはずです。
2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi
この出力は簡単に変更できます。SocketAppender
が送信者情報を含めるように設定するには、includeCallerDataオプションにtrueを指定するだけです。次のように実行すればよいです。
java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \ chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml
デシリアライズされたロギングイベントは、ローカルで生成されたロギングイベントと同じように扱うことができます。つまり、追加で何か処理するためにさらに別のリモートサーバに送信することができるのです。サーバを2つ用意して、最初のサーバがクライアントから受け取ったロギングイベントをトンネルのようにそのまま二つ目のサーバに転送するところを確認するのは、読者の演習課題にしておきます。
SimpleSSLSocketServerの使い方
SimpleSSLSocketServer
では、SimpleSocketServer
と同様にコマンドライン引数でportとconfigFileを指定します。それに加えて、ロギングサーバのX.509証明書ファイルの場所とパスワードをシステムパラメータで指定しなければなりません。
logback-examplesディレクトリに移動してから、次のコマンドを実行するとSimpleSSLSocketServer
を開始できます。
java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \ -Djavax.net.ssl.keyStorePassword=changeit \ ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \ src/main/java/chapters/appenders/socket/ssl/server.xml
この例では、テストや検証で使う用のX.509証明書ファイルを指定してSimpleSSLSocketServer
を実行しています。本番環境でSimpleSSLSocketServer
を使う前に、ロギングサーバを識別するための正式なX.509証明書を手に入れなければなりません 。詳しくはSSLを使用するを参照してください。
サーバの使用する設定ファイルのルート要素にdebug="true"
を指定しているので、サーバの開始ログを見ればSSLを設定していることがわかるでしょう。これはセキュリティポリシーが正しく設定されていることを確認するのに便利です。
実行中のSimpleSSLSocketServer
には、SSLSocketAppender
を使って接続することができます。アペンダーの設定例は次のとおりです。
例:SSLSocketAppenderの設定(logback-examples/src/main/java/chapters/appenders/socket/ssl/client.xml)
Groovyとして表示<configuration debug="true"> <appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"> <remoteHost>${host}</remoteHost> <port>${port}</port> <reconnectionDelay>10000</reconnectionDelay> <ssl> <trustStore> <location>${truststore}</location> <password>${password}</password> </trustStore> </ssl> </appender> <root level="DEBUG"> <appender-ref ref="SOCKET" /> </root> </configuration>
前の例と同じく、remoteHostとportの値は変数になっています。また、sslプロパティとネストしているtrustStoreプロパティが増えているので注意してください。locationとpasswordも変数になっています。例として用意したサーバが自己署名証明書を使っているので、これらの設定は必須です。SSLSocketAppender
のSSL設定について、詳しくはSSLを使用するを参照してください。
コマンドラインから、設定ファイルで使用している変数をシステムプロパティとして指定すれば、クライアントアプリケーションを実行することができます。
java -Dhost=localhost -Dport=6000 \ -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \ -Dpassword=changeit \ chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/ssl/client.xml
前の例と同じく、クライアントアプリケーションのコンソールにメッセージを入力することができます。そうすると、メッセージは(安全な通信路上で)ロギングサーバに送信されます。そして、サーバ側のコンソールにログが出力されます。
コマンドラインで指定したtruststoreプロパティには、信頼できるキーストアのURLを指定することに注意しましょう。SSLを使用するでも説明していますが、URLでクラスパス上のリソースを指定することもできます。
前の例でサーバの起動時にいろいろと出力されているのと同じように、クライアントの設定ファイルのルート要素にdebug="true"
と指定しているので、クライアントの起動時にもSSL設定に関する情報が出力されています。ローカルポリシーの適合性の監査に役立つでしょう。
ServerSocketAppenderとSSLServerSocketAppender
前に説明したSocketAppender
コンポーネントとSSL対応版のSSLSocketAppenderは、ネットワークの向こう側のサーバにロギングイベントを配信するためのものです。アプリケーションがリモートロギングサーバに接続するために設計されています。場合によりますが、アプリケーションから特定のリモートロギングサーバへの接続を確立するのが不便だったり不可能だったりすることがあります。そういう場合のためにlogbackではServerSocketAppender
が用意されています。
ServerSocketAppender
は、特定のリモートロギングサーバとの接続を確立する代わりに、リモートクライアントからのTCPソケット接続を待ち受けます。アペンダーに送信されたロギングイベントは、接続しているクライアントに配布されます。接続しているクライアントがいなければ、ロギングイベントはすぐに破棄されます。
logback は、普通のServerSocketAppender
だけでなく、SSL対応版のSSLServerSocketAppender
も用意しています。安全な、暗号化された通信路で接続したクライアントにロギングイベントを配布します。さらに、SSL対応版のアペンダーは、完全な相互認証をサポートしています。つまり、認証されたクライアントだけがロギングイベントを受信するためにアペンダーに接続できることが保証されるのです。
通信路上でのロギングイベントの符号化方法は使用しているSocketAppender
に関わらず同一です。ロギングイベントはILoggingEvent
のインスタンスがシリアライズされたものです。接続を確立する方向だけが逆転しています。SocketAppender
が特定のロギングサーバに対して接続を確立しようとする活性ピアとして振る舞うのに対して、ServerSocketAppender
は受動的にクライアントからの接続を待ち受けます。
ServerSocketAppender
の派生タイプは、logbackの他のレシーバーコンポーネントとは排他的に利用されることを想定しています。コンポーネントタイプに関する詳細についてはレシーバーを参照してください。
ServerSocketAppender
の設定可能なプロパティを表にまとめました。
プロパティ名 | 型 | 説明 |
---|---|---|
address | String |
アペンダーが待ち受けるためのローカルネットワークインターフェイスに割り当てられたIPアドレス。このプロパティが指定されていない場合、アペンダーはすべてのネットワークインターフェイスで待ち受けます。 |
includeCallerData | boolean |
trueの場合、リモートホスト側で送信者情報が利用できるようになります。デフォルトでは、送信者情報はクライアントに送信されません。 |
port | int |
アペンダーが待ち受けるポート番号。 |
ssl | SSLConfiguration |
SSLServerSocketAppender でのみ使用出来るプロパティ。SSLを使用するで説明したように、アペンダーの使用するSSLの設定を指定します。 |
ServerSocketAppender
の使用例を次に示します。
例:ServerSocketAppenderの基本的な設定(logback-examples/src/main/java/chapters/appenders/socket/server4.xml)
<configuration debug="true"> <appender name="SERVER" class="ch.qos.logback.classic.net.server.ServerSocketAppender"> <port>${port}</port> <includeCallerData>${includeCallerData}</includeCallerData> </appender> <root level="debug"> <appender-ref ref="SERVER" /> </root> </configuration>
前の例との違いは、class属性に指定しているのがSocketAppender
ではないこと、remoteHostプロパティが無いことだけなのがわかりましたか。このアペンダーはリモートロギングサーバに接続するのではなく、リモートホストからの接続を受動的に待ち受けます。
SSLServerSocketAppender
の設定例は次のとおりです。
例:SSLServerSocketAppenderの基本的な設定(logback-examples/src/main/java/chapters/appenders/socket/server3.xml)
<configuration debug="true"> <appender name="SERVER" class="ch.qos.logback.classic.net.server.SSLServerSocketAppender"> <port>${port}</port> <includeCallerData>${includeCallerData}</includeCallerData> <ssl> <keyStore> <location>${keystore}</location> <password>${password}</password> </keyStore> </ssl> </appender> <root level="debug"> <appender-ref ref="SERVER" /> </root> </configuration>
前の例との主な違いは、class属性にSSLServerSocketAppender
を指定していることと、ネストしているssl要素があることです。この例ではアペンダー用のX.509証明書が置かれたキーストアが指定されています。SSLの設定について詳細はSSLを使用するを参照してください。
SMTPAppender
SMTPAppender
は、一つ以上の固定サイズのバッファにロギングイベントを蓄積し、利用者が指定したイベントが発生したら適切なバッファを選んで内容をメールで送信します。SMTPによるメール送信は非同期で実行されます。デフォルトでは、ERRORレベルのロギングイベントがトリガとなってメールが送信されます。また、1つのバッファがすべてのロギングイベントから使用されます。
SMTPAppender
の設定可能なプロパティを表にまとめました。
プロパティ名 | 型 | 説明 |
---|---|---|
smtpHost | String |
SMTPサーバのホスト名。これは必須パラメータです。 |
smtpPort | int |
SMTPサーバーの待ち受けポート番号。デフォルトでは25です。 |
to | String |
recipient のメールアドレスのパターンを指定します。指定したパターンは、送信されるメールそれぞれに対して、トリガとなったロギングイベントと共に評価されます。複数のrecipientを指定するときは、宛先アドレスをカンマ区切りにします。また、to要素 を複数並べることもできます。
|
from | String |
SMTPAppender の送信するメールメッセージの originator を一般的なメールアドレス形式で指定します。送信者名を含めたい場合は "Adam Smith <smith@moral.org>" のようにします(XML形式の設定ファイルではブラケットを文字実態参照にします)。そうすると、メールでは "Adam Smith <smith@moral.org>" のようになります。
|
subject | String |
メールの件名。PatternLayoutで利用できる全ての値を指定できます。レイアウトについては次の章で説明します。 送信されるメールメッセージの件名には、メール送信をトリガしたロギングイベントを適用したパターンが指定されます。 subjectオプションに指定されたパターンが "Log: %logger - %msg" で、"com.foo.Bar"ロガーが "Hello World" というメッセージのロギングイベントを発生して、このロギングイベントがトリガとなった場合、送信されるメールの件名は "Log: com.foo.Bar - Hello World" になるでしょう。 デフォルトでは、"%logger{20} - %m" が設定されています。 |
discriminator | Discriminator |
デフォルト以外の弁別器を指定すれば、特定のユーザー、ユーザーセッション、クライアントのIPアドレスに関連するロギングイベントを含むメールだけを送信することもできます。 |
evaluator | IEvaluator |
このオプションを指定するには、新しく このオプションを指定しなかった場合、 logback の配布物には他の評価器もいくつか含まれています。 |
cyclicBufferTracker | CyclicBufferTracker
|
名前が示す通り、 cyclicBufferTracker指定しなかった場合、自動的にCyclicBufferTrackerImplのインスタンスを生成して使用します。デフォルトで256個のロギングイベントを循環バッファに保持します。bufferSizeオプションを指定してサイズを変更することもできます(下記参照)。 |
username | String | 平文のパスワード認証をするときに使用するユーザー名。デフォルトではnullです。 |
password | String |
平文のパスワード認証をするときに使用するパスワード。デフォルトではnullです。 |
STARTTLS | boolean |
trueを指定すると、サーバがサポートしているならSSL接続に切り替えるため、STARTTLSコマンドを発行します。接続が確立した時点では暗号化されていないので注意してください。デフォルトではfalseに設定されています。 |
SSL | boolean | trueを指定するとサーバにSSL接続をします。デフォルトではfalseです。 |
charsetEncoding | String |
送信されるメッセージは指定された文字セットでエンコードされます。デフォルトの文字セットは"UTF-8"ですが、ほとんどの場合十分でしょう。 |
localhost | String |
SMTPクライアントのホスト名が正しく設定されていない場合(例えば、ホスト名が完全修飾名ではない場合)、SMTPサーバの中にはそういうクライアントから送信された HELO/EHLO コマンドを拒否することがあります。この問題を解決するには、クライアントのホスト名として、localhostに完全修飾名を指定することができます。com.sun.mail.smtpパッケージのドキュメントで説明されている "mail.smtp.localhost" パラメータも参考にしてください。 |
asynchronousSending | boolean |
メール送信を非同期で行うかどうかを指定します。デフォルトはtrueです。しかし、非同期送信が不適切な場合もあります。たとえば、あなたのアプリケーションで致命的なエラーが発生した際、SMTPAppender でアラートメールを送信してから終了するとしても、その仕事を任されたスレッドにはメールを送信する時間が残されていないからです。そういう場合は同期的にメール送信をするためfalseを指定しましょう。
|
includeCallerData | boolean |
デフォルトはfalse です。asynchronousSendingが有効でログに送信者情報を含めたいときはtrue を指定しなければなりません。 |
sessionViaJNDI | boolean |
javax.mail.Session を使用してメールを送信します。デフォルトはfalse なので、SMTPAppender は利用者の指定した設定に従ってjavax.mail.Session インスタンスを構築します。true を指定するとJNDIからjavax.mail.Session インスタンスを取得します。jndiLocationプロパティも参照してください。
注意JNDIから |
jndiLocation | String |
JNDIのjavax.mail.Sessionの位置を指定します。デフォルトは"java:comp/env/mail/Session"です。 |
SMTPAppender
は循環バッファで最新の256個のロギングイベントだけを保持します。バッファが一杯になったら古いロギングイベントを捨てます。したがって、SMTPAppender
からメールで送信するロギングイベントの最大値は256個になります。つまり、アプリケーションコンテキストに妥当なメモリ量を割り当てられるよう、メモリ要件に合わせて制限できるということです。
SMTPAppender
はJavaMail APIにも依存しています。テストされているJavaMail APIのバージョンは1.4です。JavaMail APIにはJavaBeans Activation フレームワークが必要です。JavaMail APIとJavaBeans Activation フレームワークはそれぞれのウェブサイトからダウンロードすることができます。以降の例を試す前に、クラスパス上にこれらのjarファイルが配置されていることを確かめてください。
サンプルアプリケーションでは、chapters.appenders.mail.EMail
がいくつかのログメッセージを生成したあとで、エラーメッセージを1つ生成しています。このアプリケーションの引数は二つあります。一つ目のパラメータは生成するロギングイベントの数となる整数値です。二つ目のパラメータはlogbackの設定ファイルです。EMailアプリケーションが最後に生成するロギングイベントのログレベルはERRORなので、メール送信をトリガします。
Email
アプリケーションから使う設定ファイルは次のとおりです。
例:SMTPAppender
の設定例(logback-examples/src/main/java/chapters/appenders/mail/mail1.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost> <to>EMAIL-DESTINATION</to> <to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible --> <from>SENDER-EMAIL</from> <subject>TESTING: %logger{20} - %m</subject> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date %-5level %logger{35} - %message%n</pattern> </layout> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
上記の設定ファイルでchapters.appenders.mail.Email
アプリケーションを動かしてみる前に、smtpHostやtoやfromへ、あなたの環境に適した値を指定しなければなりません。設定ファイルに正しい値を指定したら次のコマンドを実行しましょう。
java chapters.appenders.mail.EMail 100 src/main/java/chapters/appenders/mail/mail1.xml
指定したrecipientにはPatternLayout
で指定したとおりに書式化された100個のロギングイベントが含まれたメールが届くはずです。Mozilla Thunderbird で受信したメールを開いたところを示します。
設定ファイルmail2.xmlでは、smtpHostやtoやfromが変数で指定されています。mail2.xmlの大事な部分を次に示します。
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> </appender>
コマンドラインで必要なパラメータを指定します。
java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \ chapters.appenders.mail.EMail 10000 src/main/java/chapters/appenders/mail/mail2.xml
値はあなたの環境で利用できるものに置き換えてください。
最後の例では、PatternLayout
がHTMLLayout
になっていました。これはログの内容をHTMLのテーブルとして書式化します。列の並びと順番は変更することができます。CSSも変更できます。HTMLLayoutについて詳しくはドキュメントを参照してください。
循環バッファのサイズが256なので、recipientが受け取るメールでは256個のロギングイベントが綺麗にHTMLのテーブルに整形されるようになっています。chapters.appenders.mail.Email
アプリケーションを実行して10000件のロギングイベントを生成しても、送信されるメールには最新の256件しか含まれないことになるので気をつけてください。
Mozilla ThunderbirdやEudoraやMS Outlookといったメールクライアントは、HTML形式のメールでもCSSをわりと上手く処理します。ですが、勝手にHTMLを平文テキストにダウングレードすることがあります。例えば、Thunderbird でHTMLメールを表示するには、"表示→メッセージ本文→オリジナルのHTML" でオプションを指定しなければなりません。Yahoo!メールはHTMLメールをサポートしており、CSSの対応具合はピカ一です。一方、Gmailは普通のHTMLテーブルはそのまま表示してくれますが、内部CSSによる整形はしてくれません。GmailはインラインCSSをサポートしていますが、インラインCSSを使うとHTMLソースコードが膨れ上がってしまうので、HTMLLayout
ではインラインCSSを使用しません。
カスタムバッファサイズ
デフォルトでは、SMTPAppender
から送信されるメッセージには最大で256件のロギングメッセージが含まれています。次の例に示すように、別のバッファサイズを指定することができます。
例: バッファサイズを変更したSMTPAppender
の設定(logback-examples/src/main/java/chapters/appender/mail/customBufferSize.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <subject>%logger{20} - %m</subject> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker"> <!-- send just one log entry per email --> <bufferSize>1</bufferSize> </cyclicBufferTracker> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
トリガイベント
Evaluatorプロパティが指定されなかったら、SMTPAppender
はデフォルトでOnErrorEvaluatorを使用して、ERRORレベルのロギングイベントの発生をトリガとしてメールを送信する。エラーの発生に応じて、メール送信をトリガするのはそれなりに合理的ですが、EventEvaluator
インターフェイスの別の実装を指定すればデフォルトの振る舞いを上書きすることができます。
SMTPAppender
はevaluate()
メソッドを呼び出すことで、受け取ったロギングイベントを評価器に渡します。そのロギングイベントがメール送信をトリガするものなのか、単に循環バッファに入れておくだけでいいのかを判定するためです。評価器が評価した結果が真なら、メールを送信します。SMTPAppender
の保持する評価器オブジェクトは1つだけです。このオブジェクトは、自身の内部状態を管理することができます。わかりにくいので、CounterBasedEvaluator
クラスのコードで説明します。これはロギングイベントが1024件発生するたびにメール送信をトリガします。
例: 1024件ごとに評価値true
を返すEventEvaluator
インターフェイスの実装(logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java)
package chapters.appenders.mail; import ch.qos.logback.core.boolex.EvaluationException; import ch.qos.logback.core.boolex.EventEvaluator; import ch.qos.logback.core.spi.ContextAwareBase; public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator { static int LIMIT = 1024; int counter = 0; String name; public boolean evaluate(Object event) throws NullPointerException, EvaluationException { counter++; if (counter == LIMIT) { counter = 0; return true; } else { return false; } } public String getName() { return name; } public void setName(String name) { this.name = name; } }
このクラスはContextAwareBase
を継承して、EventEvaluator
インターフェイスを実装しているのがわかるでしょうか。こうすれば、利用者はEventEvaluator
としての中心機能に集中することができるし、基底クラスを使って共通する機能を提供できるようになります。
SMTPAppender
のevaluatorオプションを指定するということは、カスタム評価器を使うということです。次の設定ファイルではルートロガーにSMTPAppender
を割り当てています。SMTPAppenderでは、CounterBasedEvaluator
を評価器として指定しています。
例: SMTPAppender
とカスタム評価器
とバッファサイズの設定例(logback-examples/src/main/java/chapters/appenders/mail/mail3.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="chapters.appenders.mail.CounterBasedEvaluator" /> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <subject>%logger{20} - %m</subject> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
マーカーに基づくトリガ
全てのERRORレベルのロギングイベントをトリガとしてメールを送信するデフォルトのポリシーは合理的ではありますが、メールを送信する機会が多すぎると対象になっているユーザのメールボックスを埋め尽くしてしまいます。logback の配布物にはOnMarkerEvaluatorというトリガポリシーも含まれています。これはマーカーに基づいてトリガするものです。原則として、利用者が指定したマーカーのロギングイベントが発生したときだけメール送信がトリガされます。次の例を見ればどういう動き方をするのかはっきりするでしょう。
Marked_EMailのアプリケーションには、ERRORレベルを含むいくつものロギング式があります。そのうちで、一つのロギング式にだけマーカーが指定されています。該当するコードは次のとおりです。
Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN"); logger.error(notifyAdmin, "This is a serious an error requiring the admin's attention", new Exception("Just testing"));
次の設定ファイルは、マーカーとしてNOTIFY_ADMINあるいはTRANSACTION_FAILUREを指定されたロギングイベントが発生したときだけメール送信をトリガするものです。
例: OnMarkerEvaluator
を指定したSMTPAppender
の設定(logback-examples/src/main/java/chapters/appenders/mail/mailWithMarker.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator"> <marker>NOTIFY_ADMIN</marker> <!-- you specify add as many markers as you want --> <marker>TRANSACTION_FAILURE</marker> </evaluator> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> </appender> <root> <level value ="debug"/> <appender-ref ref="EMAIL" /> </root> </configuration>
次のコマンドを実行してみましょう。
java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \ chapters.appenders.mail.Marked_EMail src/main/java/chapters/appenders/mail/mailWithMarker.xml
JaninoEventEvaluatorを使ったマーカーに基づくトリガ
マーカーだけを対象にしたOnMarkerEvaluator
の代わりに、より汎用的なJaninoEventEvaluator
を使うことができますし、それ以上に強力なGEventEvaluator
を使うことも出来ます。たとえば、次の設定ファイルはOnMarkerEvaluator
の代わりにJaninoEventEvaluator
を指定した以外は前の設定ファイルとまったく同じ内容になります。
例: JaninoEventEvaluator
を指定したSMTPAppender
の設定(logback-examples/src/main/java/chapters/appenders/mail/mailWithMarker_Janino.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <expression> (marker != null) && (marker.contains("NOTIFY_ADMIN") || marker.contains("TRANSACTION_FAILURE")) </expression> </evaluator> ... same as above </appender> </configuration>
GEventEvaluatorを使ったマーカーに基づくトリガ
GEventEvaluatorを使っている以外は前の例と同じ内容です。
例:GEventEvaluator
を指定したSMTPAppenderの設定(logback-examples/src/main/java/chapters/appenders/mail/mailWithMarker_GEvent.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"> <expression> e.marker?.contains("NOTIFY_ADMIN") || e.marker?.contains("TRANSACTION_FAILURE") </expression> </evaluator> ... same as above </appender> </configuration>
マーカーを指定されなかったロギングイベントの場合、e.markerはnullになるので注意してください。この例ではGroovyの安全なデリファレンス演算子である .? 演算子を使っています。
認証/ STARTTLS / SSL
SMTPAppender
では、平文のユーザーパスワード認証だけでなく、STARTTLSとSSLプロトコルの両方をサポートしています。STARTTLSとSSLの違いは、STARTTLSでは接続を確立するときは暗号化されないこと、そして、クライアントがSTARTTLSコマンドを発行してサーバがサポートしている場合はSSL接続に切り替えることです。SSLでは始めから通信が暗号化されます。
GmailにSSLで接続するSMTPAppenderの設定
次の例はGmailにSSLプロトコルで接続するSMTPAppender
の設定です。
例:GmailにSSLで接続するSMTPAppender
の設定(logback-examples/src/main/java/chapters/appenders/mail/gmaliSSL.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>smtp.gmail.com</smtpHost> <smtpPort>465</smtpPort> <SSL>true</SSL> <username>YOUR_USERNAME@gmail.com</username> <password>YOUR_GMAIL_PASSWORD</password> <to>EMAIL-DESTINATION</to> <to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible --> <from>YOUR_USERNAME@gmail.com</from> <subject>TESTING: %logger{20} - %m</subject> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date %-5level %logger{35} - %message%n</pattern> </layout> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
STARTTLSでGmailに接続するSMTPAppenderの設定
次の例はGmailにSTARTTLSで接続するSMTPAppender
の設定です。
例:GmailにSTARTTLSで接続するSMTPAppender
の設定(logback-examples/src/main/java/chapters/appenders/mail/gmailSTARTTLS.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>smtp.gmail.com</smtpHost> <smtpPort>587</smtpPort> <STARTTLS>true</STARTTLS> <username>YOUR_USERNAME@gmail.com</username> <password>YOUR_GMAIL_xPASSWORD</password> <to>EMAIL-DESTINATION</to> <to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible --> <from>YOUR_USERNAME@gmail.com</from> <subject>TESTING: %logger{20} - %m</subject> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date %-5level %logger - %message%n</pattern> </layout> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
MDCDiscriminatorを指定したSMTPAppenderの設定
前述したように、SMTPAppender
にデフォルト以外の弁別器を指定すれば、特定のユーザ、ユーザセッション、送信元のIPアドレスを含むロギングイベントが発生した場合にだけメールメッセージを生成することができます。
次の例はMDCBasedDiscriminatorに"req.remoteHost"というキーを指定したものです。値として、架空のWebアプリケーションにアクセスしてきたクライアントのリモートホストのIPアドレスが設定されることを想定しています。WebアプリケーションならMDCInsertingServletFilter使ってMDCに値を設定することができます。
例:MDCDiscriminatorを指定したSMTPAppender
の設定(logback-examples/src/main/java/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml)
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost> <to>EMAIL-DESTINATION</to> <from>SENDER-EMAIL</from> <discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"> <key>req.remoteHost</key> <defaultValue>default</defaultValue> </discriminator> <subject>${HOSTNAME} -- %X{req.remoteHost} %msg"</subject> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg</pattern> </layout> </appender> <root> <level level="DEBUG"/> <appender-ref ref="EMAIL" /> </root> </configuration>
こうすると、SMTPAppender
の送信するメールそれぞれに固有のIPアドレスが記録されるようになるので、問題解決に役立ちます。
高負荷システムにおけるバッファ管理(★要見直し)
内部的な事情ですが、弁別器が返す値ごとに循環バッファが作成されます。しかし、ほとんどの場合maxNumberOfBuffers(デフォルト値は64)はそのままです。バッファの数がmaxNumberOfBufferesを越えてしまうと、一番最近更新されたバッファはすぐに自動的に破棄されてしまいます。もう一つの予防策として、直近の30分間に更新されなかったバッファはやはり自動的に破棄されてしまいます。
毎分大量のトランザクションを処理するシステムでは、maxNumberOfBuffers(デフォルト値は64)が小さいと送信するメールに含まれるロギングイベントの数がとても少なくなってしまいます。大量のトランザクションが発生する場合、同じトランザクションには1つ以上のバッファが関連付けられてしまいます。弁別器が同じトランザクションには同じ値を返すので、バッファの破棄と生成が繰り返されてしまうからです。高負荷システムであっても、循環バッファの上限はmaxNumberOfBufferesによって制御されてしまうので注意してください。
ヨーヨー効果を避けるため、SMTPAppender
は"FINALIZE_SESSION"というマーカーの指定されたロギングイベントを見つけたら、そのロギングイベントに対して弁別器の返す値に関連付けられたバッファを直ちに開放するようになっています。これにより、トランザクションの終了時に適切にバッファを廃棄できるようになります。そうすれば、maxNumberOfBuffersには、安全のためより大きな値の512や1024を指定することができます。メモリ不足の危険性はありません。
循環バッファを管理するために協調的に機能する、3つの相補的な仕組みがあります。これらの仕組みが、高負荷システムであっても常に有効なバッファが利用できることを保証するのです。
DBAppender
DBAppender
はデータベース上の3つのテーブルに、Javaプログラミング言語に依存しない形式のロギングイベントを登録します。
3つのテーブルとは、logging_event、logging_event_property、logging_event_exceptionです。DBAppender
を使う前に事前に用意しておかなければなりません。logack の配布物にテーブルを作成するSQLスクリプトが含まれています。フォルダの場所は logback-classic/src/main/java/ch/qos/logback/classic/db/scriptです。一般的なデータベースそれぞれのスクリプトが用意されています。あなたの使用するデータベース用のスクリプトが無かったとしても、既存のスクリプトを参考にすれば自分で作るのは簡単です。logback の開発メンバーに教えてくれれば、喜んで今後のリリースに含めるようにします。
あなたが使用しているJDBCドライバが、JDBC3.0で導入されたgetGeneratedKeys()
メソッドをサポートしているなら、紹介したスクリプトでテーブルを作ること以外に必要な作業はありません。そうはいっても、データベースに対応するSQLDialect
を指定しなければなりません。今のところ logback が対応しているSQL方言は、H2、HSQL、MS SQLServer、MySQL、Oracle、PostgreSQL、SQLite、Sybase です。
データベースの種類と、getGeneratedKeys()
メソッドの対応状況を表にまとめました。
データベースの種類 | テストしたバージョン | テストしたJDBCドライバのバージョン | {0}getGeneratedKeys(){/0}メソッド
の対応状況 |
logbackがSQL方言を提供しているかどうか
|
---|---|---|---|---|
DB2 | 未テスト | 未テスト | 不明 | 無し |
H2 | 1.2.132 | - | 不明 | 提供している |
HSQL | 1.8.0.7 | - | 未対応 | 提供している |
Microsoft SQL Server | 2005 | 2.0.1008.2(sqljdbc.jar) | 対応済み | 提供している |
MySQL | 5.0.22 | 5.0.8(mysql-connector.jar) | 対応済み | 提供している |
PostgreSQL | 8.x | 8.4-701.jdbc4 | 未対応 | 提供している |
Oracle | 10g | 10.2.0.1(ojdbc14.jar) | 対応済み | 提供している |
SQLLite | 3.7.4 | - | 不明 | 提供している |
Sybase SQLAnywhere | 10.0.1 | - | 不明 | 提供している |
検証したところ、"標準的"なPCでは1つのロギングイベントをデータベースに書き込むのにおよそ10ミリ秒かかるようです。コネクションプールを使えば1ミリ秒くらいは速くなるでしょう。一般的に利用できるほとんどのJDBCドライバではコネクションプールを使うことができるので、ぜひそうしてください。
DBAppender
の設定方法はいろいろありますが、データベースに接続するツールや、データベース自体によって異なります。DBAppender
の設定で重要なのは、ConnectionSource
です。どういうものか簡単に説明しましょう。
DBAppender
がデータベースに接続できたら、ロギングイベントは指定されたデータベースに送信されます。前述したとおり、logbackはロギングイベントを3つのテーブルに格納します。
logging_eventテーブルには次のようなカラムがあります。
カラム名 | 型 | 説明 |
---|---|---|
timestamp | big int |
ロギングイベントが作成された時のタイムスタンプ。 |
formatted_message | text |
org.slf4j.impl.MessageFormatter で書式化されてからロギングイベントに設定されたメッセージ。引数のオブジェクトはメッセージにくっついています。 |
logger_name | varchar |
ロギング要求を発行したロガーの名前。 |
level_string | varchar |
ロギングイベントのレベル。 |
reference_flag | smallint |
このフィールドは、ロギングイベントに例外オブジェクトが含まれているか、もしくは、 値は |
caller_filename | varchar |
ロギング要求を発行した場所が含まれるファイル名。 |
caller_class | varchar |
ロギング要求を発行したクラス名。 |
caller_method | varchar |
ロギング要求を発行したメソッド名。 |
caller_line | char |
ロギング要求を発行した場所の行番号。 |
event_id | int |
データベースが払いだしたロギングイベントのID。 |
logging_event_propertyには、MDC
またはContext
に含まれるキーと値を格納します。次のようなカラムがあります。
カラム名 | 型 | 説明 |
---|---|---|
event_id | int |
データベースが払いだしたロギングイベントのID。 |
mapped_key | varchar |
MDC のキー値。 |
mapped_value | text |
MDC の値。 |
logging_event_exceptionテーブルには、次のようなカラムがあります。
カラム名 | 型 | 説明 |
---|---|---|
event_id | int |
データベースが払いだしたロギングイベントのID。 |
i | smallint |
完全なスタックトレースを文字列化した際の各行の添字。 |
trace_line | varchar |
スタックトレースの文字列中の1行。 |
DBAppender
を使用した結果をもっと視覚的にわかりやすくお見せしましょう。次に示すのは、DBAppender
がMySQLデータベースを使用した場合のスクリーンショットです。
logging_eventテーブル
logging_event_exceptionテーブル
logging_event_propertyテーブル
ConnectionSource
ConnectionSource
インターフェイスは、logbackがjava.sql.Connection
を取得するためのJDBC接続を透過的に取得するためのプラグイン可能な機能を提供するものです。ConnectionSource
の実装クラスは3つあります。DataSourceConnectionSource
、DriverManagerConnectionSource
、JNDIConnectionSource
です。
最初に、DriverManagerConnectionSource
を使ってMySQLデータベースに接続する例を見てみましょう。次の設定ファイルを見てください。
例: DBAppender
の設定(logback-examples/src/main/java/chapters/appenders/db/append-toMySQL-with-driveManger.xml)
<configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://host_name:3306/datebase_name</url> <user>username</user> <password>password</password> </connectionSource> </appender> <root level="DEBUG" > <appender-ref ref="DB" /> </root> </configuration>
正しいJDBCドライバクラス名を指定してください。この例ではcom.mysql.jdbc.Driver
です。urlはjdbc:mysql://で始まらなければなりません。
DriverManagerConnectionSource
はConnectionSource
の実装クラスで、JDBCの伝統的なやり方(接続用URLに基づくやり方)でデータベース接続を取得します。
このクラスはgetConnection()
メソッドが呼ばれるたびに新しいConnection
を生成することに注意してください。コネクションプーリングをサポートしているJDBCドライバーを使うか、コネクションプーリングを利用するConnectionSource
を自分で実装することをおすすめします。Java EE アプリケーションサーバの上で、javax.sql.DataSource
をサポートしたJNDI実装を利用する場合は、後述するJNDIConnectionSource
を参照してください。
DataSource
を使ってデータベースに接続する場合もだいたい同じです。設定ファイルではDataSourceConnectionSource
を指定してください。JDBCの推奨するやり方(javax.sql.DataSource
に基づくやり方)でデータベース接続(Connection
)を取得します。
例: DBAppender
の設定(logback-examples/src/main/java/chapters/appenders/db/append-with-datasource.xml)
<configuration debug="true"> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="${dataSourceClass}"> <!-- Joran cannot substitute variables that are not attribute values. Therefore, we cannot declare the next parameter like the others. --> <param name="${url-key:-url}" value="${url_value}"/> <serverName>${serverName}</serverName> <databaseName>${databaseName}</databaseName> </dataSource> <user>${user}</user> <password>${password}</password> </connectionSource> </appender> <root level="INFO"> <appender-ref ref="DB" /> </root> </configuration>
この設定例ではたくさん変数を使っているので気をつけてください。1つの設定ファイルに接続情報の詳細をまとめておくと、logback と他のフレームワークで設定内容を共有できるので便利です。
JNDIConnectionSource
JNDIConnectionSource
もlogbackの配布物に含まれるConnectionSource
の実装クラスです。名前のとおり、JNDIからjavax.sql.DataSource
を取得し、そこからjava.sql.Connection
を取得します。JNDIConnectionSource
は、Java EE アプリケーションサーバの内部か、アプリケーションサーバのクライアント(アプリケーションサーバのjavax.sql.DataSource
にリモートアクセスできることを想定しています)で使用することを念頭に設計されています。したがって、他にどんな便利機能を提供しているかはともかくとして、少なくともコネクションプーリングを利用することができるはずです。もっと重要なのは、logback.xmlでDataSource
を定義しなくてもよくなるので、アプリケーションをよりDRYにできることです。
次の例はTomcat用の設定ファイルから抜粋したものです。PostgreSQL で使うための設定ですが、サポートしているデータベースならどれでも同じように動くはずです。
<Context docBase="/path/to/app.war" path="/myapp"> ... <Resource name="jdbc/logging" auth="Container" type="javax.sql.DataSource" username="..." password="..." driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost/..." maxActive="8" maxIdle="4"/> ... </Context>
Java EE アプリケーションサーバで定義したDataSource
をlogbackの設定ファイルから参照するのは簡単です。
例:JNDIConnectionSource
を使ったDBAppender
の設定(logback-examples/src/main/java/chapters/appenders/db/append-via-jndi.xml)
<configuration debug="true"> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> <!-- please note the "java:comp/env/" prefix --> <jndiLocation>java:comp/env/jdbc/logging</jndiLocation> </connectionSource> </appender> <root level="INFO"> <appender-ref ref="DB" /> </root> </configuration>
このクラスは引数無しのコンストラクタでjavax.naming.InitialContext
のインスタンスを生成するので注意してください。ほとんどの Java EE コンテナで正常に動作します。Java EE コンテナ以外で動かすときは、JNDIプロバイダのドキュメントで説明されたとおりにjndi.propertiesを用意してください。
コネクションプーリング
ロギングイベントはかなり高い頻度で生成されることがあります。ロギングイベントが生成されるのに合わせてデータベースに登録していなければなりません。そのためには、DBAppender
でコネクションプーリングを利用するとよいでしょう。
DBAppender
でコネクションプーリングを利用すると、著しく性能が改善することが実証されています。次の設定ファイルは、コネクションプーリング無しでMySQLデータベースにロギングイベントを登録するものです。
例: コネクションプーリング無しのDBAppender
の設定(logback-examples/src/main/java/chapters/appenders/db/append-toMySQL-with-datasource.xml)
<configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"> <serverName>${serverName}</serverName> <port>${port$</port> <databaseName>${dbName}</databaseName> <user>${user}</user> <password>${pass}</password> </dataSource> </connectionSource> </appender> <root level="DEBUG"> <appender-ref ref="DB" /> </root> </configuration>
この設定ファイルでは、MySQLデータベースに500件のロギングイベントを送信するのになんと5秒もかかりました。つまり、1件あたり10ミリ秒もかかるのです。大規模なアプリケーションでは使いものにならないことがよくわかると思います。
DBAppender
でコネクションプーリングを利用するには、外部ライブラリが必要です。次の例はc3p0を使っています。c3p0を利用するには、まずダウンロードして、c3p0-VERSION.jarをクラスパス上に配置しなければなりません。
例: DBAppender
でコネクションプーリングを利用する設定(logback-examples/src/main/java/chapters/appenders/db/append-toMySQL-with-datasource-and-pooling.xml)
<configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}</jdbcUrl> <user>${user}</user> <password>${password}</password> </dataSource> </connectionSource> </appender> <root level="DEBUG"> <appender-ref ref="DB" /> </root> </configuration>
この設定ファイルを使った場合、MySQLデータベースに500件のロギングイベントを送信するのにかかった時間は約0.5秒でした。1件あたりの所要時間は1ミリ秒です。つまり、10倍高速化できたことになります。
SyslogAppender
syslogプロトコルは非常に単純なプロトコルです。syslogの送信者は小さなメッセージをsyslogの受信者に送信します。一般的に受信者はsyslogデーモンやsyslogサーバと呼ばれます。logbackは、SyslogAppender
を使ってリモートのsyslogデーモンにメッセージを送信することができます。
SyslogAppenderの設定可能なプロパティは次のとおりです。
プロパティ名 | 型 | 説明 |
---|---|---|
syslogHost | String |
syslogサーバのホスト名。 |
port | String |
syslogサーバーのポート番号。ほとんどの場合はデフォルト値の514を使うでしょう。 |
facility | String |
facilityは、メッセージの送信元を区別するためのものです。 facilityオプションには、次のいずれかの文字列を指定しなければなりません。KERN、USER、MAIL、DAEMON、AUTH、SYSLOG、LPR、NEWS、UUCP、CRON、AUTHPRIV、FTP、NTP、AUDIT、ALERT、CLOCK、LOCAL0、LOCAL1、LOCAL2、LOCAL3、LOCAL4、のlocal5、LOCAL6、LOCAL7。大文字小文字は区別されません。 |
suffixPattern | String |
suffixPatternオプションには、syslogサーバに送信されるメッセージ中の任意部分の書式を指定します。デフォルトは"[%thread] %logger %msg"です。 |
stackTracePattern | String |
stackTracePatternプロパティには、スタックトレースの各行の先頭に表示する文字列を指定します。デフォルトはタブ文字、つまり "\t"です。 |
throwableExcluded | boolean |
true を指定すると、Throwableに関連付けられているスタックトレースの情報を省略するようになります。デフォルトはfalese です。ですので、syslogサーバにはスタックトレースの情報が送信されます。 |
ロギングイベントに対する syslog の severity(重要度)は、ロギングレベルを変換したものになります。DEBUGは7、INFOは6、WARNは4、ERRORは3に変換されます。
syslog要求の書式には厳密なルールがあるので、SyslogAppender
はレイアウトを使用しません。しかし、suffixPatternオプションを使えば思った通りの内容を表示することができます。
SyslogAppender
の設定例を見てみましょう。
例: SyslogAppender
の設定(logback-examples/src/main/java/chapters/appenders/conf/logback-syslog.xml)
<configuration> <appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender"> <syslogHost>remote_home</syslogHost> <facility>AUTH</facility> <suffixPattern>[%thread] %logger %msg</suffixPattern> </appender> <root level="DEBUG"> <appender-ref ref="SYSLOG" /> </root> </configuration>
この設定ファイルを試してみるときは、リモートsyslogデーモンが外部からのリクエストを受け付けるようになっていることを先に確認しておいてください。経験上、デフォルトではsyslogデーモンはネットワークからのリクエストを拒否するようになっていることが多いです。
SiftingAppender
名前のとおり、SiftingAppender
は設定に基づいてロギングイベントを分配(あるいはふるいにかける)ことができます。例えば、SiftingAppender
がユーザーセッションに基づいてロギングイベントを分配するようになっていれば、ユーザーごとにログファイルを生成するようになるでしょう。
プロパティ名 | 型 | 説明 |
---|---|---|
timeout | Duration |
タイムアウト時間を経過してもアクセスされなかったアペンダーは、古くなったものと判断されます。古くなったアペンダーは閉じられて、SiftingAppender から切り離されます。デフォルトは30分です。 |
maxAppenderCount | integer |
SiftingAppender が作成して管理できるアペンダーの最大数を指定します。デフォルトはInteger.MAX_VALUEです。 |
SiftingAppender
は実行時にアペンダーを作成します。SiftingAppender
の設定で指定された設定テンプレート(sift要素
で囲まれた部分です。後で例を示します)に従って作成します。SiftingAppender
には、子アペンダーのライフサイクルを管理する責任があります。たとえば、 SiftingAppender
は古くなったアペンダー自動的に閉じて削除します。タイムアウト時間で指定された時間を過ぎてもアクセスの無かったアペンダーを無効とみなすのです。
ロギングイベントが発生したら、SiftingAppender
は委譲する子アペンダーを選択します。選択条件は弁別器によって実行時に計算されます。利用者はDiscriminator
で選択条件を指定することができます。例を見てみましょう。
例
SiftExampleアプリケーションは、アプリケーションが起動したことを表すメッセージをロギングします。その後、MDCのキー"uesrid"に"Alice"を設定して、メッセージをロギングします。該当するコードは次のとおりです。
logger.debug("Application started"); MDC.put("userid", "Alice"); logger.debug("Alice says hello");
SiftingAppender
で使う設定ファイルのテンプレートは次のようになっています。
例: SiftingAppender
の設定(logback-examples/src/main/java/chapters/appenders/sift/byUserid.xml)
<configuration> <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <!-- in the absence of the class attribute, it is assumed that the desired discriminator type is ch.qos.logback.classic.sift.MDCBasedDiscriminator --> <discriminator> <key>userid</key> <defaultValue>unknown</defaultValue> </discriminator> <sift> <appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender"> <file>${userid}.log</file> <append>false</append> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</pattern> </layout> </appender> </sift> </appender> <root level="DEBUG"> <appender-ref ref="SIFT" /> </root> </configuration>
discriminator 要素にclass属性がない場合、MDCBasedDiscriminatorが設定されます。弁別器は、keyプロパティで指定されたキーのMDC値を返します。MDC値がnullの場合は、defaultValueプロパティで指定した値を返します。
SiftingAppender
による子アペンダーの管理機能は独特なものです。上記の例では、SiftingAppender
によって複数のFileAppender
が作られます。それぞれのFileAppender
はキー"userid"でMDCに登録された値によって区別されます。キー"uesrid"のMDC値が新しい値のときは、新しいFileAppender
インスタンスをゼロから作ります。SiftingAppender
は自分が作ったアペンダーを追跡し続けます。30分間使われなかったアペンダーは自動的に閉じられ、破棄されます。
変数の公開出力先リソースが別々な複数のアペンダーのインスタンスを利用するにはどうしたらいいでしょうか?弁別器のキーが変数として公開されるので、上記の例の場合は、アペンダーの設定テンプレート内で"userid"を指定しています。こうすると、子アペンダーに別々の出力先を設定できるようになります。
SiftExample
アプリケーションの引数に設定ファイル"byUserid.xml"を指定して実行すると、"unknown.log" と"Alice.log"という2つのログファイルが作成されます。
ローカルスコープの変数logback 1.0.12 から、ネストされたアペンダーからローカルスコープの変数が利用できるようになりました。また、sift要素
内で変数を定義したり、変数の値を実行時に算出できるようになりました。sift要素
の外側で定義された変数の値を組み合わせることもできます。
適切なタイムアウトを設定する
アプリケーションによっては、適切なタイムアウト時間を決めるのが難しいことがあります。タイムアウト時間が短すぎると、ネストされたアペンダーが削除された直後に新しく生成されるようになってしまいます。これはゴミ漁りと呼ばれる事象です。タイムアウト時間が長すぎると、立て続けにアペンダーが生成されるとリソース不足になってしまうかもしれません。maxAppenderCountが小さすぎても同じようにゴミ漁りが発生するかもしれません。
どんな場合でも、ネストされたアペンダーが不要になる箇所を特定するのは簡単でしょう。そういう場所が特定できたら、あるいはほぼ確実にそうだと言える場所が特定できたら、そこに書かれているロギング式にFINALIZE_SESSIONマーカーを指定しましょう。SiftingAppenderはFINALIZE_SESSION
マーカーを指定されたロギングイベントを見つけたら、関連付けられたアペンダーはもう破棄していいいものと判断します。破棄されることになったアペンダーは、数秒間到着するであろうロギングイベントに備えて活性化した後、何も到着しなければそのままクローズします。
import org.slf4j.Logger; import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER; void job(String jobId) { MDC.put("jobId", jobId); logger.info("Starting job."); ... do whather the job needs to do // will cause the nested appender reach end-of-life. It will // linger for a few seconds. logger.info(FINALIZE_SESSION_MARKER, "About to end the job"); try { .. perform clean up } catch(Exception e); // This log statement will be handled by the lingering appender. // No new appender will be created. logger.error("unexpected error while cleaning up", e); } }
AsyncAppender
AsyncAppenderはILoggingEventを非同期的にロギングします。単にイベントディスパッチャとして機能するので、意味のある仕事をさせるには他のアペンダーを参照させなければなりません。
80%を越えると消えてしまう
AsyncAppenderはロギングイベントをBlockingQueueに蓄積します。AsyncAppender
のワーカースレッドは、キューの先頭からロギングイベントを取り出して、AsyncAppender
に割り当てられたアペンダーに振り分けます。キューの使用量が80%を超えている場合、デフォルトではTRACE、DEBUG、および、INFOレベルのログを捨ててしまいます。これは、ロギングイベントを失ってしまうリスクに見合うだけの性能影響があります。
アプリケーションの停止と再デプロイ
アプリケーションを停止、あるいは再デプロイ
する前に、 AsyncAppenderを停止しなければなりません。全てのロギングイベントをキューから吐き出すためです。そのためには、LoggerContextを停止すればよいです。AsyncAppender
を含む全てのアペンダーを閉じます。
AsyncAppender
の設定可能なプロパティは次のとおりです。
プロパティ名 | 型 | 説明 |
---|---|---|
queueSize | int |
キューの最大容量。デフォルトは256です。 |
discardingThreshold | int |
デフォルトは20%です。キューの使用量が閾値を越えたら、INFO以下のロギングイベントは破棄、WARN以上のロギングイベントだけをキューイングするようになります。0を指定するとロギングイベントを破棄しないようになります。 |
includeCallerData | boolean |
送信者情報の抽出自体に時間がかかることがあります。性能上の兼ね合いにより、デフォルトではロギングイベントをキューに追加するとき、関連する送信者情報を抽出しないようになっています。代わりに、スレッド名などが"軽い"情報だけをMDCにコピーします。trueを指定した場合、完全な送信者情報を含めるようになります。 |
デフォルトでは、イベントキューに登録可能な件数は256件になっています。キューが一杯になると、新しくロギングイベントを登録しようとしていたアプリケーションスレッドは、ワーカースレッドによってキューのロギングイベントが処理されるまで、ブロックします。キューの使用量が最大使用量を下回るまで、アプリケーションスレッドはロギング要求を発行できなくなります。したがって、ロギングイベントのバッファ使用量が最大値付近で推移しているとき、このアペンダーはほぼ同期的なロギングをすることになります。これは必ずしも悪いことではありません。このアペンダーは、ロギングイベントのバッファ使用量が閾値を超えるほどに増加するまでは、ロギング処理よりもアプリケーションの実行時間が長くなるように設計されています。
アプリケーションのスループットを最大化するためには、アペンダーのイベントキューの大きさを最適化する必要があり、それはいくつもの要因が絡んでいます。次に示す要因のいずれか、あるいは全てが、擬似的な同期的動作をさせる可能性があります。
- アプリケーションスレッドが多すぎる
- アプリケーションの呼び出しごとのロギング要求が多すぎる
- ロギングイベントごとに付随する情報が多すぎる
- 子アペンダーのレイテンシが遅すぎる
一般的には、アプリケーションの使用するヒープを減らして、ロギングイベントのキューを大きくすればよいでしょう。
非可逆な振る舞い 上記の議論を踏まえながらブロックする可能性を減らすため、AsyncAppenderはデフォルトでは利用可能なキューが最大値の20%未満になったらTRACE、DEBUG、INFOレベルのロギングイベントを破棄し、WARNとERRORレベルのロギングイベントだけを残すようになっています。そうすると、TRACE、DEBUG、INFOレベルのロギングイベントにコストをかけなくてもよくなるので、非同期処理を妨げず、高い性能を維持することができます。しきい値discardingThresholdを0にすればロギングイベントを破棄しないようにすることもできます。
例: AsyncAppender
の設定(logback-examples/src/main/java/chapters/appenders/conc/logback-async.xml)
<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>myapp.log</file> <encoder> <pattern>%logger{35} - %msg%n</pattern> </encoder> </appender> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="FILE" /> </appender> <root level="DEBUG"> <appender-ref ref="ASYNC" /> </root> </configuration>
アペンダーを自作する
AppenderBase
を使えば簡単にアペンダーを自作することができます。この基底クラスでは、フィルターやステータスメッセージの管理などほとんどのアペンダーが備えている機能を提供するものです。派生クラスでやることと言えば、append(Object eventObject)
メソッドを実装するだけです。
次に示すCountingConsoleAppender
は、限られた数のロギングイベントをコンソールに出力する自作アペンダーです。指定された数のロギングイベントを処理したらシャットダウンします。ロギングイベントを書式化するためにPatternLayoutEncoder
を使用します。そして、limit
というプロパティを設定することができます。また、append(Object eventObject)
メソッド以外にもいくつかメソッドを用意しなければなりません。例を見ればわかりますが、これらのパラメータは logback のさまざまな設定の仕組みによって自動的に設定されます。
CountingConsoleAppender
(logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java)
package chapters.appenders; import java.io.IOException; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; public class CountingConsoleAppender extends AppenderBase<ILoggingEvent> { static int DEFAULT_LIMIT = 10; int counter = 0; int limit = DEFAULT_LIMIT; PatternLayoutEncoder encoder; public void setLimit(int limit) { this.limit = limit; } public int getLimit() { return limit; } @Override public void start() { if (this.encoder == null) { addError("No encoder set for the appender named ["+ name +"]."); return; } try { encoder.init(System.out); } catch (IOException e) { } super.start(); } public void append(ILoggingEvent event) { if (counter >= limit) { return; } // output the events as formatted by our layout try { this.encoder.doEncode(event); } catch (IOException e) { } // prepare for next event counter++; } public PatternLayoutEncoder getEncoder() { return encoder; } public void setEncoder(PatternLayoutEncoder encoder) { this.encoder = encoder; } }
start()
メソッドでは、PatternLayoutEncoder
が設定されているかチェックしています。エンコーダーが設定されなかった場合、アペンダーの起動は失敗となり、エラーメッセージを出力します。
この自作アペンダーの見どころは次の2点です。
- JavaBeans の規約通りにアクセサを定義したプロパティは、logbackの他の設定と同じように処理されます。
start()
メソッドはlogbackの設定処理から自動的に呼び出されるようになっています。アペンダーのさまざまなプロパティの設定と、一貫性を整える役割があります。 AppenderBase.doAppend()
メソッドは派生クラスのappend()メソッドを呼び出します。実際の出力はappend()
が行います。レイアウトによってロギングイベントを書式化するのもこのメソッドです。
CountingConsoleAppender
は他のアペンダーと同じように定義することができます。設定ファイルの例としてlogback-examples/src/main/java/chapters/appenders/countingConsole.xmlも見てください。
Logback Access
logback-classic のほとんどのアペンダーと同じものが logback-access にもあります。基本的には同じように動きます。以降の節ではそれらの使い方を説明します。
SocketAppenderとSSLSocketAppender
SocketAppender
は、シリアライズしたAccessEvent
をネットワーク上のリモートホストに送信するために設計されています。リモートロギングは、アクセスイベントが重要であろうとなかろうと盗聴できないようになっています。受信したイベントをデシリアライズした後は、自分で生成したロギングイベントと同じように扱うことができます。
SSLSocketAppender
は基本的なSocketAppender
を拡張したもので、Secure Sockets Layer(SSL)を介してリモートホストにログを転送します。
logback-access のSocketAppender
で設定可能なプロパティは、logback-classic のSocketAppender
と同じです。
ServerSocketAppenderとSSLServerSocketAppender
SocketAppender
と同様に、ServerSocketAppender
はシリアライズしたAccessEvent
をネットワーク上のリモートホストに送信するために設計されています。ServerSocketAppender
はサーバとして動作します。つまり、外部のクライアントがTCP接続してくるのを待ち受けます。アペンダーに渡されたロギングイベントは、接続している全てのクライアントに配布されます。
SSLSocketAppender
は基本的なServerSocketAppender
を拡張したもので、Secure Sockets Layer(SSL)を介してリモートホストにログを転送します。
logback-access のServerSocketAppender
で設定可能なプロパティは、logback-classic のServerSocketAppender
と同じです。
SMTPAppender
logback-access のSMTPAppender
は、logback-classic と同じように動作します。tだ、評価器の設定はだいぶ違います。デフォルトでは、 URLEvaluator
オブジェクトが使用されます。この評価器はロギング要求のURLと突き合わせるURLのリストを持っています。いずれかのURLにマッチするロギング要求が発生したら、SMTPAppender
はメールを送信します。
logback-access のSMTPAppender
の設定例を見てみましょう。
例: SMTPAppender
の設定(logback-examples/src/main/java/chapters/appenders/conf/access/logback-smtp.xml)
<appender name="SMTP" class="ch.qos.logback.access.net.SMTPAppender"> <layout class="ch.qos.logback.access.html.HTMLLayout"> <pattern>%h%l%u%t%r%s%b</pattern> </layout> <Evaluator class="ch.qos.logback.access.net.URLEvaluator"> <URL>url1.jsp</URL> <URL>directory/url2.html</URL> </Evaluator> <from>sender_email@host.com</from> <smtpHost>mail.domain.com</smtpHost> <to>recipient_email@host.com</to> </appender>
このやり方では、特別な処理プロセスの中で重要な手順を通ったときにメールを送信するようなことができます。メールには、トリガとなったWebページにアクセスする一つ前のWebページが含まれます。他の情報を含めることもできます。
DBAppender
DBAppender
はアクセスイベントをデータベースに登録するために使用します。
DBAppender
はaccess_eventテーブルとaccess_event_headerテーブルを使います。いずれもDBAppender
を使用する前に準備しなければなりません。テーブルを作成するSQLスクリプトはlogback の配布物に含まれています。logback-access/src/main/java/ch/qos/logback/access/db/scriptディレクトリにあるはずです。一般的なデータベースそれぞれの専用スクリプトがあります。あなたの使用しているデータベース用のスクリプトが無かったとしても、他のスクリプトを参考にすれば簡単に作成できます。あなたの使っているデータベース用のスクリプトが無かった時は、作成したスクリプトをlogbackプロジェクトに送ってください。
access_eventテーブルのカラムは次のとおりです。
カラム名 | 型 | 説明 |
---|---|---|
timestamp | big int |
アクセスイベントの作成時のタイムスタンプ。 |
requestURI | varchar |
要求されたURI。 |
requestURL | varchar |
要求されたURL。これはリクエストメソッド、リクエストURI、リクエストプロトコルを組み合わせた文字列です。 |
remoteHost | varchar |
リモートホストの名前。 |
remoteUser | varchar |
リモートユーザの名前。 |
remoteAddr | varchar |
リモートIPアドレス。 |
protocol | varchar |
HTTPまたはHTTPSなどのリクエストプロトコル、。 |
method | varchar |
リクエストメソッド、GETかPOSTになるでしょう。 |
serverName | varchar |
リクエストを受け付けたサーバーの名前。 |
event_id | int |
アクセスイベントのデータベース上のID。 |
access_event_headerテーブルには、リクエストのヘッダ情報が登録されます。次のようなものです。
カラム名 | 型 | 説明 |
---|---|---|
event_id | int |
アクセスイベントのデータベース上のID。 |
header_key | varchar |
User-Agentなどのリクエストヘッダー名。 |
header_value | varchar |
Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0のようなリクエストヘッダー値。 |
logback-access のDBAppender
に設定可能なプロパティは、logback-classic のDBAppender
でも利用できます。logback-access の DBAppender でだけ設定可能なプロパティは次のとおりです。
プロパティ名 | 型 | 説明 |
---|---|---|
insertHeaders |
boolean
|
trueの場合、ロギング要求に含まれる全てのヘッダ情報を登録するようになります。 |
DBAppender
の設定例を見てください。
例:DBAppenderの設定(logback-examples/src/main/java/chapters/appenders/conf/access/logback-DB.xml)
<configuration> <appender name="DB" class="ch.qos.logback.access.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://localhost:3306/logbackdb</url> <user>logback</user> <password>logback</password> </connectionSource> <insertHeaders>true</insertHeaders> </appender> <appender-ref ref="DB" /> </configuration>
SiftingAppender
logback-access のSiftingAppenderは、logback-classic のSiftingAppenderとほとんど同じです。主な違いは、デフォルトの弁別器がMDCを参照するものではなく、AccessEventDiscriminatorになっていることです。AccessEventDiscriminatorは、その名のとおり、ネストされたアペンダーを選択するために、AccessEventのフィールドを使用します。指定されたフィールドの値がnullの場合DefaultValueプロパティの値が使用されます。
AccessEventのフィールドとして指定できるのはCOOKIE、REQUEST_ATTRIBUTE、SESSION_ATTRIBUTE、REMOTE_ADDRESS、LOCAL_PORT、REQUEST_URIです。最初の3つのフィールドは単独で指定することはできません。AdditionalKey要素が必要なので注意してください。
設定ファイルの例を示します。
例:SiftingAppenderの設定(logback-examples/src/main/java/chapters/appenders/conf/sift/access-siftingFile.xml)
<configuration> <appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender"> <Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator"> <Key>id</Key> <FieldName>SESSION_ATTRIBUTE</FieldName> <AdditionalKey>username</AdditionalKey> <defaultValue>NA</defaultValue> </Discriminator> <sift> <appender name="access-$id-$username" class="ch.qos.logback.core.FileAppender"> <file>byUser/access-$id-$username.log</file> <layout class="ch.qos.logback.access.PatternLayout"> <pattern>%h %l %u %t \"%r\" %s %b</pattern> </layout> </appender> </sift> </appender> <appender-ref ref="SIFTING" /> </configuration>
この例ではSiftingAppender
がFileAppender
をネストしています。キー要素の値"id"は、ネストされたFileAppender
で変数として使用することができます。デフォルトの弁別器でもあるAccessEventDiscriminator
は AdditionalKey 要素で指定した"username" をAccessEvent
のセッション属性から探します。指定した属性が無かったら、defaultValue要素の値"NA"を使用します。セッション属性の"username"には、アプリケーションのログインユーザー名が含まれていることを想定しています。ユーザーごとのアクセスログは、byUserフォルダの下にユーザー名入りのファイル名で出力されます。