Java好き

カテゴリ: SLF4J+Logback

効果的に運用したいSLF4J+Logback。

logback.xmlを自動読込されるパスに置く

Logbackは、
特定のファイル名で特定の場所に配置しておけば
自動読み込みしてくれる。

自分で設定ファイルを読み込む処理を書く手間が省けるので、
自動読込を積極的に利用する。

自動読込のルールは次の通り。

  1. クラスパス上のlogback.grovy
  2. 「1」のファイルが見つからない場合、
    クラスパス上のlogback-test.xml
  3. 「2」のファイルが見つからない場合、
    クラスパス上のlogback.xml
  4. 「3」のファイルが見つからない場合、
    BasicConfiguratorクラスの設定内容(コンソール出力)

クラスパス上なので、
Mavenの構成でいうと「src/main/resources」や「src/test/resources」の直下。
Eclipseであればソースフォルダ扱いになっているフォルダの直下でいい。

「logback.xml」の設定ファイル自体は、
自分で作成するかサンプルをコピーするなどして作成する。
作成したら、その場所へ置く。

ログレベル利用基準を明確にしておく

ログレベルはtraceが一番下でerrorが一番上ということは理解できても、
どのログ内容にはこのレベルといった基準は人によりまちまち。

なのでアーキテクチャ設定者側でしっかりとした指針を提示しておくと
開発者がいちいち迷うことなくロガーを利用できる。

参考例
ログレベル 基準
error 致命的エラーの原因を出力
warn 例外は発生してるが、システムを継続可能な場合用に出力。
info 仕様などに記載されていて規定通りにログを出力する場合に利用。
debug 開発者が自由に使用可能。ただし本番時には出力しない。
trace 開発者が自由に使用可能。ただし本番時には出力しない。

開発者個人の設定ファイルを作る

開発時にログを使用する場合、
ログの設定ファイルが1つしかないととても不便。

開発範囲の内容だけ出力したいといったように、
開発者個別で設定したいときはいくらでもある。

そんなときローカルで設定ファイルを書き換えるが、
それを誤ってコミットしてしまうこともあり、とても面倒。

そこで個人用の設定ファイルを用意する。

Logbackの自動読込のルールでは、
logback-test.xmlがデフォルトだと先に読み込まれる。

これを利用して
logback-test.xmlを開発者個人に使ってもらう。
リポジトリの管理からも除外する。

本番用には別途logback.xmlを作っておく。
このように利用すると開発者が自在にロガーを利用できることになる。

logback.xmlの具体例。

「開発用以外に仕様として別途ログを出力する」場合や
「開発用でも分けて出力したい」場合など、
複数のappenderに出力するときのlogback.xmlの設定を考える。

ケース1.同じ内容を複数のappenderに出力する

ファイルとコンソールなど同じ内容を複数のappenderに出力するときは、
rootにappender-refを加えるだけでOK。

<root level="debug">
  <appender-ref ref="STDOUT" />
  <appender-ref ref="LOG_FILE" />
</root>

ケース2.特定のロガーのみ、他のappenderに出力する

ログ全てではなく、ある特定のロガー出力だけ別にしたい場合は
そのloggerにappender-refを加える。

<logger name="javazuki.DoubleAppenderTest">
  <appender-ref ref="LOG_FILE" />
</logger>
   
<root level="debug">
  <appender-ref ref="STDOUT" />
</root>

このとき、
レベルを指定する場合
rootで出力される内容もそのレベルが適用される

ことに注意する。

例えば次の設定では、
「javazuki.DoubleAppenderTest」ロガーは
infoレベル以上が「LOG_FILE」と「STDOUT」に出力される。

<logger name="javazuki.DoubleAppenderTest" level="info">
  <appender-ref ref="LOG_FILE" />
</logger>
   
<root level="debug">
  <appender-ref ref="STDOUT" />
</root>

解釈としては「levelのフィルタリングとappenderの設定がそれぞれ独立している」ってこと。
よって、levelのフィルタリングがrootの方にも適用される。
(loggerのlevelが子要素のappendar-refにのみ適用されるわけではない。)

ケース3.特定のロガーのみ、別のappendarのみに出力する

今までのパターンではrootのappenderにも出力していた。
今度は、rootのappenderには出力せず、指定したappenderのみに出力する方法。

このとき、additivity="false"を使う。

次の設定では、
「javazuki.DoubleAppenderTest」ロガーは
infoレベル以上が「LOG_FILE」のみに出力されます。

<logger name="javazuki.DoubleAppenderTest" level="info" additivity="false">
  <appender-ref ref="LOG_FILE" />
</logger>
   
<root level="debug">
  <appender-ref ref="STDOUT" />
</root>

additivity="false"の意味は、
そのロガーはそれより上階層のロガーの設定に加わらない(独立する)
ということ。

