人生で初めて、オープンソース化される Java API を作成する立場にいることに気づきました。他の多くのプロジェクトに含まれることを願っています。
ロギングについては、私 (そして実際に私が一緒に働いている人々) は常に JUL ( java.util.logging
) を使用しており、問題が発生したことはありません。ただし、API 開発のために何をすべきかをより詳細に理解する必要があります。私はこれについていくつかの調査を行いましたが、私が得た情報では、さらに混乱するだけです. したがって、この投稿。
私はJUL出身なので、それに偏っています。残りの私の知識はそれほど大きくありません。
私が行った調査から、人々が JUL を好まない理由を次のように思いつきました。
「私は、Sun が JUL をリリースするずっと前に Java で開発を始めました。新しいことを学ぶよりも、logging-framework-X を使い続ける方が簡単でした」 . うーん。冗談ではありません、これは実際に人々が言うことです。この議論により、私たちは皆 COBOL を行っている可能性があります。(しかし、私はこれが怠惰な男であることを確かに関連付けることができます)
「JUL のロギング レベルの名前が気に入らない」 . わかりました、真剣に、これは新しい依存関係を導入する理由として十分ではありません.
「JUL からの出力の標準フォーマットが気に入らない」 . うーん。これは単なる構成です。コードに関して何もする必要さえありません。(確かに、昔は独自の Formatter クラスを作成して正しく処理する必要があったかもしれません)。
「logging-framework-X も使用する他のライブラリを使用しているので、そちらを使用する方が簡単だと思いました」 . これは循環的な議論ですね。「全員」がJULではなくlogging-framework-Xを使用するのはなぜですか?
「他の誰もが 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 のパフォーマンスが良くないことを示唆するものは何も見つかりませんでした。
classpath からの構成。デフォルトの JUL は、クラスパスから構成ファイルをロードできません。そうするのは数行のコードです。これが煩わしい理由はわかりますが、解決策は短くて簡単です。
出力ハンドラの可用性。JUL には、すぐに使用できる 5 つの出力ハンドラー (コンソール、ファイル ストリーム、ソケット、およびメモリ) が付属しています。これらは拡張することも、新しいものを作成することもできます。これは、たとえば、UNIX/Linux Syslog および Windows イベント ログへの書き込みである可能性があります。私は個人的にこの要件を持ったことはなく、使用されているのを見たこともありませんが、それが便利な機能である理由は確かに理解できます. Logback には、たとえば Syslog 用のアペンダーが付属しています。それでも私はそれを主張します
- 出力先のニーズの 99.5% は、JUL のすぐに使えるものでカバーされます。
- 特別なニーズは、他の何かの上ではなく、JUL の上にあるカスタム ハンドラーによって満たされる可能性があります。JUL の Syslog 出力ハンドラーを作成するのに、別のロギング フレームワークの場合よりも時間がかかることを示唆するものは何もありません。
何か見落としがあるのではないかと、とても心配です。JUL 以外のロギング ファサードとロギング実装の使用は非常に広まっているため、理解していないのは私だけだという結論に達する必要があります。恐れながら、それは初めてではないでしょう。:-)
では、API をどうすればよいでしょうか? 成功させたいです。もちろん、「流れに乗って」SLF4J(最近最も人気があるようです)を実装することもできますが、私自身のために、すべての混乱を正当化する今日のJULの何が問題なのかを正確に理解する必要がありますか?ライブラリに JUL を選択することで、自分自身を妨害することになりますか?
テストのパフォーマンス
(2012 年 7 月 7 日に nolan600 によって追加されたセクション)
SLF4J のパラメータ化が JUL の 10 倍以上高速であることについて、以下の Ceki からの参照があります。だから私はいくつかの簡単なテストを始めました。一見すると、その主張は確かに正しい。暫定的な結果は次のとおりです (ただし、読み進めてください!)。
- 実行時間 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});
JVM は、実際の測定が行われる前に 1 回実行された同じテストで「ウォームアップ」されました。Windows 7 では Java 1.7.03 を使用しました。最新バージョンの SLF4J (v1.6.6) と Logback (v1.0.6) を使用しました。stdout と stderr が null デバイスにリダイレクトされました。
ただし、注意してください。JUL はgetSourceClassName()
デフォルトで出力にソース クラス名を出力しますが、Logback は出力しないため、JUL がほとんどの時間を費やしていることがわかりました。そこで、リンゴとオレンジを比較しています。テストを再度実行し、ロギングの実装を同様の方法で構成して、実際に同じものが出力されるようにする必要があります。ただし、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 倍あると結論付けています。ちきさんのおっしゃる通りです。
getLogger()
また、SLF4J の呼び出しは JUL の同上よりもはるかに高価であるという別のこともわかります。(プロファイラーが正確な場合は 95 ミリ秒対 0.3 ミリ秒)。意味あり。SLF4J は、基礎となるロギング実装のバインドにしばらく時間がかかります。これは私を怖がらせません。これらの呼び出しは、アプリケーションの存続期間中はほとんど発生しません。堅牢性は、実際のログ呼び出しに含まれている必要があります。
最終的な結論
(2012 年 7 月 8 日に nolan600 によって追加されたセクション)
ご回答ありがとうございます。私が当初考えていたこととは反対に、API に SLF4J を使用することにしました。これは、多くのこととあなたの入力に基づいています。
デプロイ時にログの実装を柔軟に選択できます。
アプリケーション サーバー内で実行する場合の JUL の構成の柔軟性の欠如に関する問題。
SLF4J は、上で詳述したように、特に Logback と組み合わせると、確かにはるかに高速です。これが単なる大まかなテストであったとしても、JUL よりも SLF4J+Logback の最適化に多くの労力が費やされたと信じる理由があります。
ドキュメンテーション。SLF4J のドキュメントは、より包括的で正確です。
パターンの柔軟性。テストを行ったとき、JUL が Logback のデフォルト パターンを模倣するように設定しました。このパターンには、スレッドの名前が含まれます。JUL はそのままではこれを行うことができないことがわかりました。わかりました、今まで見逃したことはありませんが、ログ フレームワークに欠けているべきものではないと思います。限目!
現在、ほとんどの (または多くの) Java プロジェクトは Maven を使用しているため、依存関係を追加することはそれほど大きなことではありません。これは SLF4J にも当てはまるようです。また、SLF4J jar とその仲間はサイズが小さいです。
奇妙なことに、SLF4J で少し作業した後、実際に JUL に腹を立てました。JULもこうじゃなきゃいけないのが今でも悔やまれます。JUL は完璧にはほど遠いですが、ある程度は機能します。十分ではありません。例として同じことが言えProperties
ますが、抽象化については考えていません。そのため、人々は独自の構成ライブラリと何を持っているかをプラグインできます。その理由は、バーのすぐ上に来るのに対し、今日の JUL にはその逆が当てはまるためだと思いProperties
ます...そして過去には存在しなかったため、ゼロになりました。