Logback 使い方メモ

# Logback とは
Java のロギングライブラリ。

Log4j の開発者が作った別のロギングライブラリ。

歴史的な背景とか、 SLF4J との関係については、 [Javaのログ出力: 道具と考え方][1] というスライドがとても参考になります。

# Hello World
## インストール
```groovy:build.gradle
    compile 'ch.qos.logback:logback-classic:1.1.3'
```

## 実装
```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.info("Hello Logback!!");
    }
}
```

```text:実行結果
11:14:22.582 [main] INFO  sample.logback.Main - Hello Logback!!
```

## 説明
- 依存関係には、とりあえず `logback-classic` を追加すれば動かせる。
    - 推移的な依存関係の解決で、 `logback-core` や `slf4j-api` などが付いてくる。
- Logback を使う場合、 API はログファサードである SLF4J を使用する。
- 設定ファイル（`logback.xml`）が存在しない場合、デフォルトでコンソールにログが出力される。
- ロガーの取得には、 `LoggerFactory#getLogger()` を使う。
    - 引数にはロガーを特定するための名前を渡す。
    - 普通は、ロガーを使うクラスの `Class` オブジェクトを渡す。
    - すると、クラスの FQCN を名前にしたロガーを取得できる。

# `Logger` インターフェースの使い方
## ログレベル
```java:Main.java
package sample.logback;

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

import ch.qos.logback.classic.Level;

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        
        ch.qos.logback.classic.Logger log = (ch.qos.logback.classic.Logger)logger;
        log.setLevel(Level.TRACE); // ★デフォルトだと trace レベルは出力されないので、出力のレベルを TRACE にしている

        logger.trace("trace message");
        logger.debug("debug message");
        logger.info("info message");
        logger.warn("warn message");
        logger.error("error message");
    }
}
```

```text:実行結果
11:16:15.220 [main] TRACE sample.logback.Main - trace message
11:16:15.222 [main] DEBUG sample.logback.Main - debug message
11:16:15.222 [main] INFO  sample.logback.Main - info message
11:16:15.222 [main] WARN  sample.logback.Main - warn message
11:16:15.222 [main] ERROR sample.logback.Main - error message
```

- `trace`, `debug`, `info`, `warn`, `error` の５つのログレベルがある。

## パラメータ
```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);

        logger.info("{}",    "HOGE");
        logger.info("{} {}", "HOGE", "FUGA");
        logger.info("{} {}", "HOGE");
        logger.info("{}",    "HOGE", "FUGA");
        logger.info("\\{}",  "HOGE");
        logger.info("{ }",   "HOGE");
    }
}
```

```text:実行結果
11:16:36.758 [main] INFO  sample.logback.Main - HOGE
11:16:36.762 [main] INFO  sample.logback.Main - HOGE FUGA
11:16:36.762 [main] INFO  sample.logback.Main - HOGE {}
11:16:36.762 [main] INFO  sample.logback.Main - HOGE
11:16:36.762 [main] INFO  sample.logback.Main - {}
11:16:36.762 [main] INFO  sample.logback.Main - { }
```

- 第一引数にパラメータ `{}` を宣言した文字列を渡し、第二引数以降にパラメータに埋め込む値を渡す。
- すると、 `{}` の部分に、第二引数以降の値が順番に埋め込まれる。
- パラメータの数と引数の数が一致しない場合は、数が少ない方に合わせられる（エラーにはならない）。
    - パラメータの方が多い場合は、そのまま `{}` と出力される（空文字にはならない）。
- バックスラッシュでエスケープできる。
- `{` と `}` の間に文字が入ると埋め込み対象にならなくなる。

## スタックトレースの出力
```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        
        try {
            throw new Exception("test exception");
        } catch (Exception e) {
            logger.info("error", e);
        }
    }
}
```

```text:実行結果
11:16:57.064 [main] INFO  sample.logback.Main - error
java.lang.Exception: test exception
	at sample.logback.Main.main(Main.java:12) ~[bin/:na]
```

- 第一引数に文字列、第二引数に例外を渡すと、スタックトレースをログに出力できる。

# 設定ファイル
## コンソールに出力するアペンダーを定義する
```text:フォルダ構成
src/main/
  |-java/
  |  `-sample/logback/
  |    `-Main.java
  `-resources/
     `-logback.xml
```

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.info("Hello Logback!!");
    }
}
```

```text:実行結果
Hello Logback!!
```

- 設定ファイルは、 `logback.xml` という名前で作成して、クラスパス直下に配置する。
- `<root>` タグで、ルートロガーの設定を記述する。
    - `<appender-ref>` でアペンダー（ログの出力先）を指定する。
- `<appender>` タグで、アペンダーを定義する。
    - `<encoder>` タグの `<pattern>` タグで、出力のフォーマットを定義する。
    - `ch.qos.logback.core.ConsoleAppender` クラスを指定することで、コンソールに出力するアペンダーを定義できる。

## メッセージのパターンを指定する
### 変換指定子
メッセージのパターン定義には、 `%m` のように `%` で始まる特別なパラメータを定義することができる。

パラメータは、次のような書式になっている。

```text:パラメータの書式
%<変換指定子>{オプション}
```

**変換指定子**には、 `logger` や `msg` など、出力する内容ごとに決められた値を設定する。

**オプション**には、変換識別子ごとに固有の設定値を渡す。例えば、日付を出力する `date` には `%date{yyyyMMdd}` のようにフォーマットを渡すことができる。
オプションなので、必要でなければ省略できる。

以下に、よく使いそうな変換指定子の使い方をまとめる。
詳細な使い方や、その他の変換指定子については、[PatternLayout | 第６章レイアウト](http://logback.qos.ch/manual/layouts_ja.html#ClassicPatternLayout)を参照のこと。

**実装**

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.info("Hello Logback!!");
    }
}
```

