java.util.logging - FileHandler.patternで指定したディレクトリを作成する

java.util.loggingはプロパティファイルでログを出力するハンドラ(Handler)の記述を行い、それを実行時のロガーに反映する。
ハンドラの中でも実業務の中核となるFileHandlerに関しては設定ファイルのデフォルトではコメントアウトされているが、簡単に使うことができる。

logging.properties(デフォルトではJAVA_HOME/jre/libにある)より抜粋

# To also add the FileHandler, use the following line instead.
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
↑ここのコメントアウトを外すことでFileHandlerが有効になる

.level= INFO

java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

デフォルトの記述では、%hつまりUSER_HOMEの配下にjavaXX.logXX(XXは世代番号)という名前のファイルが、アプリケーションの起動毎に作成されてログが記録されていることになる。(関係ないが、.formatterで指定されているデフォルトのフォーマッタ"java.util.logging.XMLFormatter"はXML形式で出力されるため、ログをツールにかけたり、XSLTを通すのではない場合は別なフォーマッタに切り替えたほうが良いと思う。)

今回の話題は"java.util.logging.FileHandler.pattern"というエントリについてだ。すでに書いたが、ロガーはこのエントリを読みこんでログを記録するファイルを決定する。上の例では%hにより、規定されたユーザディレクトリ配下が指定されているが、ここの記述は相対パスで書くこともできる。

例) カレントから見てlog/ディレクトリにファイルを作成する

java.util.logging.FileHandler.pattern = log/java%u.log

これで万々歳と思いきや、実行した際にlogという名前のディレクトリが直下に存在しないと、例外が発生してしまう。

Can't load log handler "java.util.logging.FileHandler"
java.io.IOException: Couldn't get lock for /log/java%u.log

予めディレクトリを作っておけば良い話だが、それも忘れる場合があることを考えて、実行時にディレクトリが存在していなかったら作ってしまうことにしよう。

ディレクトリは何時作れば良いか? 当たり前だが答えは「最初にロガーを使う直前」だ。実際には適当なクラスのスタティックコンストラクタなどで指定すればよいだろう。

try {
    LogManager man = LogManager.getLogManager();
    String pattern = man.getProperty(FileHandler.class.getName() + ".pattern");
    if ( pattern != null && !pattern.isEmpty()) {
        String[] tokens = pattern.split("\\/");
        for ( int i = 0; i < tokens.length-1; i++ ) {
            File file = new File(tokens[i]);
            if ( !file.isFile() && !file.exists()) {
                file.mkdir();
            }
        }
    }
} catch (SecurityException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

このコードは、java_home/jre/lib/logging.propertiesにFileHandlerの記述が存在していることを前提にしている。また、セキュリティマネジャの存在は一切考慮していないし、例外処理も適当だ。
なお、自身で用意して設定ファイルを読み込ませたい場合は、先頭の行を以下の3行に書き換える必要がある。

LogManager man = LogManager.getLogManager();
InputStream ins = Thread.currentThread().getContextClassLoader().getResource("設定ファイルへのパス").openStream();
man.readConfiguration(ins); //自身の設定ファイルを有効にする

このようにしておけば、アプリケーション毎に独立したロガーの設定が可能になる。