つまり、
「jp.doorblog.DoubleAppendarOutputTest」ロガーは、
上階層の「root」の設定には加わらないので、「STDOUT」には出力されない
ことになる。

ちなみに、このときのlevelは結果的に子要素のappender-refのみに適用となる。

logback.xmlで設定することをまとめる。

「logback.xml」では、次のことを設定する。

  • ログの出力先と出力のフォーマット
  • ロガー(クラス、パッケージ)ごとの出力レベルの変更

実はlogback.xmlなくても動くが、
使いこなすにはもちろん設定した方がよい。

ちゃんとした詳しい設定方法については
Logbackのマニュアル(→こちら)を参照。

具体的な要素(タグ)としては、
次の3種類。

種類 概要
Appendar 「どの場所に」「どんなレイアウト」で出力するのか
Logger 「このパッケージやクラス」は「どのログレベル」以上で出力するのか
Root Loggerで指定されないものについて、「どのログレベル」以上で「どのAppendar」に出力するのか

基本的にはこんな感じになる。

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
 
  <logger name="chapters.configuration" level="INFO"/>
 
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Appendar

「どの場所に」「どんなレイアウト」で出力するのかを定義する。

Appendarを定義しただけでは出力されず、
LoggerやRootに指定されると初めて出力される

ので注意する。

Appendarの実装クラスは、
たくさん用意されているのでそれを利用する(自作も可能)。

提供されている主なApendarの一部
Appendar 概要
ConsoleAppender コンソール出力
FileAppender ファイル出力
RollingFileAppender ファイル出力(ローリング可能)

実装クラスが決まれば、
nameを指定して出力するメッセージのパターンを決めればいい。

appenderタグの属性
属性 必須 概要
name このAppendarの名前。appendar-refで指定される。好きな名前をつけていい。
class Appender実装クラスの絶対クラス名。

appenderタグの子要素にあたるものはAppndarによってそれぞれ異なるので
マニュアル(→こちら)を参考にして設定する。

ConsoleAppenderでの例
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
  </encoder>
</appender>

Logger

ログの出力制御(フィルタリグ)を行う場合は、
Loggerを追加する。

loggerタグの属性
属性 必須 概要
name 対象となるロガー名(パッケージやクラス)。
level   このログレベル以上を出力する。(このレベルより下を制限する。)
additivity   デフォルトはtrue。
特定のロガーだけ、別のappendarにのみ出力するするときなどに利用する。

「chapters.configuration」というパッケージで
「INFO」以上で出力する場合の例

<logger name="chapters.configuration" level="INFO"/>

Loggerは子要素に<appender-ref>を入れることで、
そのロガーの出力先を指定できる。

<logger name="chapters.configuration" level="INFO">
  <appender-ref ref="LOG_FILE" />
</logger>

Root

Rootは、name="ROOT"というLoggerの一種。
1つだけしか定義できないので特別なLoggerという解釈。

ロガーの有効レベルの決定ルール上、
必ず定義されていないといけないものになる。
よって1つしか定義できないし、
定義しなくてもデフォルトが設定される。

Loggerが定義されないときはこのRootの設定が適用されるので、
ログレベルだけの制御ならAppendarとRootだけ定義すればよい。

rootタグの属性
属性 必須 概要
level   デフォルトはDEBUG。このログレベル以上を出力する。

子要素に<appender-ref>を入れることで、
出力先のAppendarを指定します。

「STDOUT」に出力する場合の例
<root level="DEBUG">
  <appender-ref ref="STDOUT" />
</root>

ロガーの有効レベルを把握する

rootとloggerによってログの出力制御をするが、
ロガーの有効レベルを把握した上で、
設定しないとたぶん混乱する。

どの有効レベルが適用されるかは
各ロガー(クラス名やロガーの名前)ごと決定され

それは次のルールに従って決まる。

  1. 直接loggerで指定されている場合は、そのlevel。
  2. 直接指定されていない場合は、
    logger指定されている一番直近(上)の親パッケージのlevel。
  3. 親パッケージも指定されていない場合は、rootのlevel。

Logbackは階層型ロガーなので、直接指定しなくても
階層(パッケージ)によって有効レベルが決定される。

これによって、(クラス名でロガーを取得していれば)
パッケージ単位でログレベルを制御できる。

また、このルールに従って考えると、
全体をログレベルだけ制御したいときは
rootのlevelだけをいじればよい。

その中で必要でない(フレームワークなどの)ログについては、
loggerなどで絞り込めばいい。

メソッドを呼び出すだけではない
SLF4J+Logbackの使い方。

ロガーの取得

LoggerFactory.getLogger()でLoggerを取得する。
このとき、引数の指定によって大きく2つに分かれる。

  • 自分のクラスを引数に渡す方法
  • ロガーの名前を指定する方法