|      変換指定子       |   設定例    |          実行結果          |                   説明                   |
|-----------------------|-------------|----------------------------|------------------------------------------|
| `c`, `lo`, `logger`   | `%c`        | `sample.logback.Main `     | ロガーの名前を出力する。                 |
| `d`, `date`           | `%d`        | `2015-10-24 00:36:55,947 ` | 日時を出力する。                         |
|                       | `%d{HH:mm}` | `00:57`                    | オプションでフォーマットを指定できる。   |
| `m`, `msg`, `message` | `%m`        | `Hello Logback!! `         | ロガーに渡したメッセージを出力する。     |
| `n`                   | `a%n b`     | `a<改行> b`                | 実行環境に合わせた改行コードを出力する。 |
| `p`, `le`, `level`    | `%p`        | `INFO`                     | ログレベルを出力する。                   |
| `t`, `thread`         | `%t`        | `main`                     | ログ出力時のスレッド名を出力する。       |

### 変換指定子の直後に文字列を出力する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>hoge%nfuga</pattern>
    </encoder>
  </appender>

  ...
</configuration>
```

改行を表す `%n` の直後に `fuga` と出力しようとしている。

しかし、このまま動かすと、 Logback は `nfuga` という変換指定子が指定されたものと勘違いして、「そんな変換指定子はない！」とエラーになる。

これを回避するには、空のオプションを指定してあげる。

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>hoge%n{}fuga</pattern>
    </encoder>
  </appender>

  ...
</configuration>
```

```text:実行結果
hoge
fuga
```

### 書式修飾子
`%` と変換指定子の間に、パディングなどの書式修飾子を指定できる。

**書式修飾子のフォーマット**

```text:
%[書式修飾子]<変換指定子>

書式修飾子：
    [最小幅][.最大幅]

最小幅, 最大幅：
    整数
```

|   設定例    | 出力文字列 |  実行結果  |                                 説明                                 |
|-------------|------------|------------|----------------------------------------------------------------------|
| `[%5m]`     | `"123"`    | `[  123]`  | **最小幅**を指定した場合、右寄せして半角スペースでパディング。       |
|             | `"123456"` | `[123456]` | 桁数を越える場合は、省略されずに出力される。                         |
| `[%.5m]`    | `"123"`    | `[123]`    | **最大幅**を指定した場合、桁数を超えた分が**左から**順に省略される。 |
|             | `"123456"` | `[23456]`  |                                                                      |
| `[%-5]`     | `"123"`    | `[123  ]`  | 最小幅をマイナスにすると、左寄せになる。                             |
| `[%.-5]`    | `"123456"` | `[12345]`  | 最大幅をマイナスにすると、桁数を超えた分が**右から**順に省略される。 |
| `[%3.5m]`   | `"12"`     | `[ 12]`    | 最小幅と最大幅を同時に指定することもできる。                         |
|             | `"1234"`   | `[1234]`   |                                                                      |
|             | `"123456"` | `[23456]`  |                                                                      |
| `[%-3.-5m]` | `"12"`     | `[12 ]`    | それぞれマイナスにすることもできる。                                 |
|             | `"1234"`   | `[1234]`   |                                                                      |
|             | `"123456"` | `[12345]`  |                                                                      |

### グループを作成する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%-7([%level]) : %m%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

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

```text:実行結果
[DEBUG] : debug
[INFO]  : info
[WARN]  : warn
[ERROR] : error
```

- `()` で括ると、その部分をグループ化させられる。
- グループには書式修飾子を指定できる。
- `(`, `)` を普通の文字として使いたい場合は、バックスラッシュ `\` でエスケープする。

## 変数を定義する
### 基本
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <!-- ★ property タグで宣言 -->
  <property name="hoge" value="HOGE" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>hoge = ${hoge}%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

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

```text:実行結果
hoge = HOGE
```

- `<propety>` タグを使うことで、変数を定義できる。
- `${<変数名>}` で、変数を参照できる。

### システムプロパティの値を参照する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>hoge = ${hoge}%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        System.setProperty("hoge", "Hoge"); // ★システムプロパティを設定
        
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.info("");
    }
}
```

```text:実行結果
hoge = Hoge
```

- `${<システムプロパティ名>}` で、システムプロパティの値を参照できる。

