JavaのSLF4J+Logbackの使い方について

スポンサーリンク

Javaのロガーと言えば「SLF4J + LOGBack」が主流になってるかと思いますが、log4jやらcommons-logging、インターフェースにアダプタや実装など大分複雑になっています。今回は他のロガーは置いといて「SLF4J + LOGBack」の使い方のみまとめておきたいと思います。

事前準備

 SLF4J Binary files LOGBack Download からAPIをダウンロードします。

解凍すると複数のjarファイルが含まれていますが、slf4j-api-1.7.12.jarをインターフェース、logback-classic-1.1.3.jarlogback-core-1.1.3.jarを実装として使用します。その他は今回の内容からは省きますが、アダプターやブリッジと呼ばれるものです。

SLF4J + LOGBackについて

Javaでロギングを行うためには、基本的に「インターフェース」と「実装」が必要になります。SLF4Jがインターフェース、LOGBackが実装です。

初めに  SLF4J Manual にもある通りHello Worldから試してみたいと思います。クラスパスには import しているslf4j-api-1.7.12.jarのみ追加しました。

package com.tasknotes.slf4j;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jSample {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Slf4jSample.class);
        logger.info("Hello World");
    }
}

実行すると以下のメッセージが出力されます。slf4jバインディングが見つからないというメッセージです。slf4jは実装が含まれておらず、ログを出力するためのインターフェースになりますのでこのような結果となります。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

試しにslf4jバインディングの一つであるslf4j-simple-1.6.1.jarをクラスパスに追加して実行してみましょう。そうすると次のように出力されるはずです。import には記述しなくてもOKです。

[main] INFO com.tasknotes.slf4j.Slf4jSample - Hello World

slf4j-simple-1.6.1.jarはすべてのイベントをSystem.errに対して出力するシンプルな実装のためのバインディングです。INFOレベルかそれ以上のメッセージのみが出力されます。

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Slf4jSample.class);
        logger.trace("trace log");
        logger.debug("debug log");
        logger.info("info log");
        logger.warn("warn log");
        logger.error("error log");
    }

// 実行結果 - トレースとデバッグが出力されない
[main] INFO com.tasknotes.slf4j.Slf4jSample - info log
[main] WARN com.tasknotes.slf4j.Slf4jSample - warn log
[main] ERROR com.tasknotes.slf4j.Slf4jSample - error log

LOGBackの使用

先程のslf4j-simple-1.6.1.jarをクラスパスから除去して、logback-classic-1.1.3.jarlogback-core-1.1.3.jarを追加して下さい。これは2つセットでないとエラーになります。上記と同じコードを実行すると次のような結果になります。

17:47:35.849 [main] DEBUG com.tasknotes.slf4j.Slf4jSample - debug log
17:47:35.853 [main] INFO  com.tasknotes.slf4j.Slf4jSample - info log
17:47:35.854 [main] WARN  com.tasknotes.slf4j.Slf4jSample - warn log
17:47:35.854 [main] ERROR com.tasknotes.slf4j.Slf4jSample - error log

※ 後述しますがTRACEが出力されないのは、logback.xmlを使用しない場合、デフォルトでDEBUG以上が対象となっているからです。

プレースホルダの使用とスタックトレースの出力

SLF4Jではプレースホルダが使用できます。波括弧を文字列中に埋め込み、カンマで区切って置き換える値を渡します。可変長引数になってますのでいくつでも渡す事が可能です。

logger.info("info log : UserId = {}, Name = {}", 1234, "山田");

// 実行結果
18:26:23.299 [main] INFO  com.tasknotes.slf4j.Slf4jSample - info log : UserId = 1234, Name = 山田

スタックトレースを出力する場合にはカンマで区切って引数の末尾に渡してやります。プレースホルダを一緒に使用する場合でも引数の最後で Throwable を渡せば大丈夫です。

try {
    String hoge = null;
    hoge.length();
} catch(Exception e) {
    logger.error("error log ->", e);
}

// 実行結果
18:30:15.179 [main] ERROR com.tasknotes.slf4j.Slf4jSample - error log ->
java.lang.NullPointerException: null
    at com.tasknotes.slf4j.Slf4jSample.main(Slf4jSample.java:16) ~[bin/:na]

このプレースホルだのおかげでLog4jでよく見かけていたif (logger.isDebugEnabled())で不要な文字列結合を避けるための記述も不要になります。文字列生成をライブラリの内側で処理するようになり、ログを出力しない場合は文字列生成が行われなくなりました。

 What is the fastest way of (not) logging? - SLF4J

設定ファイルのlogback.xmlについて

これまで見てきた通りlogback.xmlが無くても動きますが、使用することによってログの出力先と出力のフォーマット、出力レベルやメール送信などを指定する事ができます。logback.xmlはクラスパスが通っているところ(src/main/resource等)に作成して下さい。

基本的な設定内容は以下のようになります。設定内容ついての詳細は公式サイト  第3章 logbackの設定 を参照して下さい。

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} %-5level %logger{10} %msg%n</pattern>
    </encoder>
  </appender>
 
  <logger name="com.tasknotes.slf4j" level="INFO"/>
 
  <root level="TRACE">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

appender

appender要素には「どこに、どのように」出力するのかを記述します。上記のサンプルではSTDOUTとなっていますが、FILEに出力したりMAIL送信を指定する事も出来ます。

root

root要素は一つだけ指定できます。level属性にはTRACE、DEBUG、INFO、WARN、ERROR、ALLまたはOFFのいずれかを指定でき、大文字小文字は区別しません。デフォルトはDEBUGです。appender-ref要素で指定したappenderがロガーに追加されます。ということは、定義してあるappenderをここで指定していないとログに出力されないので注意が必要です。

logger

ロガーの出力レベルを指定します。logger要素は複数指定する事が可能です。単純にログレベルの制御だけならroot要素だけでいいのですが、特定のパッケージだけレベルを変えたいという場合はlogger要素を指定します。上記の例ではcom.tasknotes.slf4jパッケージのログレベルをINFO以上、root要素のレベルをTRACE以上としていますので、com.tasknotes.slf4jパッケージから出力されるロガーはTRACEとDEBUGは出力されずINFO以上のみとなります。ここでもappender-ref要素を追加する事が可能です。