Java Logging API vs Log4j

2008年01月22日

Logging API vs Log4j

結局のところ Logging API と Log4j のどちらを選べばよいのでしょうか? 主観ですが双方のメリット・デメリットをざっくり比較すると以下のように なります。

Logging API Log4j
JDK標準 × Log4jは追加JAR必須
パフォーマンス 実用レベル
設計の柔軟性 実用レベル
標準機能 LoggingAPIは簡易実装並
設定ファイル LoggingAPIは詳細設定不十分
動作実績 実用レベル
下位互換 JDK 1.4 JDK 1.1 1.3以前ならLog4j
セキュリティ × LoggingAPIはアクセス制御の管理下
DB保存
Winイベントログ
Unix Syslog
メール通知等
LoggingAPIは要実装
◎…優位 (だがオーバースペック気味)、 ○…十分に実用レベル
△…使用に難あり、 ×…必須要件なら選択不可

使う側にしてみれば必要な実装が一通り揃っておりパフォーマンス的にも有利な Log4j を選択したいところです。特に大規模なシステム開発では、チームごとに 自分たちのドメインの設定ファイルを作成したり、設定ファイル上でパッケージ ごとの出力先を分けられる必要が出てきます。また運用要件で syslog などを使用する必要が出てくるかも知れません。

しかし必要な要件をどちらも満たしているのであれば、実質的にこの選択は JAR の追加配備に問題があるかどうかで決まります。

Log4j 推奨

JAR の追加配備が容易な場合。具体的にはサーブレットや EJB などのサーバサイド開発。 あるいはイントラネットで Java Web StartGoogle I'm Feeling Lucky™ を使用したり、インストーラが整っているリッチクライアント。

技術者であればどちらを選択しても実質的なパフォーマンスはあまり変わらないと 理解できるでしょう。しかしログに関するパフォーマンスは技術者でない立場の人間 でも気になってしまうところです。後々、パフォーマンスに関して痛くもない腹を 探られる心配をするくらいなら (逆にそれを一蹴できる理由がないなら) Log4j が無難といえます。

Logging API 推奨

JAR の追加配備が難しい場合。不特定多数のブラウザで動くアプレットなどのように ネットワークダウンロードを伴う場合や、小回りが重視される小さなユーティリティ ツール、バッチなど。またライセンスやライブラリ管理などの理由でプロジェクトで 不必要なライブラリは極力使わない方針の場合。

また Logging API は Java 標準レベルのアクセス制御が考慮されており、信用の 置けないクラスライブラリが勝手にログハンドラを差し替えてシステムのログを 盗めないような構成もできます (そもそもそんなライブラリを、とも思いますが)。

実際問題、実測的な裏づけもなしに Logging API から Log4j へ差し替えるチューニング 行為は徒労に終わるでしょう。これは余程手を尽くした後の悲劇的なショットガン対処 なのか、あるいは分析者が手を抜いているかのどちらかであり、一般的には使用頻度の高い SQL や XML 解析処理の一つ二つを再考した方がはるかに効果的です。

とはいえまともな技術者なら根拠もなしに問題ないだろうと結論付けるのも問題ですので、 実質的なパフォーマンスでどの程度の差が出るかを以下簡単にレポートしてみました。

目的

Java 1.4 から標準機能となった Logging API と Jakarta Log4j 双方のロギング機能に 対して、一般的なシステムを想定した場合のパフォーマンス検討ができる程度の 実効値を取得します。なお、これはログ出力のパフォーマンス向上を検討するもの ではありません。

実験方法

Logging API、Log4j それぞれ 10 秒間で何回のログ出力を実行できるかを計測。 これを交互にそれぞれ 10 回実行して平均をとり 1 回の呼び出しあたりの実行時間 を求める。

双方共に同じ書式のログを出力させるためには Logging API で FormatterJava™ API リファレンス を実装しなければならないが、その実装のちょっとした差が結果に大きく影響した ため、同じ実装の LayoutJava™ API リファレンス も作成してフォーマット処理の時間差は考慮から除外することにした。フォーマット されたログの出力内容は以下の通り。

出力内容
[2008/01/23 06:01:11] - hello, world
[2008/01/23 06:01:11] - hello, world
...

実行は実質的にシングルスレッドで行っているため、並列処理に対する効率は結果に反映されない。

OS Linux 2.6.18.2-34
CPU Intel Xeon 2.67GHz SMP
HDD ATA/100 7,200rpm (論理ボリューム ext3)
JVM Java SE 6 Sun JRE 1.6.0_04-b12 HotSpot Server VM mixed mode
Log4j Apache Log4j 1.2.15

ここに記載している結果の信頼性や、別の環境での追従試験を行う場合は、以下の ZIP ファイルに格納されているソースファイルを参照のこと。

結果と考察

まず、ログ出力時に I/O ブロッキングを伴う FileHandlerJava™ API リファレンスFileAppenderJava™ API リファレンス で実行する。続いてディスク I/O を伴わない NullHandler (自作)、 NullAppenderJava™ API リファレンス で実行し、ディスク I/O の影響がどれほどかを算出する。

+ディスク I/O あり
+ディスク I/O なし

実行結果から単位秒あたりの実行回数平均と 1 回の呼び出しに要する平均時間は以下の通り。

Logging API Log4j   Logging API Log4j
[call/sec] [call/sec] [μsec/call] [μsec/call]
ディスク I/O あり 69,622 89,708 14.363 11.147
ディスク I/O なし 398,340 1,094,975 2.510 0.913
ディスク I/O 影響 11.853 10.234

単位時間あたりの実行回数から I/O の影響を受ける状況で 28.9%、全く受けない 状況なら 174.9% ほど Log4j の方が高い パフォーマンスであることが窺えます。

また、ディスク I/O の割合に対してログ機能の 実行時間は 1~2 割程度でしかないことが分かります。これはパフォーマンス 的な観点でログの選定を思案するより、非同期ログ (Log4j で言うところの AsyncAppenderJava™ API リファレンス) の使用やディスク性能、I/O バッファ調整による効果が大きく出ると予想されます。

どちらが速いかと問われれば今のところ Log4j の優位性は揺るぎありません。 PatternLayoutJava™ API リファレンス にしても、 MessageFormatJava™ API リファレンスString#format()Java™ API リファレンス はもちろんの事、普通のプログラマが普通に組んだ固定フォーマット用 Layout クラスの方が遅くても私はあまり驚かないでしょう。

しかし、これは所詮μ秒 オーダーの差でしかないということも事実です。具体例を挙げれば、 秒 1,000 回のログを出力する負荷の高いサーバで全体で数ミリ秒、 並列処理の同期化を考慮してもせいぜい数十ミリ秒程度の差にしかならない でしょう。

一般的には、どちらを使用してもロギング機能自体のパフォーマンスが システムに与えるインパクトはないと言えるでしょう。むしろログを 出力するために文字列を組み立てる処理 (Object#toString()Java™ API リファレンス など) や、自作の Formatter クラス実装、ファイルシステムの I/O ブロックといったライブラリ以外の要因の方がはるかに大きいと予想します。

CVS 2008/02/06