Log4J徹底解説

1.3で追加されたAppender

目次

1.3 で登場した新しい Appenderたち

log4j-1.3-alpha-8 に従って大幅改稿!(2006.3/28)

まだαの 1.3 だが、Appender に関しても若干の異同がある。どうやら LF5Appender は無くなったようだし、RollingFileAppender は org.apache.log4j.rolling.RollingFileAppender に位置変えして、仕様が大々的に変更されて柔軟になっている(互換性のため元の場所にもある...わりとダサ目の仕様だったしね)。あと特に解説しなかった NullAppender(何もしない Appender なんで...)がなぜか2つ(org.apache.log4j.performance.NullAppender と org.apache.log4j.varia.NullAppender)もあったが、org.apache.log4j.performance.NullAppender の方はなくなったようである(サンプル用だったしね)。結局、新しく次の8つの Appender が増えていることになるが、DBAppender と 新しい RollingFileAppender はそれぞれのところで解説しているので、ここでは触れない。

UDPAppender

これは UDP でログを送信する Appender である。動作自体はクライアントとしてサーバに接続し、レイアウトによってフォーマットされた文字列のログを送信する。まあ、これが無いのは「落ち!」に近いものだ(現実的にはどれほど有用かは疑問だが...要するに SyslogAppender はこれの特殊ケースのようなものだ)。

当然オプションとしては、

RemoteHost
接続先ホスト名。
Port
接続するポート。デフォルトは 9991 である。
Application
このオプションはネットワーク系Appender 一般に追加されるようである。要するに複数のアプリからログを受け付ける場合にそれらを区別する「アプリケーション名」とを示すログ要素に当たる。詳細はネットワーク系Appender とプロパティ(1.3)を参照されたい。
OverrideProperties
デフォルトは true。アプリケーションとホスト名のプロパティの上書きを許可する。詳細はネットワーク系Appender とプロパティ(1.3)を参照されたい。
Encoding
出力文字列のエンコーディングを指定する。デフォルトは無しで、そのまま。
Threshold
(AppenderSkeltonから継承)

がある。これを Chainsaw でテストする、というのをやるので期待してくれ。

MulticastAppender

さて、これはちょっと解説が要るな。マルチキャストソケットというのを皆さんご存知か? 「それって 127.255.255.255 みたいなもん?」というと、それは「ブロードキャストアドレス」で関係がない。ちなみに「クラスC」とか「クラスA」のIPアドレスというのがあるが、「クラスD(224.0.0.0〜239.255.255.255)」というのがあるんである。これと「マルチキャストソケット」は深い関係がある。

例えば「電子会議」とか「インターネットラジオ」みたいなことをする時に、1つの発信元から複数の受信先に対して、データを送信する。これに使うべく用意されたのがこの「マルチキャストソケット」であり、この特殊なパケットをうまく取り扱うことのできるルータによって、不要なパケットのコピーとか静的な配送ツリーとかを考えなくても、デフォルトのルーティングの上に乗っかって、効率的にブロードキャストが可能になる、というスグレ物だ。

とはいえ、インターネット基幹技術は保守的なので、まだそうそう普及しているとは言えないが、「MBONE」とかいう言葉で呼ばれるものの技術的インフラがこの「マルチキャストソケット」なのである。

で、Log4J でもこのマルチキャストソケットを使って「ログを配信しよう」というなかなか無茶なことをするのがこの MulticastAppender だ。オプションはこんなところ。

RemoteHost
接続先ホスト名。
Port
接続するポート。デフォルトは 9991 である。
Application
このオプションはネットワーク系Appender 一般に追加されるようである。要するに複数のアプリからログを受け付ける場合にそれらを区別する「アプリケーション名」とを示すログ要素に当たる。詳細はネットワーク系Appender とプロパティ(1.3)を参照されたい。
OverrideProperties
デフォルトは true。アプリケーションとホスト名のプロパティの上書きを許可する。詳細はネットワーク系Appender とプロパティ(1.3)を参照されたい。
Encoding
出力文字列のエンコーディングを指定する。デフォルトは無しで、そのまま。
TimeToLive
マルチキャストの場合、ルータで転送されると「TimeToLive」の値が一つ減ることになる。ここらへんは通常のパケットと同様だが、マルチキャストの場合はこの機能を積極的に利用し、「マルチキャスト・パケットが届く範囲」を制限することができる。だから、自分のホスト内部だけ、というようなテストの場合は 0 を指定するのが良かろうし、LAN 内部だったら 1 を指定するのが良かろう。デフォルトは値がないので 0 になって外には出ていかない。
Threshold
(AppenderSkeltonから継承)

ListAppender

ListModelAppender

さて、この2つの Appender は初登場のカテゴリーに入る Appender である。JDK1.4 の java.util.logging パッケージなどは MemoryHandler という名前でこういうのを実装しているが、要するにメモリ上にログを出力するというだけのものである。