### OS の環境変数を参照する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property file="./fuga.properties" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>OS = ${OS}%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```text:実行結果
OS = Windows_NT
```

- `${<環境変数名>}` で OS の環境変数を参照できる。
- `<property>` で宣言された変数 → システムプロパティ → 環境変数の順に検索され、最初に見つかった値が採用される。

### 変数を別ファイルに定義する
```properties:fuga.properties
fuga=FUGA
```

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property file="./fuga.properties" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>fuga = ${fuga}%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```text:実行結果
fuga = FUGA
```

- `<property>` タグの `file` 属性を指定すれば、別途作成したプロパティファイルを読み込める。
- `file` 属性の代わりに `resource` 属性を指定すれば、クラスパス上のファイルを指定することもできる。

### 変数のデフォルト値を指定する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>hoge = ${hoge:-default value}%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```text:実行結果
hoge = default value
```

- `${<変数名>:-<デフォルト値>}` という形式で、変数が定義されていなかった場合のデフォルト値を指定できる。

### 変数名のネスト
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property name="nest" value="hoge" />
  
  <property name="value.hoge" value="HOGE" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>value.${nest} = ${value.${nest}}%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```text:実行結果
value.hoge = HOGE
```

- `${}` は入れ子にできる。
- `${value.${nest}}` が `${value.hoge}` になって `HOGE` になってる。

## 設定ファイルを分割する
```text:フォルダ構成
src/main/
  |-java/
  |  `-Main.java
  `resources/
     |-logback.xml
     `-hoge.xml
```

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <include resource="hoge.xml" />

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```xml:hoge.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<included>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>hoge : %m%n</pattern>
    </encoder>
  </appender>
</included>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.info("hello world");
    }
}
```

```text:実行結果
hoge : hello world
```

- `<include>` タグを使うと、分割した他の設定ファイルを読み込むことができる。
    - クラスパス外のファイルを指定する場合は `file` 属性でパスを指定する。
    - クラスパス上のファイルを指定する場合は `resource` 属性で指定する。
- 読み込まれる側のファイルは、 `<included>` タグで囲んだ場所に設定を記述する。

# ロガー
## ロガーの設定を宣言する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>
  
  <logger name="sampleLogger" />

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("sampleLogger");
        
        logger.debug("hello sampleLogger");
    }
}
```

```text:実行結果
hello sampleLogger
```

- `<logger>` タグを使って `sampleLogger` というロガーの設定を宣言している。
- `getLogger()` メソッドの引数でロガーの名前を渡すことで、指定したロガーを取得できる。

## ロガーの出力レベルを指定する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>
  
  <!-- ★level 属性を指定 -->
  <logger name="sampleLogger" level="info" />

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("sampleLogger");
        
        logger.debug("debug");
        logger.info("info");
    }
}
```

```text:実行結果
info
```

- `<logger>` タグの `level` 属性で、そのロガーのレベルを指定できる。
- 指定したレベル以上のログだけが出力されるようになる。

## ロガーの親子関係
全てのロガーは、**ルートロガー**を最上位とした親子関係になっている。

ロガーの名前にドット (`.`) が含まれる場合、 `.` が親子関係の区切りを表すことになる。

例えば、 `foo.bar.Hoge` という名前のロガーは、 `foo.bar` というロガーの子供ということになる。
また、 `foo.bar` ロガーは、 `foo` というロガーの子供になる。
そして、 `foo` ロガーはルートロガーの子供になる。

![logback.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/776f4883-5e1f-26dc-b4ac-fc32f6368202.jpeg)

子供のロガーは、親のロガーに設定されているレベルやアペンダーを継承するようになっている。
つまり、子供のロガーで設定を記述しない場合は、デフォルトで親のロガーと同じ設定が適用されることになる。

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>
  
  <logger name="foo" level="trace" />
  <logger name="foo.bar" level="info" />

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        log("foo");
        log("foo.Fuga");
        log("foo.bar");
        log("foo.bar.Hoge");
    }
    
    private static void log(String loggerName) {
        Logger logger = LoggerFactory.getLogger(loggerName);
        
        System.out.println("[" + loggerName + "]");
        logger.trace("trace");
        logger.debug("debug");
        logger.info("info");
        System.out.println();
    }
}
```

```text:実行結果
[foo]
trace
debug
info

[foo.Fuga]
trace
debug
info

[foo.bar]
info

