1. ホーム

[解決済み】なぜjava.util.loggingを使用しないのですか?

2022-07-02 12:51:20

質問

私の人生で初めて、私はオープンソース化されるJava APIを書く立場にいることに気づきました。うまくいけば、他の多くのプロジェクトに含まれることになります。

ロギングのために、私は (そして実際に私が一緒に働いている人たちも) 常に JUL (日本語) を使用してきました。 java.util.logging を使用してきましたが、これについては何の問題もありませんでした。しかし今、私は自分の API 開発のために何をすべきか、より詳細に理解する必要があります。この件についていくつか調べてみましたが、情報を得るにつれ、ますます混乱するばかりです。そのため、この投稿を行いました。

私はJUL出身なので、それについては偏っています。残りの部分についての私の知識はそれほど大きくはありません。

私が調べたところ、JULが嫌われる理由は以下のようなものでした。

  1. 私は Sun が JUL をリリースするずっと前から Java で開発を始めており、新しいことを学ぶよりも logging-framework-X を使い続ける方が簡単だったのです。 . うーん。冗談ではなく、実際にこういうことを言う人がいるんです。この論法だと、私たちはみんなCOBOLをやっているかもしれませんね。(ただし、私も怠け者なので、確かに共感できます)

  2. "JUL"のロギングレベルの名前が好きではありません。 . OK、真剣に、これは新しい依存関係を導入する十分な理由ではありません。

  3. "JUL"からの出力の標準フォーマットが好きではありません。 . うーん。これは単なる設定です。コード的には何もする必要すらありません。(確かに、昔はFormatterクラスを自作しないとうまくいかなかったかもしれませんね)。

  4. logging-framework-Xを使用する他のライブラリも使用しているので、そちらを使用する方が簡単だと思いました。 . これは循環論法ではないでしょうか?なぜ「みんな」logging-framework-Xを使っていて、JULを使っていないのでしょうか?

  5. "他のみんなはlogging-framework-X"を使用しています。 . これは私にとって、上記の特殊なケースに過ぎません。多数派が常に正しいとは限りません。

だから、本当の大問題は なぜJULではないのか? . 私が見逃しているものは何でしょうか?ロギングファサード(SLF4J、JCL)の存在意義は、複数のロギング実装が歴史的に存在したことで、その理由は私の見るところ、JUL以前の時代までさかのぼります。もしJULが完璧だったら、ロギングファサードは存在しないのでしょうか?JUL はある程度ファサードそのものであり、ハンドラ、フォーマッタ、さらには LogManager を交換することができます。

同じこと (ロギング) を行う複数の方法を受け入れるのではなく、それらがそもそもなぜ必要であったのかを問うべきではないでしょうか。(そして、それらの理由がまだ存在するかどうかを確認する)

OK、これまでの私の研究は、以下のようないくつかのことを導き出しました。

本当の問題 であることがわかりました。

  1. 性能 . 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のパフォーマンスが競合他社に比べて良くないと示唆するようなものは見つかりませんでした。

  2. クラスパスからの設定 . JULはクラスパスから設定ファイルを読み込むことができません。それは の数行のコード という数行のコードを書くだけで、読み込むことができるようになります。これが煩わしいのはわかりますが、解決策は短くて簡単です。

  3. 出力ハンドラの利用可能性 . JULは、コンソール、ファイルストリーム、ソケット、メモリの5つの出力ハンドラをすぐに利用できます。これらは拡張したり、新しいものを書くことができます。例えば、UNIX/LinuxのSyslogやWindowsのイベントログに書き込むような場合です。私自身はこのような要求があったことも、使われているのを見たこともありませんが、便利な機能であることは間違いないでしょう。LogbackにはSyslog用のアペンダーが付属しています。それでも、私は次のように主張します。

    1. 出力先に対するニーズの 99.5% は、JUL のアウトオブボックスにあるものによってカバーされています。
    2. 特別なニーズには、何か他のものの上ではなく、JUL の上のカスタム ハンドラで対応することができるかもしれません。JUL 用の Syslog 出力ハンドラを書くのに他のロギングフレームワークよりも時間がかかるというようなことは、私にはありません。

何か見落としていることがあるのではないかと、とても心配です。JUL 以外のロギング ファサードとロギング実装の使用は非常に広く普及しているので、私が理解していないだけだという結論に達しました。それは初めてではないので、心配です。)

