kagamihogeの日記

kagamihogeの日記です。

Why use Log4j logging vs System.out.println in Javaをテキトーに訳した

http://javarevisited.blogspot.com/2016/06/why-use-log4j-logging-vs.html をテキトーに訳した。

Why use Log4j logging vs System.out.println in Java

コンソールへのメッセージ表示はJavaプログラムの開発・テスト・デバッグにかかせない要素です。サーバーサイドアプリケーションなど、サーバ内で発生中の事象を見れない環境の場合、唯一のツールはログファイルです。ログ以外でアプリケーション内で発生中の事象を参照したり何らかのデバッグをすることは出来ません。Javaでコンソール表示を行うメソッドSystem.out.println()はそこそこ便利で、ログファイルにも転送可能ですが、実際のJavaアプリケーションでは不十分です。JavaプログラムをLinuxないしUNIXベースのシステム上で動かす場合、Log4jやSLF4jなどのロギングフレームワークは、様々な機能・柔軟性・メッセージ品質の向上など、System.out.println()では不可能なものを提供します。

私はプロダクションコードで何らかの出力を行うのにSystem.out.println()ないしSystem.err.println()を使うのは強くお勧めしません。単に要件を満たさないので、このエントリでSystem.out.println()ではなくLog4jを使うべき理由を学ぶのが良いでしょう。

ところで、EclipseNetbeansなど近代的なIDEを使用している場合、ログ目的でSystem.out.println()を使うと警告を受ける場合があります。これは好ましい動作で、あるメッセージはログファイル出力だが、それ以外はコンソールがリダイレクトされたファイルなどへ出力、というケースがありうるためです。

Javaでのコーディングの際にロギングフレームワークを使うのはベストプラクティスであり、Clean Codeなどの書籍ではロギングにLog4jを学ぶことを推奨しています。

Clean Code アジャイルソフトウェア達人の技

Clean Code アジャイルソフトウェア達人の技

Why prefer Log4j over System.out.println

たいていのJavaプログラマは何らかのロギングフレームワークを使う理由を持ち合わせており、フレームワークとしてはLog4j, java.util.Logger, SL4j, Apache Commons loggingがありこれらは総じてSystem.out.println()よりも優れています。筆者としてはprefer SLF4j over Log4jで論じたようにJavaでのログ出力にはLog4j上でSLF4jを使うことをオススメします。

ともあれ、プロダクションコードでSystem.out.println()で使わないほうがよい理由がいくつか存在します。

1) Log4j, SL4J, logback, java.util.loggerなどのロギングフレームワークはログレベル付きのデバッグ情報のログ出力が可能で、後からフィルタ条件としてそのレベルを使用できます。たとえば、プロダクションではDEBUGメッセージではなくWARNメッセージに集中したい場合、ある特定のログレベルに属するメッセージを無効化できます。

2) 次に、一つ目の理由とも関連しますがロギングフレームワークは異なるログレベルのメッセージを抑制可能です。たとえば、テスト環境ではアプリケーションはDEBUGモードで動かし、プロダクション環境ではERRORモードで動かせます。これにより生成されるログが少なくなるだけでなく、ログの処理が減るのでアプリケーションのパフォーマンスが向上します。詳細については10 tips on logging in Javaを参照してください。

System.out.println()では不可能であり、設定やJMXなど何らかのウォッチドッグ機能で実行時の動的な制御は出来ません。

なお、ウォッチドッグスレッドを使用することでアプリケーションを再起動せずにlog4jベースのロガーではログレベルを変更可能です。ウォッチドッグスレッドは、Apache Log4jの設定ファイルである、log4j.xmlの更新をモニターします。また、java.util.LoggerJMXを使用してサーバ再起動せずにログレベルを変更可能です。

3) ロギングフレームワークを使用することで、トラブルシュートやデバッグしやすくなるメタデータを含む出力が可能となります。Log4jなどのロギングフレームワークPatterLayoutのフォーマット形式を指定することで出力の整形が可能です。タイムスタンプ・クラス名・コードを実行するスレッドなどを含めることが出来ます。

これらの情報は複数スレッドがオーバーラップする出力を行うコンカレントアプリケーションのデバッグにもかなり有用です。ログ出力にSystem.out.println()を使う場合には上記の機能はありません。また、Joshua Bloch*1氏が彼の古典的な一冊となったEffective Javaでログ出力にはjava.util.Loggerなど適切なログ出力機構を使うよう推奨しています。本書は経験あるJavaプログラマのための必読書の一つです。

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)

専用のログ出力機能ではなくSystem.outやSystem.errを使う場合はプログラムの振る舞いをモニタするのが難しくなります。

例1:開発者が最初に学ぶJavaプログラムはおおむね以下のようになります。

public class MyClass
  public static void main(String[] args) {
    System.out.println("hello world");
  }
}

基本的にはプログラマJavaに関する細かな扱いや規則等を学び続けるものの、驚くべきことに最初に学んだやり方に固執してSystem.out.println()で標準出力にメッセージ出力するのを辞めない人たちもいます。まぁその……小さなテストプログラムを書くにはそれでいいとして、多数のユーザなり取引なりを実行するプロダクション環境では全く正しくありません。

Java Logging Tips

ログ出力は様々な局面でパフォーマンスに影響を与えます。500msの待ち時間短縮に繋がる原因が、サポートチームがプロダクション環境に残したDEBUGレベルだけだった、という経験があります。ユーザは遅いことに不満を述べていました。

以上が、JavaプログラマはプロダクションコードでSystem.out.println()ではなくロギングフレームワークを使うべき理由、となります。上記で述べたように、テスト用プログラムや小規模アプリケーションでは全く問題無いですが、実際のJavaアプリケーションでは全く正しくありません。XMLもしくはプロパティファイル無しでLog4jを使うことも可能で、これによりSystem.out.print()でなくロギングフレームワークを使う気が増すかもしれません。また、メタデータつきのフォーマット出力や実行時のログレベル制御は無視できない機能です。

P.S. より詳しいベストプラクティスを学びたい場合、筆者の10 Java Concurrency Best Practicesも参照してみて下さい。

*1:原文には、eve Joshua Blochとeveがついてるがこのeveって何だ……?