[foo.bar.Hoge]
info
```

![logback.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/0151c586-f90a-21fa-e4b1-41d690d06c28.jpeg)

- ルートロガーは、デフォルトでレベルが `debug` になっている。
- それを、 `foo` ロガーで `trace` に上書きしている。
- さらに、その設定を `foo.bar` ロガーでは `info` に上書きしている。
- `foo` ロガーの子供である `foo.Fuga` ロガーは、 `foo` ロガーの設定を継承して `trace` レベルまでログが出力されている。
- 一方で、 `foo.bar.Hoge` ロガーは `foo.bar` ロガーの設定を継承して `info` レベルのログしか出力されていない。

## ロガーの名前
一般的に、ロガーの名前にはそのロガーを使用するクラスの FQCN を使用するのが良いとされている。

そうすることで、パッケージ名がそのロガーの親ロガーということになり、パッケージ単位でロガーの設定を変更する、といった調整が可能になる。

ロガーの名前を FQCN にする場合、文字列で指定するのではなく `Class` オブジェクトを渡す方が良い。
そうしておけば、クラス名の変更やパッケージの移動が必要になったときでも IDE のリファクタリング機能を使うことで自動でロガー名を変更できるようになる。

ただし、クラス名にしなければならない、というわけではなく、用途に合わせた名称でロガーを宣言しても問題はない。

## アペンダーを設定する
アペンダーとは、実際にログを出力するコンポーネントで、ログの出力先（コンソール、ファイル、データベース）やフォーマットなどを指定することができる。

ロガーにアペンダーを設定することで、そのロガーを使ったときのログ出力方法が決定される。

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT1" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>STDOUT1 : %msg%n</pattern>
    </encoder>
  </appender>
  
  <appender name="STDOUT2" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>STDOUT2 : %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="foo.bar.Hoge">
    <appender-ref ref="STDOUT2" />
  </logger>

  <root>
    <appender-ref ref="STDOUT1" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("foo.bar.Hoge");
        
        logger.info("hello appender");
    }
}
```

```text:実行結果
STDOUT2 : hello appender
STDOUT1 : hello appender
```

- `<appender>` タグを使って、アペンダーを定義できる。
- `<appender>` タグには、 `name` と `class` という必須の属性が２つある。
    - `name` は、そのアペンダーの名前を任意に指定する。
    - `class` は、アペンダーの具体的な実装クラスを FQCN で指定する。
        - `ch.qos.logback.core.ConsoleAppender` は Logback が提供しているアペンダーで、コンソールにログを出力するアペンダーになる。
- ロガーにアペンダーを設定するには、子供要素に `<appender-ref>` タグを追加する。
    - `ref` 属性に、設定するアペンダーの `name` を指定する。

### アペンダーの設定は積み重ねられる
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <logger name="foo.bar.Hoge">
    <appender-ref ref="STDOUT" />
  </logger>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
```

ルートロガーと `foo.bar.Hoge` ロガーに、それぞれ `STDOUT` アペンダーを設定している。

この場合、アペンダーの名前が一緒だからルートロガーの `STDOUT` は `foo.bar.Hoge` の `STDOUT` で上書きされて結果的に１つのアペンダーが設定されるのかという、そうではない。

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("foo.bar.Hoge");
        
        logger.info("hello appender");
    }
}
```

```text:実行結果
hello appender
hello appender
```

アペンダーの設定は積み重ねられるので、 `foo.bar.Hoge` ロガーはルートロガーで設定された `STDOUT` と自身に設定された `STDOUT` に２回ログ出力を指示することになる。
結果、ログは重複して出力される。

### アペンダーの積み重ねを止める
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="STDOUT1" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>STDOUT1 : %msg%n</pattern>
    </encoder>
  </appender>
  
  <appender name="STDOUT2" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>STDOUT2 : %msg%n</pattern>
    </encoder>
  </appender>
  
  <appender name="STDOUT3" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>STDOUT3 : %msg%n</pattern>
    </encoder>
  </appender>

  <!-- ★ additivity に false を設定 -->
  <logger name="foo.bar" additivity="false">
    <appender-ref ref="STDOUT2" />
  </logger>

  <logger name="foo.bar.Hoge">
    <appender-ref ref="STDOUT3" />
  </logger>

  <root>
    <appender-ref ref="STDOUT1" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        log("foo.bar");
        log("foo.bar.Hoge");
    }
    
    private static void log(String loggerName) {
        Logger logger = LoggerFactory.getLogger(loggerName);
        
        System.out.println("[" + loggerName + "]");
        logger.info("hello appender");
        System.out.println();
    }
}
```

```text:実行結果
[foo.bar]
STDOUT2 : hello appender

[foo.bar.Hoge]
STDOUT3 : hello appender
STDOUT2 : hello appender
```

- `<logger>` タグの `additivity` 属性に `false` を設定すると、親のロガーに設定されていたアペンダーを継承しなくなる。
- `additivity="false"` を設定したロガーの子供は、再び親のアペンダーを継承するようになる。

# ファイル出力
## 基本
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>logback-sample.log</file>
    <encoder>
      <charset>UTF-8</charset>
      <pattern>%m%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Main.class);
        logger.info("hello world");
    }
}
```

```text:logback-sample.log
hello world

```

- `ch.qos.logback.core.FileAppender` でアペンダーを定義すると、ファイル出力が可能になる。
- `<file>` タグで出力ファイルを指定する。
- ファイルがなければ作成する。
- ファイルまでのフォルダがなければ、勝手に作成してくれる。
- 文字コードは `<encoder>` タグの下に `<charset>` タグを入れて設定する。

## ファイル名にタイムスタンプを入れる
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>

  <timestamp key="time" datePattern="yyyyMMdd_HHmmss" />
  
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>logback-sample_${time}.log</file>
    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

```text:実行後のフォルダ構成
|-logback-sample_20151024_230826.log
`-src/
```

```text:logback-sample_20151024_230826.log
hello world