では、APIをどうすればいいのでしょうか? 成功させたい。 もちろん、流れに任せて SLF4J を実装することもできますが (最近最も人気があるようです)、私自身のためにも、今日の JUL の何が問題で、大騒ぎになっているのか、正確に理解する必要があります。 私のライブラリにJULを選択することで、私は自分自身を妨害しているのでしょうか?

パフォーマンスのテスト

(セクションは 07-JUL-2012 に 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 
        }
    }

(私はlog文に原始的なデータ型(この場合int)とより複雑なデータ型(この場合String)の両方を持たせたかったのです。それが重要であるかどうかはわかりませんが、そこにあるのです)。

SLF4Jのlogステートメントです。

logger.info("Logging {} and {} ", i, someString);

JULのログ文です。

logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});

実際の測定が行われる前に、同じテストが一度実行され、JVMは「ウォームアップ」されました。Windows 7 上で Java 1.7.03 が使用されました。SLF4Jの最新版(v1.6.6)とLogbackの最新版(v1.0.6)を使用しています。標準出力と標準エラー出力はNULLデバイスにリダイレクトされました。

しかし、今注意してみると、JUL はそのほとんどの時間を getSourceClassName() なぜなら、JUL はデフォルトで出力にソースクラス名を表示しますが、Logback はそうしないからです。つまり、私たちはリンゴとオレンジを比較しているのです。もう一度テストをして、ログの実装を同じように設定し、実際に同じものを出力するようにしなければなりません。しかし、SLF4J+Logbackはまだ上位に来るとは思いますが、上記のような最初の数字からは程遠いでしょう。ご期待ください。

今回のテストは、SLF4JやLogbackと実際に仕事をするのは初めてでした。楽しい経験でした。JULは確かに始めたばかりの頃は歓迎されませんね。

パフォーマンスのテスト(その2)

(セクションは 08-JUL-2012 に nolan600 によって追加されました)

結局のところ、JUL でパターンをどのように構成するか、つまりソース名を含むか含まないかは、パフォーマンスにとってあまり重要ではありません。非常に単純なパターンで試してみました。

java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"

を追加しましたが、上記のタイミングは全く変わりませんでした。プロファイラで調べたところ、ロガーはまだ多くの時間を getSourceClassName() の呼び出しに多くの時間を費やしていることがわかりました。パターンは重要ではありません。

したがって、パフォーマンスの問題については、少なくともテストしたテンプレート ベースのログ ステートメントでは、JUL (遅い) と SLF4J+Logback (速い) の間で実際のパフォーマンスにおよそ 10 倍の差があるようだと結論付けています。Cekiさんがおっしゃるとおりです。

また、もうひとつ、SLF4J の getLogger() の呼び出しは、JUL の同上よりもずっと高いです。(私のプロファイラが正確であれば、95 ms 対 0.3 ms)。これは理にかなっています。SLF4Jは基礎となるロギング実装のバインディングに時間をかけなければなりません。これは私を脅かすものではありません。このような呼び出しはアプリケーションの寿命の中では稀なはずです。速さは実際のログ呼び出しにあるはずです。

最終的な結論

(セクションは08-JUL-2012にnolan600によって追加されました)

すべての回答ありがとうございました。当初考えていたこととは逆に、結局、私のAPIにはSLF4Jを使うことにしました。これは、多くの事柄と皆さんの意見に基づくものです。

  1. デプロイ時にログの実装を柔軟に選択できるようになります。

  2. アプリケーションサーバー内で実行される場合、JULの設定の柔軟性に欠ける問題。

  3. SLF4J は、特に Logback と組み合わせた場合、上記のように確かにかなり高速になります。これが単なる大まかなテストであったとしても、JUL よりも SLF4J+Logback の方がより多くの最適化努力がなされていると信じるに足る根拠があります。

  4. ドキュメンテーション SLF4Jのドキュメントは、単純に、より包括的で正確です。

  5. パターンの柔軟性。テストを行うにあたり、私はJULにLogbackのデフォルトパターンを模倣させることにしました。このパターンには、スレッドの名前が含まれています。しかし、JUL はこれをそのまま実行することができないことがわかりました。しかし、これはログフレームワークに欠けてはならないものだと思います。ピリオド!

  6. 今日、ほとんどの(あるいは多くの)JavaプロジェクトはMavenを使っているので、依存関係を追加することは、特にその依存関係がむしろ安定している場合、つまり、そのAPIを常に変更しない場合、それほど大きなことではありません。これはSLF4Jにも当てはまると思われます。また、SLF4Jのjarとその仲間はサイズが小さいです。