だから、本質的にはコードサポートをしないと意味のないものである。単に設定ファイルにこれを指定しただけでは、何の出力も生まれるわけがないな。

なのでオプションは単にレイアウトだけである。これを使って、今まで出したログを、アプリの最後に標準出力にもう一度出力するのならば、次のようなコードでやればいい。

(jp.or.nurs.sug.log4j.test.TestList)
package jp.or.nurs.sug.log4j.test;

import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.apache.log4j.varia.ListAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;

import java.util.List;
import java.util.Iterator;

import jp.or.nurs.sug.log4j.test.child.TestChild;

public class TestList {
     static protected Logger log = Logger.getLogger( TestList.class ); 

     public static void main( String [] args ) {
         // ルートロガーを取得し、ListAppender を作って追加する
         Logger root = Logger.getRootLogger();
         ListAppender list = new ListAppender();
         root.addAppender( list );
         
         // 適当にログを出させる
         log.info( "ENTER: TestLog4j" );
         TestChild tc  = new TestChild();
         tc.doSomething();
         log.info( "EXIT: TestLog4j" );

         // 表示用にパターンレイアウトを作っておく
         Layout layout = new PatternLayout( "%d %5p %c{1} - %m%n" );

         // ListAppender からリスト内容を取得する
         List l = list.getList();
         Iterator i = l.iterator();
         while( i.hasNext() ) {
             // List に入っているのは LoggingEvent 
             LoggingEvent event = (LoggingEvent)i.next();
             System.out.print( "From List: " + layout.format(event) );
         }
     }
}

これで List に貯まった LoggingEvent を再度取得できる。で、これが ListAppender だが、ListModelAppender はどう違うか、というと、ListModelAppender が管理し返すのは、javax.swing.ListModel である。言い替えると Swing 用のモデル・オブジェクトを返すわけだ。

だったらこれは要するにGUIの「ログモニター」を作るためにあるようなものだ。まあ、こんなプログラムはいかが?

(jp.or.nurs.sug.log4j.test.TestListModel)
package jp.or.nurs.sug.log4j.test;

import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.apache.log4j.NDC;

import org.apache.log4j.varia.ListModelAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;

import javax.swing.*;
import java.awt.Container;
import java.awt.Dimension;

import jp.or.nurs.sug.log4j.test.child.TestChild;

public class TestListModel {
    static protected Logger log = Logger.getLogger( TestListModel.class ); 
    ListModel model;

    public static void main( String [] args ) {
	new TestList();
    }

    TestList() {
	Logger root = Logger.getRootLogger();
	ListModelAppender list = new ListModelAppender();
	list.setLayout( new PatternLayout( "%d %5p %c{1} - %m%n" ) );
	model = list.getModel();
	root.addAppender( list );
	
	ListGUI gui = new ListGUI();
	gui.start();
	 
	log.info( "ENTER: TestLog4j" );
	TestChild tc  = new TestChild();
	tc.doSomething();
	log.info( "EXIT: TestLog4j" );
    }

    class ListGUI extends Thread {
	public void run() {
	    JFrame top = new JFrame();
	    top.setTitle( "Log watcher" );
	    top.setSize( new Dimension( 400,300 ) );
	    Container pane = top.getContentPane();
	    JPanel panel = new JPanel();
	    pane.add( panel );
	    JScrollPane sp = new JScrollPane();
	    panel.add( sp );
	    JList list = new JList( model );
	    sp.getViewport().add( list );
	    top.setVisible( true );
	}
    }
}

が...大変残念なことに、表示はこんな感じだ。

これは要するに、ListModel が LoggingEvent のレンダリングの仕方を知らない...ということである。何とかするには、ListModelAppender の上書きが必要だ...が、ちょいとお馬鹿なことに、このクラスは final クラスである。ちょいとうまくやるやり方を思い付かない。まだまだだなあ!

で、これを「自前でAppenderを書いちゃう!」というアプローチで解消するのが、「自前 Appender の書き方!」 である。面白いぞ!

SoundAppender

これはお笑い的価値(Hack Value とも言う)のためにあるような Appender だ。要するに「音」でログが発生したことを知らせてくれる! とはいえ、実装は汎用的なもので、要するに Applet パッケージの AudioClip クラスを使って音出しをするものである。だから、オプションはコレだけだ。

AudioURL
ログが発生した時に出す音声のファイルをURLで指定する。まあ、「http://〜」で指定するのが普通だろう。Applet が再生可能な音声ファイルなら何でも結構である。
Threshold
(AppenderSkeltonから継承)

MultiplexAppender