```

- `<timestamp>` タグで実行日時を記録した変数を定義できる。
- `key` 属性に他から参照のための名前を。
- `datePattern` 属性に日付書式を指定する。

## ローテーションさせる
ログファイルをローテーションさせる場合は、アペンダーのクラスに `ch.qos.logback.core.rolling.RollingFileAppender` を指定する。

### 時間基準でローテーションさせる
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logback-sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>logback-sample-%d{yyyyMMdd_HHmmss}.log</fileNamePattern>
    </rollingPolicy>

    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender> 

  <root>
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

何度かログ出力を実行する。

```text:実行後のフォルダ構成
|-logback-sample.log
|-logback-sample-20151024_232440.log
|-logback-sample-20151024_232459.log
|-logback-sample-20151024_232501.log
`-src/
```

- `<rollingPolicy>` タグに `ch.qos.logback.core.rolling.TimeBasedRollingPolicy` を指定する。
- `<fileNamePattern>` タグで、ローテーション後のファイル名を指定する。
- 日付部分の書式には、 `%d` を使用する。
    - 具体的なフォーマットの指定は `%d{yyyyMMdd}` のように、変換指定子の場合と同じ要領で記述する。
    - オプションを指定しない場合、デフォルトで `yyyy-MM-dd` という日付書式が使用される。
- ローテーションは、日付の書式から自動的に推測されて実行される。
    - `%d{yyyyMMdd_HHmmss}` の場合は秒まで書式が指定されているので、秒が切り替わればローテーションが実行される。
    - `%d{yyyy-MM-dd}` なら日付が切り替わればローテーションされる。

### 古いログファイルを自動で削除する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logback-sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>logback-sample-%d{yyyyMMdd_HH}.log</fileNamePattern>
      <maxHistory>3</maxHistory> <!-- ★最大保持件数を設定 -->
    </rollingPolicy>

    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender> 

  <root>
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

```text:ログファイル（ログ出力実行前）
2015/10/24  18:00                52 logback-sample-20151024_18.log
2015/10/24  19:00                52 logback-sample-20151024_19.log
2015/10/24  20:00                52 logback-sample-20151024_20.log
2015/10/24  21:00                52 logback-sample-20151024_21.log
2015/10/24  22:00                37 logback-sample.log
```

```text:ログファイル（ログ出力実行後）
2015/10/24  20:00                52 logback-sample-20151024_20.log
2015/10/24  21:00                52 logback-sample-20151024_21.log
2015/10/24  22:00                37 logback-sample-20151024_22.log
2015/10/24  23:58                37 logback-sample.log
```

- `<maxHistory>` タグで、いくつ前までのログファイルを保持するかを設定できる。
- 日付書式が `%d{yyyyMMdd_HH}` で、 `maxHistory` が `3` なので、「３時間より前のファイル」は削除されることになる。
    - ログ出力が２３時なので、それより３時間前（２０時）より前（１９時以前）のファイルは全て削除される。

### 毎日ローテーションさせつつ過去ログは月ごとのフォルダに格納する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logback-sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>%d{yyyyMM,aux}/logback-sample-%d{yyyyMMdd}.log</fileNamePattern>
    </rollingPolicy>

    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender> 

  <root>
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

- 月ごとのフォルダを表す部分に `%d{yyyyMM,aux}` というふうに `aux` オプションを指定する。
    - `aux` は `auxiliary` （補助）の略で、これが設定された `%d` はファイルローテーションのタイミングとしては識別されなくなる。
    - この場合、 `aux` が指定されていない `%d{yyyyMMdd}` の方がファイルローテーションのタイミングとして認識される。

### 過去ログを圧縮する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logback-sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>logback-sample_%d{yyyyMMdd_HHmmss}.zip</fileNamePattern>
    </rollingPolicy>

    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender> 

  <root>
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

- `<fileNamePattern>` に指定するファイル名の拡張子を `zip` や `gz` にすれば、自動的に圧縮してくれる。

# メールで送信する
`SMTPAppender` を使うと、 `error` レベルのログを出力したタイミングでメールを送信することができる。

試しに GMail を使ってメールを送信する。

