私は、特に人前で、他の人々の作業を批判することを全く不愉快であると考えています。 しかし、JDK 1.4で含まれるロギングAPIが「標準」とみなされる可能性がでてきてから、 私は反応することを強要されています。 JSR47への批評は、私一人だけではなく、グレッグ・デイビスは独自に一連のコメントを提示しています。
JDK 1.4のロギングAPIは、グレアム・ハミルトンによって率いられる JSR47 の努力の結果です。
詳細に入る前に、すこしばかり経緯を整理します。 私は、log4jプロジェクトの創設者です。 私は、専門家としてではありませんがJSR47 APIの仕様策定に参加しました。 1999年に、私はまだIBMで働いており、そして、ビッグ・ブルーはJSR47専門家グループにおいてメンバーとしてすでにクリス・バーロックがいました。 クリスは、IBMのJavaロギング・ツールキットの著者です。
表面上は、彼のツールキットは、重くJSR47 APIに影響しました。 特に、この2つは、同じ基本的なコンポーネントを共有しており、それらはloggers、 レベル、ハンドラー、そして、フォーマッタです。 log4jで、これらのコンポーネントは、同じように、カテゴリー、優先度、アペンダーと レイアウトと呼ばれています。 どちらも、同じ目的をもっています。 このように、用語としてloggerとカテゴリー、レベルと優先度、ハンドラーとアペンダー、フォーマッターとレイアウトは、以降のこの文書ので読み替えることができます。
ざっとしたレビューの後でさえ、log4jとJSR47 APIがとても類似したもので あることは、明白です。一例として、それらは名付階層に基づく唯一のロギングAPIです。 片方のAPIを理解していれば、もう一方の概念も用意に理解できるはずです。 しかし、違いがあります。
JSR47では、親loggerは、その子供を知っていますが、子供は親を知りません。
子供には、彼らの親とのリンクがありません。
例えば、"foo"という名前のloggerは、"foo.bar1"と
"foo.bar2"を知っています。
しかし、"foo.bar1"には、その親"foo"へのリンクがありません。
log4jでは、それは正反対となります。 log4jのカテゴリーは、その両親とのリンクがありますが、親にはその子供との リンクがありません。
一見しただけでは、これは平凡な実装詳細のように見えるかもしれませんが、 これは実際に非常に基本的なことです。
JSR47であなたがloggerのレベルを決めたとき、
たとえば、wombatという名前のものでは、
JSR47はwombatの下のツリーを横断します。
言い換えると、wombatから降りている全てのloggersのためのレベルは、上書きされます。
これは、大きいツリーのための非常に高価な操作でありえます。
とりわけ、root loggerのレベルを決める最も共通の場合です。
しかし、パフォーマンスは私が作るために試みている点ではありません。
log4jでは、カテゴリーの優先度を変えるには、一つのフィールドの変更で実施します。 子供カテゴリーは、上方へ階層ツリーを横断することによって動的に彼らの両親の優先度を受け継ぎます。
あなたが「foo」のためにレベルを設定する前にlogger「foo.bar1」の ためにレベルを設定するならば、それはJSR47でそれに追従します、 そして、必ずしもまるで「foo.bar1」を構成するための最初の指示が これまで存在しなかったように、後の指示は第一に上書きすることになります。 設定順序依存は、ショー・ストッパーでなくそれであるというは、 再三再四あなたを噛むことになる何かです。 対照的に、log4jでは、カテゴリーはどんな順序ででも設定することができます。 あなたは、設定順序について決して心配する必要はありません。
JSR47では、loggerはそのレベルを受け継ぐ階層を歩かずに、それのコピーを所有します。
残念なことに、JSR47 APIで、特に大きいツリーで、全ての受け継がれたハンドラーのはっきりしたベクトルを含むために各loggerにそうさせることはひどく高価であるので、ハンドラーは受け継ぐことができません。
JSR47によってこの問題を回避するには、グローバルなハンドラーを定義します。 loggerが、グローバルなハンドラーに記録して、直接それ自体にハンドラーにありました。 それは、階層からどんなハンドラーも受け継ぎません。
log4jで、アペンダは階層から累積的に受け継がれます。 カテゴリーは、その先祖に付けられるアペンダーと同様に それ自体に付けられるアペンダに記録することになります。 あなたが必要とする日まで、これはあまりハンドラー継承のようでないかもしれません; 多分、あなたは来週、ログAPIを採用することに決めるでしょう。
同じように、log4jリソースバンドルでは階層から継承されます。 JSR47で、リソース・バンドルは、個々に各loggerに付けられなければなりません。 リソース・バンドル継承が、JSR47にはありません。 実際問題として、これはあなたが名をつけられたlogger階層の国際化と利点のどちらかを選ばなければならないことを意味します。 それは、どちらか一方です。 国際化のサポートがJSR47 APIの主要な利点のうちの1つとしてサポートされるので、 この制限は特に驚くべきです。
JSR 47 では、 ALL, SEVERE,
WARNING, INFO, CONFIG,
FINE, FINER, FINEST,
OFFといったレベルを定義している。
経験では、ALLと OFF のレベルは
決して必要ではありません。
SEVERE と CONFIG レベルは、JSR47独自の
ものです。
3つのデバッグレベル FINE,
FINER, FINEST は、良い案に見えます。
しかし、どのレベルを使ったらよいのかを決定するのが面倒だと
いうことが、あなた自身によってすぐに分かるはずです。
グループ化は明確に無理です。
Log4j では、対照的に自明で限定された優先度の組があります:
FATAL, ERROR, WARN,
INFO, DEBUG。
JSR47もlog4jも、ユーザが優先度のセットを拡張することができます。 Log4jは、綱渡りと同様に設定ファイルの中で、優先度のサブクラスを サポートします。JSR47はこれができません。
Log4jは、コンソール、ファイル、Unix Syslogデーモン、Microsoft NTイベントログ、 リモートサーバー、JMSチャネル、Eメールの自動生成にログを送ることができるアペンダがあります。また、ログファイルをサイズや日付ごとに切り替えたり、非同期に記録することができます。
JSR47 は、コンソール、ファイル、ソケット、メモリバッファに記録することができます。
Log4j は、PatternLayoutと呼ばれる拡張性のある強力なレイアウト機能が
あります。JSR47は、選択肢として非常により弱いSimpleFormatterを提供します。
Log4jは、プロパティ・ファイルと同様に、XML文書による設定をサポートしています。
JSR47は、現在プロパティ・ファイルだけを認めています。
さらに、JSR47構成ファイルの言語は、非常に弱いです。
特に、ハンドラー・クラスの1つのインスタンスを構成することができるだけです。
この意味は、一度に1つのファイルだけに記録することができるということです。
log4jがJSR47と異なる多くの他の詳細が、あります。 たとえlog4jのコアが小さいとしても、プロジェクトはよくテストされたコードが 合計30,000行以上含まれます。 JSR47のコードはおよそ5,000行です。
Log4jは、何年もの間に、5人の活発な開発者(committers)のサポートを楽しんで、 何千ものプロジェクトにおいて使われています。 我々のサイトは、毎日ダウンロード回数が500回を超えており、その数は増えています。 Log4jは、C++とPythonに移植されました。 また、log4jを拡張した商用製品を提供している会社もあります。
log4jを使用しているオープンソース・プロジェクトまたはサイトの短いリストは、ここにあります。
ところで、log4jはJDK 1.1以降で問題なく動作します。 JSR 47は、JDK 1.4で、そして、JDK 1.4だけの環境で実行することになります。 とても面白いことに、JDK1.4で、JSR47APIを使用したパッケージは出荷していません。
Brian R. Gilstrapは、JDK 1.2とJDK 1.3で動作するようにJSR47 APIを書き換えました。彼は、また、JavaWorld誌の記事
で発表しました。
これは、非常に将来性があるのですが、JDK 1.3で実行するとき、java.util.loggingがjava.* namespaceの下にあるので、あなたはシステム的に出くわすことになります:
Exception in thread "main" java.lang.ExceptionInInitializerError: java.lang.SecurityException: Prohibited package name: java.util.logging
at java.lang.ClassLoader.defineClass(ClassLoader.java:477)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:248)
at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:286)
at java.lang.ClassLoader.loadClass(ClassLoader.java:253)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
Jochen Hillerは、彼がlog4jをラップしてJSR47 APIを実装したとき、 2001年の初めにこの問題を観察しました。
エラーがそれから発生するJSR 47でも、RunTimeException
はユーザーに投げられます、あるいは、内部のフィールドは決められます。
(ハンドラーだけの中の)
最初の場合には、RunTimeExceptionによって、
あなたのアプリケーションがクラッシュすることになります。
後者の場合、あなたはそのハンドラーのgetExceptionメソッドに
ついてたずねることによってハンドラーにおいて最後の捕えられた例外を
取り出すことができます。
後者が非実用的であるが、前者は全く容認できないです。
log4jで、どんな事情があっても、例外はユーザーに投げつけられます。
しかし、全てのappendersは、関連されたErrorHandlerを持ちます。
ハンドラーに特有のエラーが発生するときはいつでも、
このErrorHandlerはappenderによって呼び出されます。
デフォルトで、log4j appendersは、appenderにおける最初のエラーのために、
コンソールの上でメッセージを発するOnlyOnceErrorHandlerと関連されて、
エラーに追従している全てを無視しています。
ErrorHandlerは、任意のエラー取扱いポリシーを実装することができます。
例えば、データベースに書こうとして失敗したあと、JDBCAppenderは、
FileAppenderにフォールバックするために向け直すことができます。
この機能は、XML設定ファイルでサポートされます。
あなたは、クライアント・コードに1行たりとも変更を必要としません。
しかし、再び、誰がエラーを気にかけますかねぇ?
ログ・パフォーマンスは、明示的に3つの場合に分けて研究する必要があります。 ログ出力がオフにされるとき、 オンにされたときに優先度との比較、 そして実際のロギングの3つです。 ロギング・パフォーマンスの詳細な議論のためには、 log4j マニュアルを参照してください。 ログ出力がオンのとき、log4jはログ・ステートメントが有効かどうか、 よりゆっくり決めるためにおよそ3つのポイントがあります。 これは、それに階層を歩くことを要求するlog4jの動的な性質によります。 関係している図についてあなたに考えを与えるために、我々は800Mhzインテル・プロセッサの上で30ナノ秒の代わりに90ナノ秒について話しています。 言い換えると、100回の無効なログ要求は、両方の環境において1秒未満のコストがかかることになります。
出荷したバイナリでは、あなたは完全にログをオフにして、両方のAPIは、同じように 実行されることになります。 もし慎重に行なわなければ、出力禁止を呼び出す前のログ文のパラメータ構築のコストが 、他のどのパフォーマンス配慮も圧倒することになります。 あなたが使うことに決めるAPIに関係なく、例えば、ログ文を置く場所は、ソート・アルゴリズムにおいて要素交換命令の前か後にして、きついループに決して置くべきはありません。
log4jでは、呼出し側の位置の情報(行番号など)はオプションですが、 JSR47では、常に抽出されます。 呼出し側の位置の抽出は非常に遅い操作であるため、呼出し側の位置の情報が必要でない 場合には、log4jは、同じ情報を、4〜100倍速く記録します。
あなたがこれらの違いが重要であると感じるならば、 log4jをロギングAPIとしてJDK1.4とともに出荷するように 採用するようにSunに圧力をかけるときです。 JSR47がまだ達された最後仕様の段階ではないし、正式に承認されてないので、 まだJSR47 APIを修正することは可能なはずです。 これは、いったんJDK 1.4がされてしまうと非常難しくなります。 実際に、JSR47専門家グループさえ、この問題に関して意見が分かれます。
どうか java-logging-input@eng.sun.com に対して、あなたの礼儀正しくて個人化された要求を向けてください: 同時に Bcc: を cgu@qos.chに送ってください。
ところで、java-logging-input@eng.sun.comへの電子メールは、
JSR47専門家グループへ行かなくて、サン・マイクロシステムズに内部になります。
JSR47専門家グループ・メーリングリストのアドレスは、秘密の情報と思われます。
私は、私自身でそれを知りません。
Over one hundred individuals, including some from very large accounts, have written to Sun to express their concern, in their vast majority pushing for the adoption of log4j. Their names and the content of their request are listed below. I am very grateful for their support. Some of these requests are quite detailed and insightful.
非常に大きいアカウントをいくつかを含む、100以上の個人が、 log4jの採用を促進している大多数で、Sunに重要性を示すために書きました。 彼らの名前とその要求内容は、下にリストされています。 私は、彼らのサポートに非常に感謝します。 これらの要求の一部は、まったく詳細で、するどい洞察に満ちあふれています。
Jon Stevens
Court Demas and his follow up
Henrik Fredholm and his follow up to Graham.
Andy DePue and his follow up
Emily Bache and her follow up
Nate Sammons author of Protomatter Syslog
Balaji Kithiganahalli and his follow up
大部分の電子メール注意は、許可のもと複写されることに注意してください。 しかし、あなたの名前や要求した内容が公開されることで、あなたが不快に なるような場合には、ためらわずcgu@qos.chに連絡 してください。
Graham Hamiltonの 返答 の要旨は 「Merlinサイクルに遅れすぎる」です。これは、もちろん有効な重要性です。 彼も、説明しています:
APIを設計する際に、我々は多数のソースから汲み上げることを試みました、 そして、我々はサポートに必要条件の広い集合を試みました。
残念なことに、このAPIが引き起こしている騒ぎのため、結果として起こっているAPIが必要条件の広い集合に合致しそうにないことは、ますます明白になっています。 JSR47 APIの明白な失敗は、また、Javaコミュニティ・プロセスの性質の上で、影を落としています。 Michael C. Daconta は、JCPについての興味深いいくつかのコメントをしています。 全てが言われて、Javaは素晴らしいコンピューティング・プラットホームのままです。 Sunは、彼らの進行中の投資のために信用に値します。 彼らは、また、たくさんの驚くほど革新的なAPIを紹介しました。 しかし、誰もいつでも正しいとはかぎりません。
我々がコミュニティから得たサポートの量と質を与えられて、 私はJSR47 APIが撤回されるか、log4jと丸ごと置き換えられたしても驚きません。