第14章 レシーバー

岸が見えなくなることを恐れない勇気が無ければ、地平線の向こうまで泳ぎ着くことはできない。

—WILLIAM FAULKNER

レシーバーとは何か?

レシーバーとは、リモートアペンダーからロギングイベントを受信し、ローカルポリシーに基づいてログに記録するlogbackのコンポーネントです。ソケットベースのアペンダーとレシーバーを組み合わせることで、分散アプリケーションのロギングイベントをネットワーク越しに配信する高度なトポロジーを実現することができます。

レシーバーはch.qos.logback.classic.net.ReceiverBaseクラスを継承しています。おかげでLifeCycleに参加できるし、レシーバー自体がContextAwareになります。

歴史的な経緯により、logback によるネットワーク越しのロギングイベントの配信はSocketAppenderSimpleSocketServerによって実現されてきました。アペンダーはクライアントとして動作します。サーバーアプリケーションへのネットワーク接続を確立し、ロギングイベントを配信するのです。レシーバーコンポーネントと対応するアペンダーを使うことで、より優れた柔軟性を実現することができます。

レシーバーコンポーネントは他のlogbackのコンポーネントと同様にlogback.xmlで設定します。つまり、Joranの全ての機能を利用することができるのです。さらに、複数のレシーバーコンポーネントを設定するだけで、あらゆるアプリケーションのリモートアペンダーからロギングイベントを受け付けることができます。

アペンダーとレシーバー間のネットワーク接続の確立はどちら側からでも始めることができます。レシーバーはサーバーとして振る舞うことができます。つまり、クライアントたるリモートアペンダーからの接続を待ち受けることができます。レシーバーはクライアントとして振る舞うこともできます。サーバーとして振る舞うリモートアペンダーに対してネットワーク接続を確立することができるのです。アペンダーとレシーバーのそれぞれの役割とは無関係に、ロギングイベントは常にアペンダーからレシーバーに送信されます

レシーバーからアペンダーに接続できることは、特定の状況においてとても有用です。

logbackの設定には、サーバーあるいはクライアントのいずれかの役割を担うレシーバーコンポーネントを混在させることができます。数少ない制限として、サーバーとして振る舞うレシーバーコンポーネントはそれぞれ固有のポート番号で接続を待ち受けなければならないということと、クライアントとして振る舞うレシーバーコンポーネントはそれぞれただ1つのリモートアペンダーにしか接続してはならないということがあります。

サーバーとして振る舞うレシーバー

サーバーとして振る舞うレシーバーはリモートアペンダーからの接続を待ち受けます。これはスタンドアローンのSimpleSocketServerアプリケーションと同じ機能です。ただし、レシーバーコンポーネントなら、logback.xmlに設定するだけで、logback-classicモジュールを使っているあらゆる{\1}アプリケーションからロギングイベントを受信することができます。

logbackの配布物には二つのレシーバーコンポーネントが含まれています。ServerSocketReceiverとそのSSL対応のSSLServerSocketReceiverです。どちらもSocketAppender(あるいはSSLSocketAppender)からの接続を待ち受けるように設計されています。

ServerSocketReceiverの設定可能なプロパティは次のとおりです。

プロパティ名 説明
address String レシーバーが待ち受けるローカルネットワークインターフェイスのネットワークアドレス。このプロパティが指定されない場合、レシーバーは全てのネットワークインターフェイスで待ち受けるようになります。
port int レシーバーが待ち受けるTCPのポート番号。このプロパティが指定されない場合、デフォルト値が使用されます。
ssl SSLConfiguration SSLServerSocketReceiverでのみサポートされているプロパティです。このプロパティにはSSLを使用するで説明されているSSLの設定を指定します。

ServerSocketReceiverの使い方

次の設定は、最小限のアペンダーとロガーの設定とともにServerSocketReceiverを使用するものです。リモートアペンダーから受信したロギングイベントは、ルートロガーに渡されて、ローカルのコンソールアペンダーに配信されます。

