とにかくファイルにログを書く
 

まずはJDK1.4でログ出力してみます

java.util.loggingパッケージを使うと、ファイルIOの操作を自前でしなくてもログ出力を実現できます。
1.ログを出力するには?
2.ログをファイルに書き出すには?
3.ファイルの出力書式を読みやすくする
4.出力レベルを変更する
5.例外のスタックトレースを出力する
6.追加モードで出力する
7.関連リンク

1. ログを出力するには?

ちょっと長いのですが、以下のソースをご覧ください。この項で使うのはtest1( )メソッドです。

LoggerTester.java
package ytp.test.logging;

import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.FileHandler;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 *  JDK1.4から導入されたログ出力機構をテストするクラスです。
 *  単純に画面/ファイルに出力するだけです。
 */
public class LoggerTester {
    /**
     *  ログファイル名です。
     */
    public static final String LOGFILE = "LoggerTester.log";
    
    /**
     *  JDKのロガー(ログ出力クラス)です。
     */
    private Logger logger = null;
    
    /**
     *  デフォルトコンストラクタです。
     *  自分のインスタンスをもとにクラス名を取得し、
     *  そのクラス名によるLoggerオブジェクトを取得します。
     */
    public LoggerTester() {
        logger = Logger.getLogger(this.getClass().getName());
    }
    
