System.out.printlnとロガー
1. なぜロガー?
プログラムを書いたり、エンタープライズプロダクションアプリケーションを開発したりするときは、System.out.printlnを使用するのが最も簡単で簡単なオプションのようです。 クラスパスに追加する追加のライブラリはなく、追加の構成も必要ありません。
ただし、 System.out.println を使用すると、多くの状況での使いやすさに影響するいくつかの欠点があります。 このチュートリアルでは、なぜ、いつ、昔ながらのSystem.outとSystem.errでロガーを使用するのかについて説明します。 Log4J2ロギングフレームワークを使用した簡単な例もいくつか示します。
2. 設定
始める前に、必要なMavenの依存関係と構成を調べてみましょう。
2.1. Mavenの依存関係
Log4J2依存関係をpom.xmlに追加することから始めましょう。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
log4j-apiとlog4j-coreの最新バージョンはMavenCentralで見つけることができます。
2.2. Log4J2構成
System.out を使用する場合、追加の構成は必要ありません。 ただし、Log4J2を使用するには、log4j.xml構成ファイルが必要です。
<Configuration status="debug" name="baeldung" packages="">
<Appenders>
<Console name="stdout" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
</Console>
</Appenders>
<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
</Configuration>
ほとんどすべてのロガーフレームワークは、プログラムまたはここに示すXMLファイルなどの外部構成ファイルを介して、ある程度の構成を必要とします。
3. ログ出力の分離
3.1. System.outおよびSystem.err
アプリケーションをTomcatなどのサーバーにデプロイする場合、サーバーは独自のロガーを使用します。 System.out を使用すると、ログはcatalina.outになります。 ログを別のファイルに保存すると、アプリケーションのデバッグがはるかに簡単になります。 Log4j2では、アプリケーションログを別のファイルに保存するために、構成にファイルアペンダーを含める必要があります。
また、 System.out.println では、どのログを印刷するかを制御またはフィルタリングすることはできません。 ログを分離する唯一の可能な方法は、情報ログには System.out.println を使用し、エラーログにはSystem.err.printlnを使用することです。
System.out.println("This is an informational message");
System.err.println("This is an error message");
3.2. Log4J2ログレベル
デバッグ環境または開発環境では、アプリケーションが印刷しているすべての情報を確認する必要があります。 ただし、稼働中のエンタープライズアプリケーションでは、ログが増えるとレイテンシが増加します。 Log4J2のようなロガーフレームワークは、複数のログレベルコントロールを提供します:
- 致命的
- エラー
- 暖かい
- 情報
- デバッグ
- 痕跡
- 全て
これらのレベルを使用すると、いつどこでどの情報を印刷するかを簡単にフィルタリングできます:
logger.trace("Trace log message");
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
logger.warn("Warn log message");
logger.fatal("Fatal log message");
また、各ソースコードパッケージのレベルを個別に構成する場合もあります。 ログレベルの構成の詳細については、JavaLoggingの記事を参照してください。
4. ファイルへのログの書き込み
4.1. System.outおよびSystem.errの再ルーティング
System.setOut()メソッドを使用して、System.out.printlnをファイルにルーティングすることができます。
PrintStream outStream = new PrintStream(new File("outFile.txt"));
System.setOut(outStream);
System.out.println("This is a baeldung article");
また、 System.err の場合:
PrintStream errStream = new PrintStream(new File("errFile.txt"));
System.setErr(errStream);
System.err.println("This is a baeldung article error");
System.outまたはSystem.errを使用して出力をファイルにリダイレクトする場合、ファイルサイズを制御できません。したがって、ファイルはアプリケーションの実行中、大きくなり続けます。
ファイルサイズが大きくなると、これらの大きなログを開いたり分析したりするのが難しくなる可能性があります。
4.2. Log4J2を使用したファイルへのログ記録
Log4J2は、ログを体系的にファイルに書き込み、特定のポリシーに基づいてファイルをロールするメカニズムを提供します。 たとえば、日付/時刻パターンに基づいてロールオーバーするファイルを構成できます。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<File name="fout" fileName="log4j/target/baeldung-log4j2.log"
immediateFlush="false" append="false">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
</File>
<Loggers>
<AsyncRoot level="DEBUG">
<AppenderRef ref="stdout"/>
<AppenderRef ref="fout"/>
</AsyncRoot>
</Loggers>
</Configuration>
または、指定されたしきい値に達したら、サイズに基づいてファイルをロールすることができます。
...
<RollingFile name="roll-by-size"
fileName="target/log4j2/roll-by-size/app.log" filePattern="target/log4j2/roll-by-size/app.%i.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="5 KB"/>
</Policies>
</RollingFile>
5. 外部システムへのロギング
前のセクションで見たように、ロガーフレームワークではログをファイルに書き込むことができます。 同様に、他のシステムやアプリケーションにログを送信するためのアペンダーも提供します。 これにより、Log4Jアペンダーを使用するのではなく、Log4Jアペンダーを使用してログをKafkaStreamまたはElasticsearchデータベースに送信できます。
このようなアペンダーの使用方法の詳細については、Log4jアペンダーの記事を参照してください。
6. ログ出力のカスタマイズ
ロガーを使用すると、実際のメッセージと一緒に印刷する情報をカスタマイズできます。 印刷できる情報には、パッケージ名、ログレベル、行番号、タイムスタンプ、メソッド名などが含まれます。
これはSystem.out.printlnで可能ですが、ロギングフレームワークがこの機能をすぐに提供する一方で、多くの手作業が必要になります。 ロガーを使用すると、ロガー構成でパターンを簡単に定義できます:
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%style{%date{DEFAULT}}{yellow}
%highlight{%-5level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green} %message"/>
</Console>
ロガーフレームワークにLog4J2を検討する場合、選択またはカスタマイズできるパターンがいくつかあります。 詳細については、公式Log4J2ドキュメントを参照してください。
7. 代わりに例外出力をログに記録することにより、 printStackTrace()を回避します
コードで例外を処理するとき、実行時に実際に発生した例外を知る必要があることがよくあります。 これには2つの一般的なオプションがあります: printStackTrace()またはロガー呼び出しの使用。
printStackTrace()を使用して例外に関する詳細を出力する例外処理を確認することは非常に一般的です。
try {
// some code
} catch (Exception e) {
e.printStackTrace();
}
ここでの問題は、 printStackTrace()がその情報をSystem.err に出力することであり、これは避けたいとすでに述べています。
代わりに、ロギングフレームワークを使用して例外をログに記録すると、ログを簡単に取得できるようになります。
try {
// some code
} catch (Exception e) {
logger.log("Context message", e);
}
8. 結論
この記事では、ロガーフレームワークを使用するさまざまな理由と、アプリケーションログをSystem.out.printlnのみに依存しない理由について説明します。 小規模なテストプログラムにSystem.out.printlnを使用することは正当ですが、エンタープライズ実稼働アプリケーションの主要なロギングソースとして使用することは望ましくありません。
いつものように、この記事のコード例はGitHubから入手できます。