```groovy:build.gradle
dependencies {
    compile 'ch.qos.logback:logback-classic:1.1.3'
    compile 'javax.mail:mail:1.4.7' // ★ Java Mail を追加
}
```

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    <smtpHost>smtp.gmail.com</smtpHost>
    <smtpPort>465</smtpPort>
    <SSL>true</SSL>
    <username>xxxxxxxxxxxxxxxx@gmail.com</username>
    <password>xxxxxxxxxxxxxxxxxxxxx</password>
    
    <to>yyyyyyyyyyyy@gmail.com</to>
    <from>xxxxxxxxxxxxxxxx@gmail.com</from>
    <subject>Logback SMTPAppender Sample</subject>
    
    <layout class="ch.qos.logback.classic.PatternLayout">
      <pattern>%m%n</pattern>
    </layout>
  </appender>

  <root>
    <appender-ref ref="EMAIL" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) throws Exception {
        Logger logger = LoggerFactory.getLogger(Main.class);
        
        for (int i=0; i<256; i++) {
            logger.info("i=" + i);
        }
        logger.error("error!!");
        
        // ★メールの送信が完了するまで少し待機
        Thread.sleep(5000);
    }
}
```

**実行結果**

![logback.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/b137a17e-cff6-25ad-3fae-1c6a9f3318db.jpeg)

- メール送信を使うには `ch.qos.logback.classic.net.SMTPAppender` をアペンダーとして指定する。
- 各設定は以下のような感じ。

|     設定     |                                            説明                                            |
|--------------|--------------------------------------------------------------------------------------------|
| `<smtpHost>` | SMTP サーバーのホスト名                                                                    |
| `<smtpPort>` | SMTP サーバーのポート番号。デフォルトは `25`。                                             |
| `<SSL>`      | `true` を指定すると、 SMTP サーバーに SSL で接続する。                                     |
| `<username>` | SMTP サーバーの接続に認証が必要な場合のユーザー名。                                        |
| `<password>` | 同接続用のパスワード。                                                                     |
| `<to>`       | 送信先のメールアドレス。`<to>` を複数宣言するか、カンマ `,` 区切りで複数の宛先を指定可能。 |
| `<from>`     | 送信元のメールアドレス。                                                                   |
| `<subject>`  | メールの件名。 `%logger` などの変換指定子も指定可能。                                      |

- `SMTPAppender` に出力されたログは、デフォルトで最大 256 個までキャッシュされる。
- `error` レベルでログを出力すると、このキャッシュされたログと `error` レベルで出力したログの内容がメールで送信される。
- メールの送信処理は、デフォルトでは非同期で行われる。
    - サンプルでは `Thread.sleep()` を入れてメールの送信が完了するのを待機している。
    - こうしないと、非同期で行われているメールの送信が完了する前に JVM が終了してしまい、動作の確認ができなかった。

## Gmail で送信するときのパスワードについて
二段階認証を有効にしている場合、[アプリパスワード](https://support.google.com/accounts/answer/185833?hl=ja)を発行する必要がある。

## キャッシュするログの量を変更する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    <smtpHost>smtp.gmail.com</smtpHost>
    <smtpPort>465</smtpPort>
    <SSL>true</SSL>
    <username>xxxxxxxxxxxxxxxx@gmail.com</username>
    <password>xxxxxxxxxxxxxxxxxxxxx</password>
    
    <to>yyyyyyyyyyyy@gmail.com</to>
    <from>xxxxxxxxxxxxxxxx@gmail.com</from>
    <subject>Logback SMTPAppender Sample</subject>
    
    <!-- ★キャッシュサイズを変更 -->
    <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
      <bufferSize>10</bufferSize>
    </cyclicBufferTracker>
    
    <layout class="ch.qos.logback.classic.PatternLayout">
      <pattern>%m%n</pattern>
    </layout>
  </appender>

  <root>
    <appender-ref ref="EMAIL" />
  </root>
</configuration>
```

**実行結果**

![logback.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/357219e3-5cd1-dd1b-4349-7252cf7ef6be.jpeg)

- `<cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">` を追加して、 `<bufferSize>` タグでキャッシュするログの量を指定できる。

## メールの送信を同期処理にする
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    <smtpHost>smtp.gmail.com</smtpHost>
    <smtpPort>465</smtpPort>
    <SSL>true</SSL>
    <username>xxxxxxxxxxxxxxxx@gmail.com</username>
    <password>xxxxxxxxxxxxxxxxxxxxx</password>
    
    <to>yyyyyyyyyyyy@gmail.com</to>
    <from>xxxxxxxxxxxxxxxx@gmail.com</from>
    <subject>Logback SMTPAppender Sample</subject>
    
    <!-- ★同期処理 -->
    <asynchronousSending>false</asynchronousSending>
    
    <layout class="ch.qos.logback.classic.PatternLayout">
      <pattern>%m%n</pattern>
    </layout>
  </appender>

  <root>
    <appender-ref ref="EMAIL" />
  </root>
</configuration>
```

- `<asynchronousSending>` タグを追加して `false` を指定すると、メールの送信が同期処理になる。
- エラーが発生すると、その直後に JVM を終了させてしまうようなアプリの場合は、メールの送信を同期にしておく必要がある。

# フィルター
アペンダーにフィルターを設定することで、特定の条件に一致したログだけを出力できるようになる。

## 特定のログレベルだけを出力させる（させない）
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="DENY_INFO" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>DENY</onMatch>
    </filter>
    <encoder>
      <pattern>%level %n</pattern>
    </encoder>
  </appender>
  
  <appender name="ACCEPT_DEBUG" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>DEBUG</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
    <encoder>
      <pattern>%level %n</pattern>
    </encoder>
  </appender>

  <logger name="accept.debug">
    <appender-ref ref="ACCEPT_DEBUG" />
  </logger>

  <logger name="deny.info">
    <appender-ref ref="DENY_INFO" />
  </logger>

  <root level="trace">
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) throws Exception {
        log("accept.debug");
        log("deny.info");
    }
    
    private static void log(String loggerName) {
        Logger logger = LoggerFactory.getLogger(loggerName);
        
        System.out.println("[" + loggerName + "]");
        logger.trace("");
        logger.debug("");
        logger.info("");
        logger.warn("");
        logger.error("");
        System.out.println();
    }
}
```