これは実はまだホントに「Under Construct!」なもののようだ。実際まだファイルがあるだけである。が、そんな状態でも見てると何をしようとしているのか...くらいは判る。要するに、一つの MultiplexAppender の中に入れ子にして、複数の Appender を指定できるようにする。そうして、「何かの条件」に従って、その中から実際に起動される Appender を選択する、というかたちのものである。で、やはり「何かの条件」は「Strategy」になっており、org.apache.log4j.multiplex.MultiplexSelecter interface を implements したクラスで指定する。現状では org.apache.log4j.multiplex.MDCKeySelecter がファイルだけ存在し(実装は全然...)、これは MDC のキーを指定し、その値で実際に起動する Appender を選択するもののようだ。

推測するに、こんな感じになるのだろう。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">
  <appender name="A" class="org.apache.log4j.AAAAAppender">
      <!-- 略 -->
  </appender>

  <appender name="B" class="org.apache.log4j.BBBBAppender">
      <!-- 略 -->
  </appender>

  <appender name="C" class="org.apache.log4j.CCCCAppender">
      <!-- 略 -->
  </appender>

  <appender name="multi" class="org.apache.log4j.multiplex.MultiplexAppender">
    <selecter class="org.apache.log4j.multiplex.MDCKeySelecter">
       <param name="MDCKey" value="SomeKeyInMDC" />
    </selecter>
    <appender-ref ref="A"/>
    <appender-ref ref="B"/>
    <appender-ref ref="C"/>
  </appender>

  <logger name="jp.or.nurs.sug.log4j.test">
    <level value ="debug" />
    <appender-ref ref="multi"/>
  </logger>
</log4j:configuration>

で、MDC にセットされたキーの値によって、A,B,C のどれかの Appender が起動される、というような格好になりそうだ。使い道はなんだろうな...

SandBox にある Appender たち(参考)

さて、これはお楽しみだ。気楽に読んでくれたまえ。

まあ、こういうオープンソース・プロジェクトの開発だと、いわゆる「SandBox(砂場)」というのがあるのを御承知の読者も多かろう。要するに、「開発途中で実際にはまだアイデアに過ぎないような実装」を、本来の開発リポジトリの外でいろいろ実験してみる、というものである。当然 Log4J にもこういう SandBox がある。そこを覗いて「今後追加されるか?と思われる Appender」を調査してみる、というお楽しみだ。

実際、URLだと「http://svn.apache.org/viewcvs.cgi/logging/sandbox/log4j/ Log4J SandBox」で、見ることができる。で、どんなのがあるか...というとこんなものがある。

JMSQueueAppender
作者:Jamie Tsao。これはJMSAppenderがなぜか Topic 形式でしかログを送れない...というのを補うためにあるものだ。要するに Queue でも送れるようにしよう、という実用的なものである。実は Avalon Logkit のJMSTargetFactory は Queue だろうと Topic だろうと、どっちでも送れるものだから、Log4J はマイナーな Avalon Logkit に負けているわけである...追加される可能性は高いんじゃない?
ServletContextLogAppender
作者:Alekseei Valikov。これは言うまでもなく、Servlet 環境で使うためのものだ。要するに ServletContext の log() を呼んで、フォーマットされたログメッセージを渡すものである。同様に Avalog Logkit にも ServletTargetFactory があるので、これも追加される可能性が高いんじゃない?
RSSAppender
作者:Lara D'Abreo。これも言うまでもなく RSS ファイルの形式でログを配信しちゃおう、という狙いだ。まあ、RSS で大々的にログを公開する...というのは少々お馬鹿な行為だとは思うが、面白い使いかたがあるのかもよ。時流には乗ってるよね。
TempFileAppender
作者:Ceki Gülcü。メイン開発者の御大Gülcüである。これはログの1件ごとに一時ファイルを生成し、そこにログを出すもの。管理が大変そうだ...どう使うんだろう?
ConcurrentAppender
これはメインのSandBoxにあるもの。要するに並列プログラミングを意識した、排他制御を追加する Appender である。これ自体は抽象基底クラスなので、これを拡張して「並列制御付きAppender」のシリーズを作る予定のようだ。
CompositeRollingFileAppender
作者:Kevin Steppe他。サイズベースと日時ベースと両方でログローテートをする、RollingFileAppender のバリエーション。
TextPaneAppender
作者:Sven Reimers。Swing の JTextPane にログを追加するもの。GUI 関連は 1.3 での使いづらい ListModelAppender しかないので、ここらへんが狙い目のような感じもあるな。
TextPanelAppender
作者:James House。これも Swing の JPanel に追加するもの。

...結構面白い。今ある Appender だけで「もうこれで充分?」という気もしなくもないのだが、ハッカーの集まるオープンソース・プロジェクトだと、結構意外なアイデアとかあるもんである。

ちなみに筆者も面白がって書いてみよう....(See 「AppendableAppender」)



copyright by K.Sugiura, 1996-2006