# 第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のように書けばよいでしょう。デフォルト値はありません。 指定したファイルの親ディレクトリが存在しない場合、 `FileAppender`は途中の全てのディレクトリを作成します。

prudent

`boolean`

prudentモードでは、指定されたファイルに安全に書き込むようになります。同じファイルを対象にした他の`FileAppender`がいるとしても、それが別のJVMで動いているとしても、ましてや別のホストで動いているとしても、です。デフォルト値は`false`です。

prudent モードは、多少の制限はあるものの、`RollingFileAppender`と組み合わせて利用することもできます。

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`はサポートされていません。

`TimeBasedRollingPolicy`を使えば`RollingFileAppender`でもprudentモードを利用できますが、二つの制限があります。

1. purudentモードでは、ファイル圧縮オプションをサポートしていませんし、利用することもできません。（他のJVMがログファイルを圧縮している間、書き込むことはできません）
2. `FileAppender`のfileプロパティを指定することはできません。空のままにしておかなければなりません。ほとんどのOSでは、他のプロセスが開いているファイルの名前を変えることはできません。

fileプロパティについては`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に指定された値から推測されます。

`TimeBasedRollingPolicyの親アペンダーである`RollingFileAppender`のfileプロパティは、指定することもできるし省略することもできます。`fileプロパティにファイル名を指定した場合、ログを出力するファイルとアーカイブファイルの場所を別々にすることができます。ログは常にfileプロパティで指定されたファイルに出力されます。 そうすると、有効なログファイルを常に同じ名前にしておくことができます。fileプロパティを省略した場合、有効なログファイル名は、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）

Groovyとして表示

    <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）

Groovyとして表示

    <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` | このオプションには`FixedWindowRollingPolicy`がログファイルをアーカイブするファイル名のパターン文字列を指定します。パターン文字列には%iを含めなければなりません。これは、現在の幅に追加する位置の添字になります。 例えば、パターン文字列が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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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 &lt;smith@moral.org&gt;" のようにします（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` | `SMTPAppender`は、弁別器の返す値に基づいてバッファを選択して、発生したロギングイベントを振り分けます。デフォルトの弁別器は常に同じ値を返すので、全てのロギングイベントが常に同じバッファに振り分けられます。 デフォルト以外の弁別器を指定すれば、特定のユーザー、ユーザーセッション、クライアントのIPアドレスに関連するロギングイベントを含むメールだけを送信することもできます。
evaluator | `IEvaluator` | このオプションを指定するには、新しく`EventEvaluator要素`を宣言します。`SMTPAppender`の`Evaluator`として使いたいクラスの完全クラス名を、class属性に指定します。 このオプションを指定しなかった場合、`SMTPAppender`はOnErrorEvaluatorのインスタンスを使用します。これはERROR以上のロギングイベントが発生したらメール送信をトリガします。 logback の配布物には他の評価器もいくつか含まれています。`OnMarkerEvaluator`（詳しくは後述します）と、より強力な評価器の`JaninoEventEvaluator`です。後者については別の章で紹介します。最新バージョンの配布物にはさらに強力な評価器である`GEventEvaluator`が含まれます。
cyclicBufferTracker | `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から`Session`を取得すれば、設定する場所は減るし、同じ情報をあちこちで指定しなくてもよくなります。そうすると、アプリケーションはよりDRYになります。TomcatでJNDIリソースを設定する方法についてはJNDIリソースハウツーを参照してください。JNDIから`Session`オブジェクトを取得する場合は、ドキュメントに記載されているように、mail.jarとactivation.jarをWebアプリケーションのWEB-INF/libフォルダから取り除いてください。

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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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` | このフィールドは、ロギングイベントに例外オブジェクトが含まれているか、もしくは、`MDC`に関連する値が設定されていないかどうかを判定するため、logback が使用します。 値は`ch.qos.logback.classic.db.DBHelper`が算出します。ロギングイベントに`MDC`あるいは`Context`プロパティが含まれるバア愛、このフラグ値は1になります。例外オブジェクトが含まれる場合は2になります。両方の要素が含まれている場合は3になります。
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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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"です。`PatternLayout`で使用できるものは、全てsuffixPatternに指定することができます。
stackTracePattern | `String` | stackTracePatternプロパティには、スタックトレースの各行の先頭に表示する文字列を指定します。デフォルトはタブ文字、つまり "\t"です。`PatternLayout`で使用できるものは、全てstackTracePatternに指定することができます。
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）

Groovyとして表示

    <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）

Groovyとして表示

    <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）

Groovyとして表示

    <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 のさまざまな設定の仕組みによって自動的に設定されます。

例4: `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 と同じように動作します。ｔだ、評価器の設定はだいぶ違います。デフォルトでは、 `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フォルダの下にユーザー名入りのファイル名で出力されます。