```text:実行結果
[accept.debug]
DEBUG 

[deny.info]
TRACE 
DEBUG 
WARN 
ERROR
```

- `<filter>` タグを追加し、 `class` に `ch.qos.logback.classic.filter.LevelFilter` を設定する。
- `<level>` タグに、対象のログレベルを指定する。
- `<onMatch>` および `<onMismatch>` タグに、ログレベルがマッチした場合にどうするかを指定する。
    - `DENY` を指定すると、出力は完全に中断される（他のフィルタの処理はスキップされる）。
    - `ACCEPT` を指定すると、出力が許可される（他のフィルタの処理はスキップされる）。
    - `NEUTRAL` を指定すると、現在のフィルタはスキップされ次のフィルタに処理が送られる。

## あるログレベルより低いログレベルを出力させないようにする
```xml:loglevel.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>WARN</level>
    </filter>
    <encoder>
      <pattern>%level %n</pattern>
    </encoder>
  </appender>

  <root level="trace">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

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

```text:実行結果
WARN 
ERROR 
```

- `ch.qos.logback.classic.filter.ThresholdFilter` をフィルターに指定することで、 `<level>` で指定したログより低いレベルのログ出力を抑制できる。

## 特定の文字列が含まれるログだけを出力する
```groovy:build.gradle
dependencies {
    compile 'ch.qos.logback:logback-classic:1.1.3'
    compile 'janino:janino:2.5.10' // ★追加
}
```

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
      <evaluator>
        <matcher>
          <name>hogeMatcher</name>
          <regex>hoge</regex>
        </matcher>
        
        <expression>hogeMatcher.matches(formattedMessage)</expression>
      </evaluator>
      
      <OnMismatch>DENY</OnMismatch>
    </filter>

    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) throws Exception {
        Logger logger = LoggerFactory.getLogger(Main.class);
        
        logger.info("hoge");
        logger.info("fuga");
        logger.info("piyo");
        logger.info("hoge fuga");
        logger.info("hoge piyo");
    }
}
```

```text:実行結果
hoge
hoge fuga
hoge piyo
```

- 依存関係に `janino:janino:2.5.10` を追加する。
- `<filter>` に `ch.qos.logback.core.filter.EvaluatorFilter` を設定する。
- `<evaluator>` タグを追加し、 `<matcher>` タグで文字列を検証するための `Matcher` を定義する。
    - `<name>` 属性でその `Matcher` の名前を、 `<regex>` タグでマッチの条件となる正規表現を定義する。
    - `<expression>` タグで、 `Matcher` を使った値の検証ロジックを記述する。
        - `Matcher` を使わなくても、評価結果が `boolean` になるのであれば、任意の Java コードを記述することができる。
    - `formattedMessage` は `<expression>` タグ中で参照可能な暗黙の変数で、最終的に出力されようとしている文字列が格納されている。
- `hoge` という文字列が含まれているメッセージのみがログ出力されている。

### `<expression>` タグ中で参照できる暗黙の変数（一部）

|       変数名       |     型      |                                  説明                                  |
|--------------------|-------------|------------------------------------------------------------------------|
| `message`          | `String`    | ロガーに渡されたメッセージ。パラメータ(`{}`)もそのままの形で渡される。 |
| `formattedMessage` | `String`    | 最終的に出力されようとしている文字列。                                 |
| `logger`           | `String`    | ロガーの名前。                                                         |
| `level`            | `int`       | ログレベルを表す整数値。                                               |
| `DEBUG`            | `int`       | `debug` のログレベルを表す整数値。                                     |
| `INFO`             | `int`       | `info` のログレベルを表す整数値。                                      |
| `WARN`             | `int`       | `warn` のログレベルを表す整数値。                                      |
| `ERROR`            | `int`       | `error` のログレベルを表す整数値。                                     |
| `marker`           | `Marker`    | ログ出力のときに指定されたマーカーオブジェクト。                       |
| `throwable`        | `Throwable` | ログ出力のときに渡された例外オブジェクト。                             |