基本的には、前者の「自分のクラスを引数に渡す方法」がよい。
理由としては次のようなものがある。

  • logbakck.xmlでのロガーのフィルタリングの際に、
    クラス名もしくはパッケージ名でフィルタリングできる。
  • このとき、名前を指定しているとその名前でフィルタリングすることになる。
    よって、つけた名前をしっかり覚えていなくてはいけない。

よって、ロガーの名前を指定する場合は、
通常のログとは別の特別なログ出力するときなどでよい。

クラスを引数に渡す場合
public class LogOutputTest {
    private static final Logger log = LoggerFactory.getLogger(LogOutputTest.class);
}
名前を引数に渡す場合
public class MyNameLoggerTest {
    private static final Logger log = LoggerFactory.getLogger("mylogger.debug");
}

テンプレートに登録する

クラスを作るごとにLoggerを作ることになるので、
Eclipseなどでは自動生成されるクラスのテンプレートに追加するのがベター。

Eclipseの「設定」-「コード・テンプレート」の「コード」-「新規クラスファイル」。

${filecomment}
${package_declaration}
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
${typecomment}
${type_declaration}

Eclipseの「設定」-「コード・テンプレート」の「コード」-「クラス本文」。

@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(${type_name}.class);

ログの出力

それぞれのメソッドは、ログレベルに対応している。

また、例外を受け取るメソッドも用意されていて、
その場合スタックトレースがログに出力される。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class LogOutputTest {
    private static final Logger log = LoggerFactory.getLogger(LogOutputTest.class);
 
    @Test public void test_outputLog() throws Exception {
        log.trace("traceです。");
        log.debug("debugです。");
        log.info("infoです。");
        log.warn("warnです。");
        log.error("errorです。");
 
        try {
            // 何らかの処理
        } catch (Exception e) {
            log.warn("○○で例外発生です。", e);
        }
    }
}

ログ文字列に「{}」を
プレースホルダーとして利用することもできる。

プレースホルダー利用例
int number01 = 1234;
int number02 = 5678;
log.debug("この数値は{}と{}です。", number01, number02);
出力
20:00:33 DEBUG - この数値は12345678です。

単純にjarが一つではないので、jarを選ぶ必要がある。

「インタフェース」と「実装」がそれぞれ必要

ライブラリを用いてロギングを行う場合は、
まずこの2種類のライブラリをそろえる。

ロギング実装は数多く存在するため、
それに対応するインタフェース用ライブラリもいくつか存在する。

「SLF4J」と「Logback」を対(つい)にして扱うのはそのため。
1つだけで使うことはしない。

ArrayListインスタンスの変数をList型で定義するように、
インタフェースに対してプログラムするのが基本。

ロギング実装ライブラリだけを導入しても、実はロギングは可能。
ただ、少しでも広く構えておけるように

代表的なライブラリは次のとおり。

ロギングインタフェース用ライブラリ 「SLF4J」「Commons-logging」など
ロギング実装ライブラリ 「Logback」「Log4j」など

必要なjarを把握する。

特にSLF4Jの方は、
様々状況に対応するためにいくつかのモジュールに分割されている。

全てをやみくもに取り入れるのではダメで、
利用しているフレームワークなどを基準にして選択が必要。

どのjarが必要なのかは公式サイトで図となっているので、
これを見れば大体分かる。(→こちら)

Maven or ダウンロード

「Maven」or「サイトからダウンロードする」で必要なjarファイルを準備する。

Maven

<dependencies>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.5</version>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.0.13</version>
  </dependency>
</dependencies>  

ダウンロード

SLF4J、Logbackともにダウンロード用のzipファイルが用意されているので、
それを持ってくる。

  • SLF4J本家サイトのダウンロードページ→こちら
  • Logback本家サイトのダウンロードページ→こちら

zipファイルを解凍すると途方に暮れるほどjarファイルが沢山あるが、
前述のように、必要なjarだけをコピーする。

構成例

記載されているバージョンは、
2014年3月1日現在で最新のもの。

SLF4J+Logbackのみの場合

標準的な構成で使用できる。

プロダクト 必要なjar
SLF4J slf4j-api.jar
Logback logback-classic.jar、logback-core.jar

Springの場合

Springではcommons-loggingが使われているがSLF4Jで置き換えることが可能。
「jcl-over-slf4j」を加える。

プロダクト 必要なjar
SLF4J slf4j-api.jar、jcl-over-slf4j
Logback logback-classic.jar、logback-core.jar
Maven
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${spring.version}</version>
  <exclusions>
     <exclusion>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
     </exclusion>
  </exclusions>    
</dependency>
 
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
  <version>${slf4.version}</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>${logack.version}</version>
</dependency>