例:基本的なServerSocketReceiverの設定(logback-examples/src/main/java/chapters/receivers/socket/receiver1.xml

Groovyとして表示
<configuration debug="true">

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>

  <receiver class="ch.qos.logback.classic.net.server.ServerSocketReceiver">
    <port>${port}</port>
  </receiver>

</configuration>

レシーバーコンポーネントのclass属性に指定した値によって、レシーバーのサブタイプを指定します。ここではServerSocketReceiverを指定しています。

例として、SimpleSocketServerと非常によく似た機能を持ったサーバーアプリケーションを用意しました。logbackの設定ファイルへのパスをコマンドライン引数から受け取って、指定された設定ファイルを読み込みます。この例のアプリケーションは大した仕事をするわけではありませんが、ServerSocketReceiver (あるいはSSLServerSocketReceiver)はどんなアプリケーションからでも利用できることを覚えておきましょう。

コマンドをプロンプトでlogback-examplesディレクトリに移動して、次のコマンドを実行しましょう。

java -Dport=6000 chapters.receivers.socket.ReceiverExample \ src/main/java/chapters/receivers/socket/receiver1.xml

クライアントアプリケーションでSocketAppenderを設定して、実行中のレシーバーに接続することができます。サンプルのクライアントアプリケーションは、上記のレシーバーに接続する設定ファイルを読み込むだけのものです。クライアントアプリケーションはユーザー入力を待ちます。ユーザー入力はそのままレシーバーに中継されます。クライアントアプリケーションは次のように実行します。

java -Dhost=localhost -Dport=6000 \ chapters.receivers.socket.AppenderExample \ src/main/java/chapters/receivers/socket/appender1.xml

SSLServerSocketReceiverの使い方

次の設定は、前の設定とは違ってSSLに対応したレシーバーを使うものです。

例:基本的なSSLServerSocketReceiverの設定(logback-examples/src/main/java/chapters/receivers/socket/receiver2.xml

Groovyとして表示
<configuration debug="true">

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>

  <receiver class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver">
    <port>${port}</port>
    <ssl>
      <keyStore>
        <location>${keystore}</location>
        <password>${password}</password>
      </keyStore>
    </ssl>
  </receiver>

</configuration>

前のServerSocketReceiverを使った設定とこちらの設定で根本的に違うのは、レシーバーのclass属性SSLServerSocketReceiverが指定されていることと、sslプロパティがネストされていることです。sslプロパティにはレシーバーの秘密鍵と証明書が配置されたキーストアの場所とパスワードを、変数で指定しています。sslプロパティの設定内容について詳しくはSSLを使用するを参照してください。

いくつか引数を追加して次のようにサーバーアプリケーションを実行します。

java -Dport=6001 \ -Dkeystore=file:src/main/java/chapters/appenders/socket/ssl/keystore.jks \ -Dpassword=changeit \ chapters.receivers.socket.ReceiverExample \ src/main/java/chapters/receivers/socket/receiver2.xml

コマンドラインで指定したプロパティkeystoreには、キーストアのfileから始まるURLを指定します。SSLを使用するでも説明していますが、クラスパス上のリソースを指定することもできます。

このレシーバーには、クライアントアプリケーションからSSLSocketAppenderで接続することができます。SSL対応のアペンダーを使う設定ファイルを、前の例で使用したクライアントアプリケーションから使うことができます。次のように実行します。

java -Dhost=localhost -Dport=6001 \ -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \ -Dpassword=changeit \ chapters.receivers.socket.AppenderExample \ src/main/java/chapters/receivers/socket/appender2.xml

この例では自己署名したX.509証明書を使用していますが、これを使っていいのはテストや実験的な用途だけです。本番環境では、SSL対応のアペンダーのために適切なX.509証明書を使うようにしてください 。詳細はSSLを使用するを参照してください。

クライアントとして振る舞うレシーバー

クライアントとして振る舞うように設定したレシーバーは、リモートアペンダーに接続します。リモートアペンダーはServerSocketAppenderなどのサーバ型でなければなりません。

logackの配布物にはクライアントとして振る舞うレシーバーコンポーネントが2つ含まれています。SocketReceiverとそのSSL対応版のSSLSocketReceiverです。どちらのレシーバーもリモートアペンダー(ServerSocketAppenderあるいはSSLServerSocketAppender)に対して接続を確立するようになっています。

SocketReceiverの派生クラスで設定可能なプロパティは次のとおりです。

プロパティ名 説明
remoteHost String リモートアペンダーのホスト名またはIPアドレス。
port int リモートアペンダーの待ち受けポート番号。
reconnectionDelay int 接続異常が発生した後で、再接続をする前に待機する時間を表す正の整数値。単位はミリ秒。デフォルト値は30000(30秒)です。
ssl SSLConfiguration SSLSocketReceiverでのみ設定可能なプロパティ。SSLを使用するで説明しているとおり、SSLの設定を指定します。

SocketReceiverの使い方

SocketReceiverの設定はServerSocketReceiverの設定と非常によく似ています。これらの差は、サーバーとクライアントという真逆の役割に起因するものです。SocketReceiverはクライアントで、リモートアペンダはサーバーとして動作します。

例:基本的なSocketReceiverの設定(logback-examples/src/main/java/chapters/receivers/socket/receiver3.xml

Groovyとして表示
<configuration debug="true">
    
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">    
    <encoder>
      <pattern>%date %-5level [%thread] %logger - %message%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>  

  <receiver class="ch.qos.logback.classic.net.SocketReceiver">
    <remoteHost>${host}</remoteHost>
    <port>${port}</port>
    <reconnectionDelay>10000</reconnectionDelay>
  </receiver>

</configuration>

この設定だと、logbackはhost変数で指定されたホストの、port変数で指定されたポート番号で実行中のServerSocketAppenderへ接続します。リモートアペンダーから受け取ったロギングイベントは、手元でコンソールアペンダーに渡されます。

コマンドプロンプトでlogback-examplesディレクトリに移動して、次のコマンドを実行してみましょう。

サンプルアプリケーションは上記の設定ファイルを読み込んだあと、リモートアペンダーからのロギングイベントを待ち受けます。リモートアペンダーが落ちているときは、定期的に接続を拒否されたメッセージが出力されます。再接続が成功するか、アプリケーションが停止するまで、レシーバーは定期的にリモートアペンダーへの再接続を繰り返します。設定例にあるとおり、再接続の間隔はreconnectionDelayプロパティで指定することができます。

java -Dhost=localhost -Dport=6000 \ chapters.receivers.socket.ReceiverExample \ src/main/java/chapters/receivers/socket/receiver3.xml

この例のレシーバーは、前のアプリケーションのアペンダーにそのまま接続できます。アペンダー用のサンプルアプリケーションは、ServerSocketAppenderを使った設定ファイルを読み込んでから、ユーザー入力を待ちます。ユーザー入力はアペンダーに接続してきたレシーバーに配信されます。アペンダー用のサンプルアプリケーションを実行してみましょう。

java -Dport=6000 \ chapters.receivers.socket.AppenderExample \ src/main/java/chapters/receivers/socket/appender3.xml

レシーバーが接続する前に入力したメッセージは破棄されます。わかりやすいですね。

SocketSSLReceiverの使い方

SSLSocketReceiverの設定はSocketReceiverの設定とほとんど変わりません。根本的に違うのは、レシーバーのclass属性の指定と、sslプロパティがネストされていることです。基本的な設定を見てみましょう。

例:基本的なSSLSocketReceiverの設定(logback-examples/src/main/java/chapters/receivers/socket/receiver4.xml

Groovyとして表示
<configuration debug="true">

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">    
    <encoder>
      <pattern>%date %-5level [%thread] %logger - %message%n</pattern>
    </encoder>         
  </appender>

  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>  
 
  <receiver class="ch.qos.logback.classic.net.SSLSocketReceiver">
    <remoteHost>${host}</remoteHost>
    <port>${port}</port>
    <reconnectionDelay>10000</reconnectionDelay>
    <ssl>
      <trustStore>
        <location>${truststore}</location>
        <password>${password}</password>
      </trustStore>
    </ssl>
  </receiver>

</configuration>

class属性にSSLSocketReceiverを指定していること、そして、リモートアペンダーが信頼できるかどうかを検証するために使用するトラストストアの場所とパスワードを指定しているところが重要なところです。sslプロパティの設定内容について詳しくはSSLを使用するを参照してください。

この設定をつかってレシーバーのサンプルアプリケーションを実行しましょう。

java -Dhost=localhost -Dport=6001 \ -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \ -Dpassword=changeit \ chapters.receivers.socket.ReceiverExample \ src/main/java/chapters/receivers/socket/receiver4.xml

アプリケーションが開始すると、レシーバーは設定ファイルで指定されたリモートアペンダーに接続しようとします。まだアペンダーが実行されていないときは、定期的に"接続を拒否されました"というメッセージがログに出力されrます。レシーバーはreconnectionDelayプロパティで指定した時間間隔で、アペンダーに接続できるまで、再接続を繰り返します。

レシーバーが接続するアペンダーのためのサンプルアプリケーションを実行しましょう。アプリケーションが開始すると、アプリケーションはユーザー入力を待ち受けます。SSLServerSocketAppenderはレシーバーからの接続を待ち受けつつ、接続されているレシーバーにユーザー入力をメッセージとして発生したロギングイベントを配信します。アペンダーのサンプルアプリケーションを実行しましょう。

java -Dport=6001 \ -Dkeystore=file:src/main/java/chapters/appenders/socket/ssl/keystore.jks \ -Dpassword=changeit \ chapters.receivers.socket.AppenderExample \ src/main/java/chapters/receivers/socket/appender4.xml

レシーバーが接続していない状態で何か入力しても、そのメッセージは単純に破棄されるだけです。

繰り返しになりますが、この例では自己署名したX.509証明書を使用していますが、これはあくまでもテストだからです。本番環境においては、SSL対応のlogbackコンポーネントが自身の身元を証明するため、適切なX.509証明書を取得しなければなりません。詳しくはSSLを使用するを参照してください。