Jakarta Log4j

1月 1, 2003 · Posted in Apache log4j · Comment 

入手

公式サイト(http://logging.apache.org/log4j/docs/)からバイナリファイルを入手して解凍。
dist/lib/log4j-?.?.?.jarをクラスパス上に置く。

ログレベル

Log4jでは5段階のログレベルが定義されている。
レベルの低い順に、DEBUG, INFO, WARN, ERROR, FATALとなっている。

基本的な分類

ログレベル 用途
DEBUG デバッグ情報
INFO 運用情報
WARN 警告情報
ERROR エラー情報
FATAL 致命的な情報

実装

サンプルクラス

import org.apache.log4j.Logger;

public class Log4jSample {
    public static void main(String[] args) {
        Log4jSample logic = new Log4jSample();
        logic.method();
    }

    public void method(){
        Logger logger = Logger.getLogger(this.getClass());
        logger.debug("debug");
        logger.info("info");
        logger.warn("warn");
        logger.error("error");
        logger.fatal("fatal");
    }
}

システムのログレベルがINFOのときは、INFOよりレベルの低いDEBUGのロギング処理がスキップされる。INFOを含んだINFOより高いレベルのロギング処理が実行される。

ログレベルの変更を定義ファイルやSystemプロパティなどで手軽に行うことができるため、開発環境ではDEBUG、運用環境ではINFOのような使い分けを行うことができる。

ログレベル、出力フォーマットなどの指定

構成

Log4jは次の3つの要素で構成されている。

  • Categories
  • Appenders
  • Layouts

Categories

複数のロギング設定をカテゴリとして分類することができる。
カテゴリの分類には、javaのパッケージが利用される。

Appenders

ログ出力のあて先を指定する。
あて先とは、コンソール・ファイル・GUIコンポーネントなどを表す。
複数のあて先を指定した場合、非同期でロギング処理が実行される。

Layouts

出力時のレイアウトを指定する。TEXT,HTML,XMLなど。

log4j.properties

Categories

プロパティファイルでカテゴリーの指定を行う場合、次のような書式で行う。

log4j.rootLogger=[レベル], [Appender]
log4j.logger.package.package=[レベル], [Appender]
log4j.logger.com.ayumu-baby.634.logic=[レベル], [Appender]

全てのパッケージに対する設定

log4j.rootLogger=[レベル], [Appender]

上記のように指定することで、すべてのパッケージに対してログの設定を行うことができる。

個別のパッケージに対する設定

log4j.logger.[パッケージ階層]

パッケージを指定することで、指定したパッケージに対するログの設定を行うことができる。
なお、親パッケージの設定は子パッケージでも有効となる。
親子ともにカテゴリ設定が行われている場合、子の設定が優先される。

Appenders

log4j.appender.[Appender名]=[Appenderクラス]

Log4jプロジェクトで用意されているAppenderにはAsyncAppender, JMSAppender, NTEventLogAppender, NullAppender, SMTPAppender, SocketAppender, SyslogAppender, WriterAppenderがある。
たとえばWriteAppenderのサブクラスであるConsoleAppenderをstandardという名称のAppenderとして指定するためには

log4j.appender.standard=org.apache.log4j.ConsoleAppender

とする。

Layouts

log4j.appender.[Appender名].layout=[Layoutクラス]

Log4jプロジェクトで用意されているLayoutには、DateLayout, HTMLLayout, PatternLayout, SimpleLayout, XMLLayoutがある。

プロパティファイルの例

Log4j.properties

log4j.rootLogger=INFO,standard
log4j.logger.logic=DEBUG,standard
log4j.logger.web=DEBUG,standard,io

log4j.appender.standard=org.apache.log4j.ConsoleAppender
log4j.appender.standard.layout=org.apache.log4j.SimpleLayout

log4j.appender.io=org.apache.log4j.FileAppender
log4j.appender.io.File=log.html
log4j.appender.io.layout=org.apache.log4j.HTMLLayout
  • logicパッケージ内のログメッセージはコンソールに出力される。
  • webパッケージ内のログメッセージはコンソールとファイル(log.html)に出力される。
  • 上記以外のパッケージ内のログメッセージはコンソールに出力される。

XML定義ファイル

Log4jの設定をXMLファイルで指定することも可能。書式が異なるだけで、構成や概念はプロパティファイルと同様。

枠組み

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "./log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
</log4j:configuration>

Category

カテゴリーの指定はCategoryタグを使用する。タグの内側に参照するAppenderを記述する。

<category name="log4j">
    <appender-ref ref="CONSOLE_INFO" />
    <appender-ref ref="LOGFILE_DEBUG" />
</category>

<root>
    <appender-ref ref="CONSOLE_INFO" />
</root>

Appender&Layouts

AppenderとLayoutsはセットで定義する。

<appender name="CONSOLE_INFO" class="org.apache.log4j.ConsoleAppender" >
    <param name="threshold" value="info"/>
</appender>

<appender name="LOGFILE_DEBUG" class="org.apache.log4j.FileAppender">
    <param name="threshold" value="debug"/>
    <param name="file" value="debug.html" />
    <param name="append" value="true" />
    <layout class="org.apache.log4j.HTMLLayout"/>
</appender>

Layoutに書式をパラメータで渡す場合、Layoutタグの中に<param>タグで指定する。

<layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern"
              value="%d %-5p %c - %m [%t] (%F:%L)%n" />
</layout>

XML定義ファイルの例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "./log4j.dtd">

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>

  <appender name="CONSOLE_INFO" class="org.apache.log4j.ConsoleAppender" >
    <param name="threshold" value="info"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d (%F:%L) %m%n"/>
    </layout>
  </appender>

  <appender name="LOGFILE_DEBUG" class="org.apache.log4j.FileAppender">
    <param name="threshold" value="debug"/>
    <param name="file" value="debug.html" />
    <param name="append" value="true" />
    <layout class="org.apache.log4j.HTMLLayout"/>
  </appender>

  <category name="log4j">
    <appender-ref ref="LOGFILE_DEBUG" />
  </category>

  <root>
    <appender-ref ref="CONSOLE_INFO" />
  </root>

</log4j:configuration>

Java/log4j/Nested Diagnostic Context(NDC)

1月 1, 2003 · Posted in Apache log4j, Java · Comment 

NDCの概要

Log4jで用意されているオブジェクト。
このオブジェクトに値を設定しておくことで、ログ出力時にその設定した値を出力することができる。
で、何がうれしいのかというと、NDCはThreadLocalなので、スレッドごとに別の値を保有できるということ。

説明が難しいのでたとえば、

log.debug("log1");
log.debug("log2");
というLog4jのコードがあって、それをユーザーAさんが実行した。

ログの内容

2009/12/01 10:11:12 log1
2009/12/01 10:11:12 log2

続いてユーザーBさんが実行。

ログの内容

2009/12/01 10:11:12 log1
2009/12/01 10:11:12 log2
2009/12/01 10:11:15 log1
2009/12/01 10:11:15 log2

さて、これだと誰の実行ログなのかわからないので、
「ログにユーザーIDを出してください」
といわれたとする。

単純にやると、

log.debug(userId + ":log1");
log.debug(userId + ":log2");

システムが単純ならいいんだけど、これが「一時的に」とか「大規模システム全体」にとかなると、きつい。
将来的に、「ユーザーの所属グループも出してくれ」とか言われたら、また繰り返し。

これを解決するのがNDC。Log4jってすばらしい。

使い方

ロギングを行う前に、NDCオブジェクトに値を設定しておく。
フィルターで使うことが多いんじゃないかと思う。
設定した値は%xで出力指定する。

コード

ソースファイル(抜粋)

NDC.push("hello");
log.debug("log1");
log.debug("log2");
NDC.remove();

log4j.properties(例)

log4j.rootCategory=DEBUG, stdout

#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%x %-5p %d{HH:mm:ss} %m%n

結果

hello DEBUG 10:05:25 log1
hello DEBUG 10:05:25 log2
ナイス!

プッシュした分だけ、

NDC.push("hello");
NDC.push("hello");
log.debug("log1");
log.debug("log2");
NDC.remove();
NDC.remove();

%xで表示される

hello hello DEBUG 10:05:25 log1
hello hello DEBUG 10:05:25 log2

ユーザーごとに(正確にはスレッドごとに)値を設定すれば、

NDC.push(userId);
log.debug("log1");
log.debug("log2");
NDC.remove();

ちゃんと追跡できる

userA DEBUG 10:05:25 log1
userA DEBUG 10:05:25 log2
userZ DEBUG 10:31:10 log1
userZ DEBUG 10:31:10 log2

設定した値の開放

設定した値は NDC.remove();で開放しないとずっと保持されたままになってしまうので、ちゃんと開放すること!
try-finallyか、destroy系の処理内で書くのがベスト。

まとめ

横槍通す感じだからアスペクト指向に似てる。
フィルターで使えば取り外しも簡単。