    /**
     *  メインです。
     *  LoggerTesterインスタンスを生成し、
     *  起動パラメータで指定されたメソッドを呼び出します。
     */
    public static void main(String[] args) {
        LoggerTester test = new LoggerTester();
        try {
            // 起動パラメータで指定されたメソッドを呼び出す
            Method getter = test.getClass().getMethod(args[0], null);
            getter.invoke(test, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     *  画面にのみ出力します。
     */
    public void test1() {
        // ログを出力する
        this.out(logger);
    }
    
    /**
     *  画面とファイルに出力します。
     */
    public void test2() {
        try {
            // 出力ファイルを指定する
            FileHandler fh = new FileHandler(LOGFILE);
            logger.addHandler(fh);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // ログを出力する
        this.out(logger);
    }
    
    /**
     *  人間が読みやすいフォーマットでファイルに出力します。
     */
    public void test3() {
        try {
            // 出力ファイルを指定する
            FileHandler fh = new FileHandler(LOGFILE);
            // 出力フォーマットを指定する
            fh.setFormatter(new java.util.logging.SimpleFormatter());
            logger.addHandler(fh);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // ログを出力する
        this.out(logger);
    }
    
    /**
     *  人間が読みやすいフォーマットで、
     *  さらに出力レベルをCONFIG以上に設定してファイルに出力します。
     */
    public void test4() {
        try {
            // 出力ファイルを指定する
            FileHandler fh = new FileHandler(LOGFILE);
            // 出力フォーマットを指定する
            fh.setFormatter(new java.util.logging.SimpleFormatter());
            logger.addHandler(fh);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 出力レベルをCONFIG以上に設定する
        logger.setLevel(Level.CONFIG);
        
        // ログを出力する
        this.out(logger);
    }
    
    /**
     *  例外のスタックトレースを出力します。
     */
    public void test5() {
        try {
            // 出力ファイルを指定する
            FileHandler fh = new FileHandler(LOGFILE);
            // 出力フォーマットを指定する
            fh.setFormatter(new java.util.logging.SimpleFormatter());
            logger.addHandler(fh);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 出力レベルをCONFIG以上に設定する
        logger.setLevel(Level.CONFIG);
        
        try {
            makeException();
        } catch (NullPointerException e) {
            // スタックトレースも出力できます
            logger.log(Level.SEVERE, "どうする〜", e);
        }
    }
    
    /**
     *  追加モードでファイルに出力します。
     */
    public void test6() {
        try {
            // 出力ファイルを追加モードで指定する
            FileHandler fh = new FileHandler(LOGFILE, true);
            // 出力フォーマットを指定する
            fh.setFormatter(new java.util.logging.SimpleFormatter());
            logger.addHandler(fh);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 出力レベルをCONFIG以上に設定する
        logger.setLevel(Level.CONFIG);
        
        // ログを出力する
        this.out(logger);
    }
    
    private void out(Logger logger) {
        logger.log(Level.CONFIG, "デバッグ");
        logger.log(Level.INFO, "単なるおまけ");
        logger.log(Level.WARNING, "ちょっとまずいんちゃう?");
        logger.log(Level.SEVERE, "えらいこっちゃ!!");
    }
    
    private void makeException() {
        String s = null;
        s.toString();
    }
}

JDKログ出力機構の中心となるのがjava.util.logging.Loggerクラスです。
上記ソースのうち、基本部分は次のコードです。
Loggerの基本コード
private Logger logger = null;
logger = Logger.getLogger(this.getClass().getName());
logger.log(Level.SEVERE, "えらいこっちゃ!!");
Logger.getLogger( )にて、デフォルトのLoggerオブジェクトを取得できます。 パラメータには、自分のクラス名を渡します。
実際にログを出力するのがlog( )メソッドで、出力レベルログメッセージをパラメータで渡します。
test1( )を実行すると次のように画面表示されます。
test1( )の実行結果
C:\Source\Java>java  ytp.test.logging.LoggerTester test1
2002/10/20 17:10:11 ytp.test.logging.LoggerTester out
情報: 単なるおまけ
2002/10/20 17:10:12 ytp.test.logging.LoggerTester out
警告: ちょっとまずいんちゃう?
2002/10/20 17:10:12 ytp.test.logging.LoggerTester out
致命的: えらいこっちゃ!!

C:\Source\Java>
日付時刻とともに、ログを出力しているクラス名とそのメソッド名が出力され、 それに続けてログのレベルとメッセージが出力されます。
およっ? メッセージの数が一つ足りませんねぇ。 ま、細かいことは気にせず次に行きましょう!!

2. ログをファイルに書き出すには?

画面に出すだけではあまり役に立ちませんよね。やっぱり男はファイルに出力!!
test2( )メソッドをご覧ください。
FileHandlerの指定
try {
    // 出力ファイルを指定する
    FileHandler fh = new FileHandler(LOGFILE);
    logger.addHandler(fh);
} catch (IOException e) {
    e.printStackTrace();
}
というコードが追加されています。
java.util.logging.FileHandlerクラスを使うことでファイルにも出力できるようになります。
この例では、実行ディレクトリのLoggerTester.logというファイルに書き出されます。 実行結果を見てみましょう。
LoggerTester.log
<?xml version="1.0" encoding="MS932" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
  <date>2002-10-20T16:36:06</date>
  <millis>1035099366972</millis>
  <sequence>0</sequence>
  <logger>ytp.test.logging.LoggerTester</logger>
  <level>INFO</level>
  <class>ytp.test.logging.LoggerTester</class>
  <method>out</method>
  <thread>10</thread>
  <message>単なるおまけ</message>
</record>
<record>
  <date>2002-10-20T16:36:07</date>
  <millis>1035099367102</millis>
  <sequence>1</sequence>
  <logger>ytp.test.logging.LoggerTester</logger>
  <level>WARNING</level>
  <class>ytp.test.logging.LoggerTester</class>
  <method>out</method>
  <thread>10</thread>
  <message>ちょっとまずいんちゃう?</message>
</record>
<record>
  <date>2002-10-20T16:36:07</date>
  <millis>1035099367102</millis>
  <sequence>2</sequence>
  <logger>ytp.test.logging.LoggerTester</logger>
  <level>SEVERE</level>
  <class>ytp.test.logging.LoggerTester</class>
  <method>out</method>
  <thread>10</thread>
  <message>えらいこっちゃ!!</message>
</record>
</log>
な、な、なんじゃこりゃー! XMLファイルになっとるやんけー!!
これではかえって何が書いてあるかわからんがなー・・・・

3. ファイルの出力書式を読みやすくする

JDKログの標準設定はXMLファイルになっていますが、普通のログ出力に切り替えるにはどうしたらいいのでしょうか?
test3( )メソッドをご覧ください。
SimpleFormatterの利用
// 出力フォーマットを指定する
fh.setFormatter(new java.util.logging.SimpleFormatter());
というコードが追加されています。
FileHandlerに対してjava.util.logging.SimpleFormatterクラスを設定することで、 画面と同じ書式でファイルに出力できるようになります。
LoggerTester.log
2002/10/20 16:36:32 ytp.test.logging.LoggerTester out
情報: 単なるおまけ
2002/10/20 16:36:32 ytp.test.logging.LoggerTester out
警告: ちょっとまずいんちゃう?
2002/10/20 16:36:32 ytp.test.logging.LoggerTester out
致命的: えらいこっちゃ!!
でも、出力されるログの数がまだ足りませんねぇ・・・・

4. 出力レベルを変更する

先程まで、ファイルに出力されるログが3つだけでした。これは、ログの出力レベルが原因です。
出力レベルというのはそのログの重要度を表すもので、 java.util.logging.Levelクラスで規定されています。
高いものから順に
  • SEVERE (最高値)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (最低値)
となっています。
JDKの標準設定ではログの出力レベルがINFOまでになっていて、それより低いものは出力されません。 これを変更する方法は、test4( )メソッドをご覧ください。
出力レベルの変更
// 出力レベルをCONFIG以上に設定する
logger.setLevel(Level.CONFIG);
というコードが追加されています。 setLevel( )メソッドを使うことで、出力レベルの指定が可能です。
LoggerTester.log
2002/10/20 16:36:56 ytp.test.logging.LoggerTester out
設定: デバッグ
2002/10/20 16:36:56 ytp.test.logging.LoggerTester out
情報: 単なるおまけ
2002/10/20 16:36:56 ytp.test.logging.LoggerTester out
警告: ちょっとまずいんちゃう?
2002/10/20 16:36:56 ytp.test.logging.LoggerTester out
致命的: えらいこっちゃ!!
ただし、これで変更されるのはファイルに対する出力レベルのみで、 画面への出力は変わりません。

5. 例外のスタックトレースを出力する

ログ出力が真価を発揮するのは、例外やエラーの発生時ですよね。
JDKログでも、この時のスタックトレースを書き出すことが当然可能です。 test5( )メソッドをご覧ください。
スタックトレースの出力
} catch (NullPointerException e) {
    // スタックトレースも出力できます
    logger.log(Level.SEVERE, "どうする〜", e);
}
というコードが追加されています。
log(Level, Throwable)メソッドを使うことで、 例外やエラーのスタックトレースを出力できます。
LoggerTester.log
2002/10/20 16:37:28 ytp.test.logging.LoggerTester test5
致命的: どうする〜
java.lang.NullPointerException
	at ytp.test.logging.LoggerTester.makeException(LoggerTester.java:144)
	at ytp.test.logging.LoggerTester.test5(LoggerTester.java:128)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at ytp.test.logging.LoggerTester.main(LoggerTester.java:42)
ところがまだ問題が・・・

6. 追加モードで出力する

実は、今まで動かしたプログラムでは、実行するたびにファイルが上書きされてしまい、 過去のログが消えてしまいます。
これでは実用性に欠けるので、追加モードで書き込めるようにしたのがtest6( )メソッドです。
追加モードの設定
// 出力ファイルを追加モードで指定する
FileHandler fh = new FileHandler(LOGFILE, true);
という部分が変更されています。
2つパラメータのあるFileHandlerのコンストラクタを使い、 2番目のパラメータにtrueを設定することで、 出力ファイルに追加モードで書き込むようになります。test6( )の実行結果が以下です。
LoggerTester.log
2002/10/21 0:10:08 ytp.test.logging.LoggerTester test5
致命的: どうする〜
java.lang.NullPointerException
	at ytp.test.logging.LoggerTester.makeException(LoggerTester.java:165)
	at ytp.test.logging.LoggerTester.test5(LoggerTester.java:129)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at ytp.test.logging.LoggerTester.main(LoggerTester.java:43)
2002/10/21 0:10:11 ytp.test.logging.LoggerTester out
設定: デバッグ
2002/10/21 0:10:11 ytp.test.logging.LoggerTester out
情報: 単なるおまけ
2002/10/21 0:10:11 ytp.test.logging.LoggerTester out
警告: ちょっとまずいんちゃう?
2002/10/21 0:10:11 ytp.test.logging.LoggerTester out
致命的: えらいこっちゃ!!

7.関連するJava2 SDKドキュメント

Loggerクラス
FileHandlerクラス

次はlogging.propertiesの設定です。


Copyright © 2002-2016, Your Technology Partner. All rights reserved.