というわけで、不思議なことに、SLF4Jで少し仕事をした後、実はJULにかなり怒られてしまったのです。JULでこのようなことになってしまったことを、私は今でも後悔しています。JULは完璧というにはほど遠いのですが、一応仕事はします。ただ、十分とは言えません。同じことが Properties についても同じことが言えますが、私たちはそれを抽象化して、人々が自分の設定ライブラリなどを差し込めるようにしようとは考えていません。その理由は Properties がバーのすぐ上に来るのに対して、今のJULは逆で......昔は存在しなかったからゼロで来たんです。

どのように解決するのですか?

免責事項 : 私はlog4j、SLF4Jとlogbackプロジェクトの創設者です。

SLF4Jを好むのには客観的な理由があります。ひとつは SLF4Jでは、エンドユーザーが基礎となるロギングフレームワークを自由に選択することができます。 . さらに、より経験豊富なユーザは log4j を超える機能を提供する logback を好む傾向があり、j.u.lはずっと後ろに落ちています。機能的にj.u.lは、あるユーザーにとって十分かもしれませんが、多くの他の人にとって、それはちょうどではありません。一言で言えば、もしロギングがあなたにとって重要であれば、基礎となる実装としてlogbackとSLF4Jを使いたいでしょう。ロギングが重要でない場合は、j.u.lでよいでしょう。

しかし、ossの開発者としては、自分の好みだけでなく、ユーザーの好みを考慮する必要があります。その結果、SLF4Jを採用すべきなのは あなたが SLF4J が j.u.l よりも優れていると確信しているからではなく、現在(2012年7月)、ほとんどの Java 開発者がロギング API として SLF4J を好んでいるからです。もし最終的にあなたが大衆の意見を気にしないと決めたのなら、次の事実を考えてみてください。

  1. を好む人は、j.u.l が JDK にバンドルされているため、便利だからそうしているのです。私の知る限り、j.u.l を支持する他の客観的な論拠はありません。
  2. あなた自身の j.u.l への好みは、まさにそれです。 好み .

このように、世論よりも確固たる事実を優先させることは、一見勇敢に見えますが、この場合、論理的な誤りです。

まだ納得がいかないなら JB Nizet は、さらに強力な議論を展開しています。

エンドユーザーがすでに彼自身のコードのためにこのカスタマイズを行うことができたことを除いては。 j.u.lは拡張可能ですが、logback、j.u.l、log4j、またはlogbackを使用する別のライブラリのために、このカスタマイズを行うことができました。 j.u.lは拡張可能ですが、logback、j.u.l、log4j、および神のみぞ知るを拡張しなければなりません。 j.u.l は拡張可能ですが、logback、j.u.l、log4j、そして神のみぞ知る他のロギング・フレームワークを拡張しなければなりません。 というのも、彼は4つの異なるロギングフレームワークを使用する4つのライブラリを使用しているからです。SLF4J を使用することで SLF4Jを使うことで、あなたが選んだフレームワークではなく、彼が望むロギングフレームワークを設定することができます。 あなたが選んだものではなく、彼が望むロギングフレームワークを設定できるようにします。 典型的なプロジェクトは、あなたのライブラリだけでなく、無数の ライブラリを使っていることを忘れないでください。 .

もし、何らかの理由でSLF4J APIが嫌いで、それを使うことで仕事の楽しみが半減するのであれば、ぜひともj.u.l.を使ってください。 j.u.lをSLF4Jにリダイレクトする手段があります。 .

ちなみに、j.u.lのパラメトリゼーションはSLF4Jのものより10倍以上遅いので、結局は顕著な差になってしまいます。