CodeGym コミュニティの皆さん、こんにちは!
今日はログ記録について話しましょう。
- それが何であるか、なぜ存在するのか、いつ使用する必要があるのか、いつ使用を避けるべきなのか。
- Java ではどのようなロギング実装が利用できるのか、またこれらすべてのロギング オプションを使用して何をすべきか。
そしてログレベル。アペンダーとは何か、そしてそれを正しく設定する方法について説明します。
- ノードのログ記録と、すべてが希望どおりに動作するようにノードを正しく構成する方法。
この資料は幅広い読者を対象としています。
logger.info("log something");
Java について知り始めたばかりの人だけでなく、すでに働いているが「Let's go!」 しか調べたことがない人にとっても、それは明らかです。
なぜログ記録が必要なのでしょうか?
ログ記録によって問題が解決される実際のケースをいくつか見てみましょう。これは私の仕事の一例です。アプリケーションが他のサービスと統合されるポイントがあります。私はこれらの時点でログを記録して、一種の
「アリバイ」を確立します。統合が機能していない場合、どちらの側に問題があるのかを簡単に把握できます。データベースに保存されている重要な情報を記録することも望ましいです。たとえば、管理者ユーザーの作成などです。これはまさにログに記録したほうがよい類のものです。
Javaにログインするためのツール
Java のよく知られたロギング ソリューションの中で、特に注目すべきものは次のとおりです。
- Log4j
- 7 月 — java.util.logging
- JCL — ジャカルタ・コモンズのロギング
- ログバック
- SLF4J — Java 用のシンプルなロギング ファサード
それぞれの概要を説明します。次に、実際的な議論の基礎としてslf4j - log4jバインディングを取り上げます。今は奇妙に思えるかもしれませんが、心配しないでください。この記事が終わるまでに、すべてが明らかになるでしょう。 |
System.err.println
最初は
System.err.println (コンソールにログ エントリを表示) がありました。この手法は現在でも、デバッグ時にログを迅速に取得するために使用されています。もちろん、ここで説明する設定はありませんので、この方法を覚えておいてください。次に進みます。
Log4j
これは、開発者が必要に迫られて作成した完全なソリューションです。その結果、使用できる非常に興味深いツールが完成しました。さまざまな状況により、このソリューションは最終的に JDK に組み込まれませんでしたが、このことがコミュニティ全体を大きく動揺させました。
com.example.type
Log4j には、パッケージ内でログ記録を有効にし、サブパッケージ内でログ記録をオフにする設定オプションがあります
com.example.type.generic
。これにより、ログに記録する必要のないコードを迅速に除外することができます。
ここで、Log4j には 1.2.x と 2.xx の 2 つのバージョンがあり、相互に互換性がないことに注意することが重要です。
Log4j はアペンダーの概念を追加しました
(ログの書き込みに使用されるツール) とレイアウト (ログの書式設定)。これにより、必要なものだけをログに記録し、必要な方法でログを記録できます。アペンダーについては後ほど詳しく説明します。
7 月 — java.util.logging
このソリューションの主な利点の 1 つは、JUL が JDK (Java Development Kit) に含まれていることです。残念なことに、このツールが開発されたとき、その作成者は人気のある Log4j ユーティリティをベースにしたのではなく、IBM のソリューションをベースにしていました。その決断は結果をもたらした。現実には、今では誰も JUL を使用していません。JUL のログ レベルは、Logback、Log4j、および Slf4j のログ レベルとは異なります。これにより、お互いを理解することが難しくなります。ロガーの作成も多かれ少なかれ似ています。これを行うには、インポートを行う必要があります。
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
クラス名が渡されるため、ログがどこから取得されるかがわかります。Java 8 以降では、 を渡すことができます
Supplier<String>
。これにより、以前のように毎回ではなく、本当に必要なときにのみ行を読んだり作成したりすることができます。Java 8 のリリースによって初めて、開発者は最終的に重要な問題を解決し、JUL を真に使用できるようにしました。つまり、
Supplier<String> msgSupplier
以下に示すようなパラメータを持つメソッドです。
public void info(Supplier<String> msgSupplier) {
log(Level.INFO, msgSupplier);
}
JCL — ジャカルタ・コモンズのロギング
長い間、ロギングに関する業界標準が存在せず、多くの人が独自のカスタム ロガーを作成していたため、他のラッパーの上で使用できる汎用ラッパーである JCL をリリースすることが決定されました。なぜ?プロジェクトに追加された依存関係では、プロジェクト内のロガーとは異なるロガーが使用されることがありました。このため、これらは推移的な依存関係としてプロジェクトに追加され、すべてをまとめようとしたときに大きな問題が発生しました。残念ながら、このラッパーはあまり機能的ではなく、何も追加しませんでした。みんながJCLを使えば便利かもしれません。しかし、実際はそうではなかったので、現時点では JCL を使用するのが最良のアイデアではありません。
ログバック
オープンソースへの道は険しいです... Log4j を作成したのと同じ開発者が、後継のログ フレームワークとして Logback も作成しました。Log4j と同じ考え方に基づいています。Logback の違いは次のとおりです。
- パフォーマンスを向上させた
- Slf4j のネイティブ サポートを追加しました
- 拡張されたフィルタリングオプション
デフォルトでは、Logback は構成を必要とせず、DEBUG レベル以上のすべてのイベントを記録します。カスタマイズが必要な場合は、XML 構成を通じて実現できます。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>app.log</file>
<encoder>
<pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
</encoder>
</appender>
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
<root level="info">
<appender-ref ref="FILE" />
</root>
</configuration>
SLF4J — Java 用のシンプルなロギング ファサード
2006 年のある時点で、Log4j の創設者の 1 人がプロジェクトを離れ、Log4j、JUL、common-logging、および Logback のラッパーである Slf4j (Simple Logging Facade for Java) を作成しました。ご覧のとおり、ラッパーの上にラッパーを作成するところまで進みました... この場合、アプリケーションで使用される API と、別のメソッドで追加される実装の 2 つの部分に分かれています。ログの種類ごとの依存関係。たとえば、
slf4j-log4j12.jar
と
slf4j-jdk14.jar
。正しい実装を接続する必要があります。それだけです。プロジェクト全体でそれが使用されます。Slf4j は、ログ用の文字列のフォーマットなど、すべての最新機能をサポートしています。以前はこのような問題がありました。次のようなログ エントリを作成するとします。
log.debug("User " + user + " connected from " + request.getRemoteAddr());
連結演算子により、
user
オブジェクトは黙って文字列になります
user.toString()
。これには時間がかかり、システムの速度が低下します。アプリケーションをデバッグする場合には、これで問題ないかもしれません。このクラスのログ レベルが INFO 以上の場合、問題が発生し始めます。言い換えれば、このログ エントリ (INFO 以上の場合) を書き込んではならず、文字列連結も使用すべきではありません。理論的には、ログ ライブラリ自体がこれに対処する必要があります。偶然にも、これが Log4j の最初のバージョンにおける最大の問題であることが判明しました。まともな解決策は提供されませんでしたが、代わりに次のようなことを行うことを提案しました。
if (log.isDebugEnabled()) {
log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
つまり、ログ記録用のコードを 1 行ではなく、3 行記述することを提案しました。ログを記録することでコードの変更は最小限に抑えられるはずですが、3 行は明らかにその一般的なアプローチに違反しています。Slf4j には JDK および API との互換性の問題がなかったため、すぐに優れた解決策が現れました。
log.debug("User {} connected from {}", user, request.getRemoteAddr());
ここで、
{}
はメソッドに渡される引数のプレースホルダーを示します。つまり、1 つ目は
{}
に対応し
user
、2 つ目
{}
は に対応します
request.getRemoteAddr()
。このようにすることで、ログ レベルでログ エントリの書き込みが必要な場合にのみ文字列の連結が実行されます。その後、Sjf4j の人気は急速に高まり始めました。現時点では、それが最善の解決策です。したがって、バインディングを使用したロギングを見てみましょう
slf4j-log4j12
。
ログに記録する必要があるもの
もちろん、すべてをログに記録する必要はありません。これは多くの場合必要ではなく、場合によっては危険ですらあります。たとえば、誰かの個人データを記録し、それが何らかの形で漏洩した場合、特に西側市場に焦点を当てたプロジェクトでは、深刻な問題が発生します。ただし、
必ずログに記録する必要があるものもあります。
- アプリケーションの開始/終了。アプリケーションが本当に期待どおりに開始および終了したかどうかを知る必要があります。
- セキュリティ上の問題。ここでは、誰かのパスワードを推測しようとした試みや、管理者がサインインしたときのインスタンスなどを記録するとよいでしょう。
- 特定のアプリケーションの状態。たとえば、ビジネス プロセス内のある状態から別の状態への遷移です。
- 特定のデバッグ情報と対応するログ レベル。
- 特定の SQL スクリプト。これが必要となる実際のケースもあります。ただし、ログ レベルを巧みに調整することで、優れた結果を達成できます。
- 正常に動作していることを確認するときに、実行中のスレッドをログに記録できます。
ロギングでよくあるエラー
ここには多くのニュアンスがありますが、いくつかのよくある間違いについて特に言及します。
- 過剰なログ記録。理論的には重要である可能性があるすべてのステップをログに記録すべきではありません。ここでの良い経験則は、ログが負荷の 10% を超えないようにすることです。そうしないと、パフォーマンス上の問題が発生します。
- すべてのデータを 1 つのファイルに記録します。これにより、特定のシステムにはファイル サイズの制限があることは言うまでもなく、ある時点でログの読み取り/書き込みが非常に困難になります。
- 間違ったログレベルを使用している。各ログ レベルには明確な境界があり、それらを尊重する必要があります。境界が明確でない場合は、どのレベルを使用するかについて合意に達することができます。
ログレベル
|
|
|
×: 見える |
|
|
|
|
致命的 |
エラー |
警告 |
情報 |
デバッグ |
痕跡 |
全て |
オフ |
|
|
|
|
|
|
|
致命的 |
バツ |
|
|
|
|
|
|
エラー |
バツ |
バツ |
|
|
|
|
|
警告 |
バツ |
バツ |
バツ |
|
|
|
|
情報 |
バツ |
バツ |
バツ |
バツ |
|
|
|
デバッグ |
バツ |
バツ |
バツ |
バツ |
バツ |
|
|
痕跡 |
バツ |
バツ |
バツ |
バツ |
バツ |
バツ |
|
全て |
バツ |
バツ |
バツ |
バツ |
バツ |
バツ |
バツ |
ログレベルとは何ですか? 何らかの方法でログ エントリの階層を作成するには、特定の規則と区切りが必要です。これが、ログ レベルが導入された理由です。レベルはアプリケーションで設定します。エントリが指定されたレベルより低い場合、そのエントリはログに記録されません。たとえば、アプリケーションのデバッグ時に使用するログがあります。通常の動作中 (アプリケーションが本来の目的で使用されている場合)、このようなログは必要ありません。したがって、ログ レベルはデバッグ用よりも高くなります。Log4j を使用してログ レベルを見てみましょう。JUL とは別に、他のソリューションでも同じログ レベルが使用されます。以下に降順に示します。
- OFF:ログ エントリは記録されません。すべてが無視されます。
- FATAL:アプリケーションの実行を続行できないエラー。たとえば、「JVM メモリ不足エラー」などです。
- エラー:このレベルのエラーは、解決する必要がある問題を示しています。このエラーによってアプリケーション全体が停止するわけではありません。他のリクエストは正しく動作する可能性があります。
- WARN:警告を表すログ エントリ。予期せぬ事態が発生しましたが、システムは対処してリクエストを実行しました
- 情報:アプリケーション内の重要なアクションを示すログ エントリ。これらはエラーや警告ではありません。これらは予期されるシステム イベントです。
- DEBUG:アプリケーションをデバッグするには、ログ エントリが必要です。アプリケーションが期待どおりに動作することを確認するため、またはアプリケーションによって実行されるアクション (つまり「入力されたメソッド 1」) を説明するため。
- TRACE:デバッグ用の優先順位の低いログ エントリ。最も低いログレベル。
- ALL:アプリケーションのすべてのログ エントリを書き込むためのログ レベル。
INFO ログ レベルがアプリケーションのどこかで有効になっている場合、INFO から FATAL までのすべてのレベルのエントリがログに記録されます。FATAL ログ レベルが設定されている場合、そのレベルのログ エントリのみが書き込まれます。
ログの記録と送信: アペンダー
ログの書き込み/送信に十分な機会を提供する Log4j を使用する場合に、これらすべてがどのように機能するかを考えてみましょう。
- ファイルに書き込むには —
DailyRollingFileAppender
- 情報をコンソールに書き込むには —
ConsoleAppender
- ログをデータベースに書き込むには —
JDBCAppender
- TCP/IP 経由でのログ送信を管理するには —
TelnetAppender
- ロギングがパフォーマンスに悪影響を及ぼさないようにするため —
AsyncAppender
さらにいくつかの実装があります。完全なリストは
ここで入手できます。ちなみに、必要なアペンダーが存在しなくても問題ありません。
Log4j がサポートするAppenderインターフェイスを実装することで、独自のアペンダーを作成できます。
ロギングノード
デモンストレーションの目的で、Log4j からの実装を備えた Slf4j インターフェイスを使用します。ロガーの作成は非常に簡単です。ロギングを行う という名前のクラスに
MainDemo
、次のコードを追加する必要があります。
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
これにより、ロガーが作成されます。ログ エントリを作成するには、使用されるログ レベルを名前に反映したメソッドがいくつかあります。例えば:
logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
クラスを渡していますが、最終的な名前はパッケージを含むクラスの完全な名前になります。これは、後でロギングをノードに分割し、各ノードのロギング レベルとアペンダーを構成できるようにするために行われます。たとえば、ロガーは
com.github.romankh3.logginglecture.MainDemo
クラスで作成されました。この名前は、ログ ノードの階層を作成するための基礎となります。メイン ノードはトップレベルの
RootLoggerです。これは、アプリケーション全体のすべてのログ エントリを受信するノードです。残りのノードは次のように表すことができます。
アペンダーは特定のログ ノードに対して構成されます。
次に、 log4j.propertiesファイルを見て、その構成方法の例を確認します。
log4j.properties ファイルのステップバイステップ ガイド
一度にすべてを 1 ステップずつ設定して、何が可能になるかを確認します。
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
この行は、org.apache.log4j.ConsoleAppender 実装を使用する CONSOLE アペンダーを登録していることを示しています。このアペンダーは、コンソールに情報を書き込みます。次に、別のアペンダーを登録します。これはファイルに書き込みます:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
アペンダー自体を構成する必要があることに注意することが重要です。アペンダーを登録したら、ノードでどのログ レベルとどのアペンダーを使用するかを決定できます。
log4j.rootLogger=デバッグ、コンソール、ファイル
- log4j.rootLogger は、すべてのログ エントリを含むルート ノードを構成していることを意味します。
- 等号の後の最初の単語は、書き込む最小ログ レベルを示します (この場合は DEBUG です)。
- カンマの後に、使用するすべてのアペンダーを示します。
より具体的なログ ノードを構成するには、次のようなエントリを使用します。
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
where
log4j.logger.
は、特定のノードを参照するために使用されます。この例では、
com.github.romankh3.logginglecture.
CONSOLE アペンダーの構成について説明します。
# CONSOLE appender customization
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
ここでは、アペンダーが動作を開始する特定のレベルを設定できることがわかります。実際に何が起こるかの例を次に示します。INFO レベルのメッセージがロギング ノードによって受信され、それに割り当てられたアペンダに渡されたとします。アペンダーのしきい値が WARN に設定されている場合、アペンダーはログ エントリを受け取りますが、それに対して何も行いません。次に、メッセージで使用するレイアウトを決定する必要があります。この例では PatternLayout を使用していますが、他にも多くのオプションがあります。この記事ではそれらについては説明しません。FILE アペンダの設定例:
# File appender customization
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
次の行からわかるように、ログ エントリが書き込まれる特定のファイルを構成できます。
log4j.appender.FILE.File=./target/logging/logging.log
エントリが
logging.log
ファイルに書き込まれます。ファイル サイズの問題を回避するために、最大値 (この場合は 1MB) を構成できます。
MaxBackupIndex
このようなログ ファイルがいくつ存在するかを示します。これより多くのファイルを作成する必要がある場合、最初のファイルは削除されます。ロギングが構成されている実際の例を確認するには、 GitHub の
パブリック リポジトリにアクセスしてください。
話し合った内容を強化する
ここで説明したことをすべて自分で実行してみてください。
- 上記の例と同様の独自のプロジェクトを作成します。
- Maven の使用方法を知っている場合は、それを使用してください。そうでない場合は、ライブラリの接続方法が説明されているこのチュートリアルをお読みください。
要約すれば
- Java に存在するロギング ソリューションについて話しました。
- 有名なロギング ライブラリのほとんどは 1 人によって書かれました :D
- 何を記録すべきか、何を記録すべきではないかを学びました。
- ログレベルを把握しました。
- ロギングノードについて紹介しました。
- アペンダーとは何か、またその目的について説明しました。
- log4j.proterties ファイルを段階的に作成しました。
追加資料
- CodeGym:ロガーのレッスン
- Weekly Geekly: Java ログ。こんにちは世界
- コーディングの恐怖:ロギングの問題
- YouTube: Java Logging Hell を理解する - 基本。Java ロギング地獄とそこから抜け出す方法
- Log4j:アペンダー
- Log4j:レイアウト
私の他の記事も参照してください。
GO TO FULL VERSION