人生で初めて、オープンソースとなる Java API を書く立場になりました。他の多くのプロジェクトに組み込めることを願っています。
ログ記録については、私 (および実際に一緒に働いている人々) は常に JUL ( java.util.logging
) を使用しており、問題が発生したことはありません。ただし、API 開発のために何をすべきかをより詳細に理解する必要があります。これについていくつか調査しましたが、入手した情報では混乱するばかりです。そのため、この投稿をします。
私は JUL 出身なので、その点については偏見があります。他の部分については、それほど詳しくありません。
私が行った調査から、人々が JUL を好まない理由として以下のことが分かりました。
「Sun が JUL をリリースするずっと前から Java で開発を始めたので、何か新しいことを学ぶよりも、logging-framework-X を使い続ける方が簡単だった」ふーむ。冗談ではなく、実際に人々が言うことです。この議論なら、私たち全員が COBOL をやっているかもしれません。(ただし、私自身が怠け者なので、この意見には共感できます)
「JUL のログ レベルの名前が気に入らない」。 正直に言うと、これは新しい依存関係を導入する理由としては不十分です。
「JUL からの出力の標準形式が気に入らない」うーん。これは単なる設定です。コードに関しては何もする必要すらありません。 (確かに、昔は正しく動作させるために独自の Formatter クラスを作成する必要があったかもしれません)。
「logging-framework-X を使用する他のライブラリも使用しているため、それを使用する方が簡単だと思いました。」 これは循環論法ではありませんか? なぜ「誰もが」logging-framework-X を使用していて、JUL を使用しないのでしょうか?
「他のみんなはlogging-framework-Xを使用しています」。私にとってこれは、上記の特殊なケースにすぎません。多数派が常に正しいとは限りません。
では、本当の大きな疑問は、なぜ JUL ではないのかということです。私が見逃しているものは何でしょうか? ロギング ファサード (SLF4J、JCL) の存在理由は、歴史的に複数のロギング実装が存在してきたことであり、その理由は、私の見解では、JUL 以前の時代にまで遡ります。JUL が完璧であれば、ロギング ファサードは存在しないはずです。それともどうでしょうか? さらに混乱を招くのは、JUL 自体がある程度ファサードであり、ハンドラー、フォーマッター、さらには LogManager の交換も可能だということです。
同じこと (ログ記録) を複数の方法で実行するのではなく、そもそもなぜそれが必要だったのかを疑問視すべきではないでしょうか (そして、その理由がまだ存在するかどうかを確認します)
さて、これまでの調査で、JUL の実際の問題と思われる点がいくつか見つかりました。
パフォーマンス。SLF4J のパフォーマンスは他よりも優れているという人もいます。これは、時期尚早な最適化の例のように思えます。1 秒あたり数百メガバイトのログを記録する必要がある場合は、いずれにしても正しい方向に進んでいるかどうかはわかりません。JUL も進化しており、Java 1.4 で行ったテストはもはや当てはまらない可能性があります。これについては、次のリンクを参照してください。こここの修正は Java 7 に取り入れられました。また、ロギング メソッドでの文字列連結のオーバーヘッドについても多くの人が話しています。ただし、テンプレート ベースのロギングではこのコストを回避でき、JUL にも存在します。個人的には、テンプレート ベースのロギングは実際には書きません。面倒だからです。たとえば、JUL で次のようにするとします。
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
IDE は警告し、次のように変更するかどうかの許可を求めます:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. もちろん承諾します。許可しました! ご協力ありがとうございました。
したがって、実際にそのようなステートメントを自分で記述するのではなく、IDE によって実行されます。
パフォーマンスの問題に関して結論を言うと、競合他社と比較して JUL のパフォーマンスが劣っていることを示すものは何も見つかりませんでした。
クラスパスからの設定。JULはクラスパスから設定ファイルを読み込むことができません。数行のコードそうします。これが面倒なのはわかりますが、解決策は短くて簡単です。
出力ハンドラの可用性。JUL には、コンソール、ファイル ストリーム、ソケット、メモリの 5 つの出力ハンドラが標準で付属しています。これらは拡張することも、新しいものを作成することもできます。たとえば、UNIX/Linux Syslog や Windows イベント ログへの書き込みなどです。個人的には、この要件に遭遇したことも、使用されているのを見たこともないのですが、なぜこれが便利な機能なのかはよく理解できます。たとえば、Logback には Syslog 用のアペンダーが付属しています。それでも、私は次のように主張します。
- 出力先のニーズの 99.5% は、JUL に標準装備されている機能でカバーされます。
- 特別なニーズは、他の何かの上にではなく、JUL の上にカスタム ハンドラーを置くことで対応できます。JUL 用の Syslog 出力ハンドラーの作成に、他のログ記録フレームワークよりも時間がかかるということを示唆するものは何もありません。
何か見落としているのではないかと心配しています。JUL 以外のロギング ファサードやロギング実装の使用は非常に広範であるため、理解していないのは私だけという結論に至らざるを得ません。残念ながら、これが初めてではないでしょう。:-)
では、API をどうしたらいいでしょうか? 成功させたいです。もちろん、単に「流れに身を任せて」SLF4J (最近最も人気があるようです) を実装することもできますが、私自身のために、今日の JUL のどこが問題なのかを正確に理解する必要があります。ライブラリに JUL を選択すると、自分自身を妨害することになるのでしょうか?
パフォーマンスのテスト
(このセクションは 2012 年 7 月 7 日に nolan600 によって追加されました)
以下に、Ceki による SLF4J のパラメータ化が JUL より 10 倍以上高速であるという参考文献があります。そこで、簡単なテストをいくつか実行し始めました。一見すると、その主張は確かに正しいです。予備的な結果は次のとおりです (ただし、読み進めてください)。
- 実行時間 SLF4J、バックエンド Logback: 1515
- 実行時間 SLF4J、バックエンド JUL: 12938
- 実行時間 JUL: 16911
上記の数字はミリ秒なので、少ないほど良いです。したがって、10 倍のパフォーマンスの違いは、まず実際にかなり近いです。私の最初の反応は、「それは大きいですね !」でした。
これがテストの核心です。ご覧のとおり、整数と文字列がループ内で構築され、ログ ステートメントで使用されます。
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(ログ ステートメントに、プリミティブ データ型 (この場合は int) とより複雑なデータ型 (この場合は String) の両方を持たせたかったのです。重要かどうかはわかりませんが、これで解決しました。)
SLF4J のログ ステートメント:
logger.info("Logging {} and {} ", i, someString);
JUL のログ ステートメント:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
実際の測定を行う前に、同じテストを 1 回実行して JVM を「ウォームアップ」しました。Windows 7 では Java 1.7.03 が使用されました。最新バージョンの SLF4J (v1.6.6) と Logback (v1.0.6) が使用されました。Stdout と stderr は null デバイスにリダイレクトされました。
しかし、注意してみると、JUL はデフォルトで出力にソース クラス名を印刷しますが、Logback は印刷しないため、JUL がほとんどの時間を費やしていることがわかりましたgetSourceClassName()
。つまり、まったく異なるものを比較していることになります。テストを再度実行し、ログ記録の実装を同様の方法で構成して、実際に同じ内容を出力するようにする必要があります。ただし、SLF4J+Logback は依然としてトップになると思われますが、上記の初期の数値からは程遠いものになるでしょう。お楽しみに。
ちなみに、このテストは、私が実際に SLF4J または Logback を扱った初めての経験でした。楽しい経験でした。JUL は、初心者には確かにあまり歓迎されません。
パフォーマンスのテスト(パート2)
(このセクションは 2012 年 7 月 8 日に nolan600 によって追加されました)
結局のところ、JUL でパターンをどのように構成するか、つまりソース名が含まれているかどうかは、パフォーマンスにはあまり関係ありません。非常に単純なパターンで試してみました。
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
そして、上記のタイミングはまったく変わりませんでした。私のプロファイラーは、これが私のパターンの一部ではなかったとしても、ロガーが呼び出しに多くの時間を費やしていることを明らかにしましたgetSourceClassName()
。パターンは関係ありません。
したがって、パフォーマンスの問題に関しては、少なくともテスト済みのテンプレート ベースのログ ステートメントについては、JUL (低速) と SLF4J+Logback (高速) の実際のパフォーマンスの違いはおよそ 10 倍あると思われると結論付けています。まさに Ceki が言ったとおりです。
また、もう 1 つ、SLF4J の呼び出しは JUL の呼び出しよりもコストがかなり高いこともわかりますgetLogger()
(プロファイラーが正確であれば、95 ミリ秒対 0.3 ミリ秒)。これは理にかなっています。SLF4J は、基礎となるログ実装のバインディングにいくらか時間を費やす必要があります。これは私にとっては心配ではありません。これらの呼び出しは、アプリケーションの存続期間中にはあまり発生しないはずです。高速性は、実際のログ呼び出しにあるはずです。
最終結論
(このセクションは 2012 年 7 月 8 日に peterh によって追加されました)
皆さんの回答に感謝します。当初考えていたこととは反対に、最終的には API に SLF4J を使用することにしました。これは、いくつかのことと皆さんの意見に基づいています。
デプロイメント時にログ実装を柔軟に選択できます。
アプリケーション サーバー内で実行する場合の JUL 構成の柔軟性の欠如に関する問題。
SLF4J は、上で説明したように、特に Logback と組み合わせると、はるかに高速になります。これは単なる大まかなテストだったとしても、SLF4J+Logback の最適化には、JUL よりも多くの努力が注がれていると信じる理由があります。
ドキュメント。SLF4J のドキュメントは、はるかに包括的かつ正確です。
パターンの柔軟性。テストを行う際、JUL が Logback のデフォルト パターンを模倣するように設定しました。このパターンにはスレッド名が含まれています。JUL ではこれをそのままでは実行できないことがわかりました。これまではこれを見逃したことはありませんでしたが、ログ フレームワークに欠けているべきものではないと思います。以上です。
今日の Java プロジェクトのほとんど (または多く) は Maven を使用しているため、依存関係を追加することは、その依存関係がかなり安定している場合、つまり API が頻繁に変更されていない場合は特に、それほど大きな問題ではありません。これは SLF4J にも当てはまるようです。また、SLF4J の jar とその仲間はサイズが小さいです。
そこで起こった奇妙なことは、SLF4J を少し扱った後、JUL にかなり腹を立てたということです。JUL がこうならざるを得なかったことを今でも後悔しています。JUL は完璧からは程遠いですが、ある程度は機能します。ただ、十分ではないのです。同じことがProperties
例にも言えますが、私たちはそれを抽象化して、ユーザーが独自の構成ライブラリなどをプラグインできるようにすることを考えていません。その理由は、SLF4J がProperties
基準をわずかに上回っているのに対し、今日の JUL ではその逆であるからだと思います... 過去には存在しなかったため、0 でした。
最終最終結論(多分)
(このセクションは 2022 年 10 月 2 日に peterh によって追加されました)
Java 9では、システム.ロガーこれは、ロギング実装のファサードとして意図されています。したがって、私の知る限り、これは SLF4J と競合しますが、JDK に含まれているという利点があります。したがって、ライブラリ開発者は SLF4J ではなく System.Logger を使用する必要があるのではないでしょうか。
私は見つけたこのブログ投稿Renato Athaydes がそれを非常にうまく説明してくれました。(ちなみに、Renato が言及した Log4j-v2 ブリッジのバグは修理済みLog4j v2 の v2.13.2 を使用)
ベストアンサー1
免責事項: 私は log4j、SLF4J、logback プロジェクトの創設者です。
SLF4Jを好む客観的な理由があります。まず、SLF4Jではエンドユーザーが基盤となるログフレームワークを自由に選択できます。さらに、より知識のあるユーザーは、log4jを超える機能を提供するlogback、jul ははるかに遅れています。機能的には、jul は一部のユーザーにとっては十分かもしれませんが、他の多くのユーザーにとっては十分ではありません。簡単に言えば、ログ記録が重要な場合は、基盤実装として logback を備えた SLF4J を使用することをお勧めします。ログ記録が重要でない場合は、jul で十分です。
しかし、OSS 開発者としては、自分自身の好みだけでなく、ユーザーの好みも考慮する必要があります。つまり、SLF4J が jul よりも優れていると確信しているからではなく、現在 (2012 年 7 月) のほとんどの Java 開発者がログ API として SLF4J を好んでいるから、SLF4J を採用すべきなのです。最終的に世論を気にしないことに決めた場合は、次の事実を考慮してください。
- jul を好む人は、jul が JDK にバンドルされているという利便性からそうしています。私の知る限り、jul を支持する客観的な議論は他にありません。
- あなた自身の jul に対する好みは、単なる好みです。
したがって、「厳しい事実」を世論よりも優先することは、一見勇敢なことのように見えるが、この場合、それは論理的誤りである。
それでも納得できない場合は、JBニゼットさらに強力な議論を展開する。
ただし、エンド ユーザーは、自分のコード、または log4j や logback を使用する別のライブラリで、すでにこのカスタマイズを行っている可能性があります。jul は拡張可能ですが、logback、jul、log4j、および他のどのロギング フレームワークを拡張する必要があるかは不明です (4 つの異なるロギング フレームワークを使用する 4 つのライブラリを使用しているため)。これは面倒です。SLF4J を使用すると、ユーザーが選択したロギング フレームワークではなく、必要なロギング フレームワークを構成できるようになります。一般的なプロジェクトでは、自分のライブラリだけでなく、無数のライブラリが使用されることに注意してください。
もし何らかの理由でSLF4J APIが嫌いで、それを使うと仕事の楽しさが失われてしまうのであれば、ぜひともjulを選んでください。結局のところ、7月をSLF4Jにリダイレクト。
ちなみに、jul のパラメータ化は SLF4J よりも少なくとも 10 倍遅く、最終的には顕著な違いが生じます。