より詳細な情報は [JaninoEventEvaluator | 第７章フィルター](http://logback.qos.ch/manual/filters_ja.html#JaninoEventEvaluator) を参照のこと。

## フィルターを自作する
```java:MyFilter.java
package sample.logback;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

public class MyFilter extends Filter<ILoggingEvent> {
    
    private String value;
    
    @Override
    public FilterReply decide(ILoggingEvent event) {
        if (event.getMessage().contains(this.value)) {
            return FilterReply.ACCEPT;
        } else {
            return FilterReply.DENY;
        }
    }

    public void setValue(String value) {
        this.value = value;
    }
}
```

```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="sample.logback.MyFilter">
      <value>hoge</value>
    </filter>

    <encoder>
      <pattern>%m%n</pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
```

```java:Main.java
package sample.logback;

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

public class Main {

    public static void main(String[] args) throws Exception {
        Logger logger = LoggerFactory.getLogger(Main.class);
        
        logger.info("a hoge");
        logger.info("Hoge");
        logger.info("hoge b");
        logger.info("hoGe");
    }
}
```

```text:実行結果
a hoge
hoge b
```

- `Filter` クラスを継承することで、任意のフィルターを自作することができる。
- `decide()` メソッドをオーバーライドして、フィルタリングの定義を実装する。
    - `FilterReply.ACCEPT` を返せば、後続のフィルターを飛ばして出力を許可する。
    - `FilterReply.DENY` を返せば、後続のフィルターを飛ばして出力を拒否する。
    - `FilterReply.NEUTRAL` を返せば、後続のフィルターに処理を続けさせる。
- setter を定義することで、任意のオプションを `<filter>` タグの子要素として指定できるようになる。

# 設定例
## info 以上のログをコンソールとファイルに出力する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property name="format" value="[%-5level] %date %-15logger{15} - %m%n" />

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>sample.log</file>
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="FILE" />
  </root>
</configuration>
```

## ファイル出力は特定のパッケージ以下だけ
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property name="format" value="[%-5level] %date %-15logger{15} - %m%n" />

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>sample.log</file>
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <logger name="sample">
    <appender-ref ref="FILE" />
  </logger>

  <root level="info">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
```

## ログファイルのローテーションを設定
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property name="format" value="[%-5level] %date %-15logger{15} - %m%n" />

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>log/sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>log/%d{yyyyMM,aux}/sample-%d{yyyyMMdd}.log</fileNamePattern>
      <maxHistory>90</maxHistory>
    </rollingPolicy>
    
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <logger name="sample">
    <appender-ref ref="FILE" />
  </logger>

  <root level="info">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
```

- `log` フォルダの下に月ごとのフォルダを作り、その下に過去ログを格納。
- 最大３ヶ月（９０日）分のログを保持しておく。

## 環境変数 `LOG_LEVEL` に合わせて出力のログレベルを切り替える  
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property name="format" value="[%-5level] %date %-15logger{15} - %m%n" />

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>log/sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>log/%d{yyyyMM,aux}/sample-%d{yyyyMMdd}.log</fileNamePattern>
      <maxHistory>90</maxHistory>
    </rollingPolicy>
    
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <logger name="sample">
    <appender-ref ref="FILE" />
  </logger>

  <root level="${LOG_LEVEL:-info}">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
```

- 未指定の場合は `info` レベルで出力。

## ERROR レベルの出力があったらメールを送信する
```xml:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
  <property name="format" value="[%-5level] %date %-15logger{15} - %m%n" />

  <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    <smtpHost>smtp.gmail.com</smtpHost>
    <smtpPort>465</smtpPort>
    <SSL>true</SSL>
    <username>xxxxx@gmail.com</username>
    <password>xxxxx</password>

    <to>xxxxx@gmail.com</to>
    <from>xxxxx@gmail.com</from>
    <subject>[System Error] %date{yyyy-MM-dd HH:mm:ss} - %m</subject>

    <asynchronousSending>false</asynchronousSending>

    <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
      <bufferSize>50</bufferSize>
    </cyclicBufferTracker>

    <layout class="ch.qos.logback.classic.PatternLayout">
      <pattern>${format}</pattern>
    </layout>
  </appender>

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>log/sample.log</file>
    
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>log/%d{yyyyMM,aux}/sample-%d{yyyyMMdd}.log</fileNamePattern>
      <maxHistory>90</maxHistory>
    </rollingPolicy>
    
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${format}</pattern>
    </encoder>
  </appender>
  
  <logger name="sample">
    <appender-ref ref="FILE" />
  </logger>

  <root level="${LOG_LEVEL:-info}">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="EMAIL" />
  </root>
</configuration>
```

- メール送信は、念のため同期処理で実行。

# eclipse での logback.xml の警告表示を無くす
eclipse でスキーマ定義のない xml ファイルを作ると、黄色く警告アイコンが表示されて鬱陶しい。

これを回避するには、先頭に `<!DOCTYPE logback>` と入れてあげるといいらしい。

参考： [logbackへ移行するの巻 - R42日記](http://takahashikzn.root42.jp/entry/20110320/1300630485)

```diff:logback.xml
  <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE logback>
  <configuration>
    ...
  </configuration>
```


# 参考
- [Javaのログ出力: 道具と考え方][1]
- [第1章：はじめに | Logback](http://logback.qos.ch/manual/introduction_ja.html)
- [Logback-0.9.19 以降の設定記法がちょっと変わっていた - gishi_yamaのブログ](http://gishi-yama.hatenablog.jp/entry/20100409/1270783030)
- [Logback error 'java.lang.ClassNotFoundException: org.codehaus.janino.ScriptEvaluator' - Chris Stucchio](https://www.chrisstucchio.com/blog/2014/logback_class_not_found_exception.html)

[1]: http://www.slideshare.net/miyakawataku/concepts-and-tools-of-logging-in-java