java.util.logging.Loggerを使う上での注意点

java.util.logging.Loggerを初めて使ったのですが、ちょっとハマってしまった点などあったので紹介しておきます(主に設定ファイル=logging.propertiesまわり)。
○問題

  1. logging設定ファイルはシステムクラスローダによりロードされる
  2. logging設定ファイルでHandlerの設定をする際、クラス単位でしか設定できない
  3. ユーザコードで設定を変更しても、VM起動時に読み込んだ設定が有効になったまま
  4. ファイルにログを出力する際、Logger単位でファイルをロックしてしまう。

1. logging設定ファイルはシステムクラスローダによりロードされる


これは何が問題かというと、サーバ上で動かすアプリケーションで、Formatterを自作しようとしたとき問題となります(そして、こういった局面は結構あると思います)。
サーバ上で動かすアプリケーションの場合、通常、アプリケーションサーバが独自でクラスローダを持っています。そして、自分が作ったクラス(=ユーザクラス)というのはアプリケーションサーバのクラスローダによりロードされます。一方、logging設定ファイルは(デフォルトのものであろうと、起動パラメータで指定した場合であろうと)システムクラスローダによりロードされます。
このため、logging設定ファイルにユーザクラス(=Formatter)を記述している場合に、システムクラスローダがユーザクラスをロードできず、エラーとなってしまいます。ユーザクラス(=Formatter)をシステムライブラリに追加してやると、ロードできるのですが。

2. logging設定ファイルでHandlerの設定をする際、クラス単位でしか設定できない


例えば、FileHanderクラスを使ってログをファイルに出力する場合、logging設定ファイルに次のような指定をします。


java.util.logging.FileHandler.pattern = %h/application.log
これでは複数のファイルには出力できないですね、クラスに対して指定してしまってるので。
ちなみに、logging設定ファイルを使わずにアプリケーションコードから出力ファイルを指定する場合は、FileHandlerのインスタンスごとに指定できます(というかそれが普通ですね)。

○1. 2. の解決策

サーバアプリの場合は、設定ファイルは使用せず、コードで行う。
サーバ上で動作するコードで、ロガーのコンフィギュレーションをしましょう。そうすれば、クラスローダの都合でユーザクラスが読み込めない、といった問題もなく、また、Handlerの設定はインスタンスごとに行うことができます。

3. ユーザコードで設定を変更しても、VM起動時に読み込んだ設定が有効になったまま


JREを普通にインストールした状態だと、ルートロガーの出力レベルは、INFO以上となっています。そのため、ユーザコードでカスタムロガーのを作成したときに、うっかり出力レベルの指定を忘れていると、ルートの指定(=INFO以上)が有効になります。
僕はカスタムロガーの設定をする際に、Handlerの出力レベルはALLにしたのですが、ロガーの出力レベルを設定するのを忘れていたために、FINE以下のログが出力されず、2時間ぐらいハマってしまいました。

○3. の解決策
ロガーのコンフィギュレーションは注意深く行う。
期待した動作にならない場合は、デフォルトの設定が有効になっている可能性が高いです。そんなときはもう一度、自分が行った設定を見直してみましょう。

4. ファイルにログを出力する際、Logger単位でファイルをロックしてしまう


ロガーを複数定義し、それぞれから同じファイルにログを出力しようとすると、競合が発生してしまうみたいです。こうなった場合、ロガーは自動的に連番を振って、別ファイルにログを出力します。監視することを考えると、なるべくファイルに出力したいのですが。
回避策としては、ログを出力するたびに Handler.close() メソッドを呼び出すという方法もあるのですが、それはそもそもの使い方とは違っているような。

○4. の解決策

ファイルに出力するときは、共通親ロガーを使う。
複数のロガーから単一のファイルにログを出力することはできないので、代わりに複数のロガーの共通の親ロガーを作成します。そして、子の方のロガーでは何も指定せずに親に処理を委譲し、親の方では、通常どおりログをファイルに出力します。
こうすることで、ロガー:ファイルが1:1の関係になるので、競合を回避できます。ただし、複数のロガーに共通の親ロガーを作成できない場合は、いまのところ打つ手なしです。