log4j2.xmlの基本的な仕組みを理解する

# 動機、前提など
- 毎回、久しぶりにlog4jを触るたびにすっかり使い方を忘れてしまっているので、自分なりに構造を理解しようと思った。
- せっかくの機会なので根本的な構造をまず理解したいと思ったので、オプションの指定は可能な限り削った形を基本にしている。
- また、基本的な使い方に限定しているので、実際の開発でよく使われるような設定も場合によっては端折っている。

# 使用した環境
- Java1.8
- log4j2.9.1

# ログ出力するJavaソース

```java
logger.fatal("fatal!");
logger.error("error!");
logger.warn("warn!");
logger.info("info!");
logger.debug("debug!");
logger.trace("trace!");
```

# 0.設定ファイル無しの場合

- 設定ファイルが無くてもデフォルトの設定でログ出力が行われる（その旨エラーメッセージは表示されるが）。
- デフォルト設定は、
    - RootLoggerにConsoleAppenderを適用
    - ConsoleAppenderにPatternLayoutを適用
    - patternは`"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"`
    - 出力ログレベルはERROR

```
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging.
15:51:56.904 [main] FATAL Test - fatal!
15:51:56.907 [main] ERROR Test - error!
```

# 1.最低限の設定ファイル

```xml:log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
</Configuration>
```

ここまで記述すれば、エラーは出なくなりつつ、デフォルトの形式でログが出力される。

```
16:52:31.453 [main] FATAL Test - fatal!
16:52:31.453 [main] ERROR Test - error!
```

- Configurationタグでstatus属性を使用すると、log4j2自体のログの出力レベルを指定できる。
- log4j2-test.xmlというファイルがあるとこちらのほうが優先して読み込まれる。一時的に設定を差し替えたい時に便利。
- xml形式以外にも、json形式やyaml形式でも記述可能。あと、レガシーなproperties形式も。
- 優先順位は、properties>yaml>json>xmlとなっている。根拠は不明（なぜxmlが一番弱い！）。

参考：
http://logging.apache.org/log4j/2.x/manual/configuration.html#ConfigurationSyntax
http://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticConfiguration

# 2.最低限のAppenderとLogger

オプションを何も指定しない`ConsoleAppender`と、それを指定しただけの`RootLogger`の組み合せ。
`Appender`の`name`属性で付けた名前を、`Logger`の`AppenderRef`の`ref`属性で指定することにより関連づけを行う。

```xml:log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="STDOUT">
        </Console>
    </Appenders>
    <Loggers>
        <Root>
            <AppenderRef ref="STDOUT"/>
        </Root>
    </Loggers>
</Configuration>
```
```
fatal!
error!
```

メッセージだけの素っ気ないログが出力される。

- Appenderはログの出力先（出力方法）・出力レイアウトなどを定義する。
- Loggerはログの出力対象や使用するAppenderを指定する。Appenderは複数指定が可能。Appenderを指定しなくてもエラーにはならないが、当然ログはどこにも出力されない。

- ConsoleAppenderはデフォルトは標準出力に出力される。標準エラー出力に出力したい場合は、target="SYSTEM_ERR"を指定する。
- 実際には出力先が標準出力の場合でも、明示的にtarget="SYSTEM_OUT"と記述するケースが多い。

- Loggerにlevelを指定することで、そのLoggerのログ出力レベルを制御できる。

```xml
<Root level="info">
```
```
fatal!
error!
warn!
info!
```

参考：
http://logging.apache.org/log4j/2.x/manual/configuration.html#Loggers
http://logging.apache.org/log4j/2.x/manual/configuration.html#Appenders

# 3.レイアウトの指定
出力レイアウトを指定する場合はAppenderに対してLayoutを指定する。

```xml
<Console name="STDOUT">
    <PatternLayout pattern="%d %p %m%n"/>
</Console>
```
```
2017-10-24 18:12:23,469 FATAL fatal!
2017-10-24 18:12:23,472 ERROR error!
2017-10-24 18:12:23,472 WARN warn!
2017-10-24 18:12:23,472 INFO info!
```

参考：http://logging.apache.org/log4j/2.x/manual/layouts.html

# 4.子Logger
必要に応じてRootLoggerの子Loggerを定義する。

```xml
<Loggers>
    <Logger name="Test">
        <AppenderRef ref="STDOUT"/>
    </Logger>
    <Root>
        <AppenderRef ref="STDOUT"/>
    </Root>
</Loggers>
```

- 子Loggerはname属性が必須で、ここにログ出力対象のクラスまたはパッケージを指定する。
- 存在しないクラス名やパッケージ名を指定してもエラーにはならないので注意。
- RootLoggerにはname属性を指定できない。

```
2017-10-25 13:06:51,840 FATAL fatal!
2017-10-25 13:06:51,840 FATAL fatal!
2017-10-25 13:06:51,840 ERROR error!
2017-10-25 13:06:51,840 ERROR error!
```

Loggerは子から親に伝播するため、ログ出力条件が同じだとログが２重に出力される。
伝播を抑止するには、子Loggerにadditivity="false"を指定する。

```xml
<Logger name="Test" additivity="false">
```
```
2017-10-25 13:15:59,282 FATAL fatal!
2017-10-25 13:15:59,282 ERROR error!
```

RootLoggerは親を持たないのでadditivityの指定はできない。

参考：http://logging.apache.org/log4j/2.x/manual/configuration.html#Additivity

# 5.フィルタ
フィルタは、特定の条件のときだけログ出力させる／させないの制御を行いたいときに使用する。
フィルタは以下の4箇所に設定できる。

- Configuration - コンテキスト全体に対するフィルタ
- Logger - 特定のLoggerに対するフィルタ
- Appender - 特定のAppenderに対するフィルタ
- AppenderRef - 特定のLogger×Appenderに対するフィルタ

ログ出力の際にフィルタが適用されると判定が行われ、以下の3つのステータスのいずれかが返る。

- ACCEPT - ログ出力確定
- DENY - ログ非出力確定
- NEUTRAL - 判定を次のフィルタに移譲

```xml
<Configuration>
    <RegexFilter regex=".*err.*"/>
```

複数のフィルタを指定する場合はFiltersで囲む。

```xml
<Configuration>
    <Filters>
        <RegexFilter regex=".*err.*"/>
        <TimeFilter start="9:00:00" end="17:00:00"/>
    </Filters>
```

- フィルタを複数設定した場合は定義した順番に判定が行われる。
- ACCEPTかDENYが返って来た場合はその時点で判定が確定し、残りのフィルタの判定は行われない。
- NEUTRALが返ってきた場合は次のフィルタに判定が移る。
- 次のフィルタがもう無い場合はACCEPT扱いとなる。

参考：http://logging.apache.org/log4j/2.x/manual/filters.html

# 6.プロパティ
プロパティ値を定義し、設定ファイルの中で参照することができる。

```xml
<Configuration>
    <Properties>
        <Property name="myPattern">%d %p %m%n</Property>
    </Properties>
    <Appenders>
        <Console name="STDOUT">
            <PatternLayout pattern="${myPattern}"/>
```

共通要素やリテラル値の抜き出しなどに使用できる。

参考：http://logging.apache.org/log4j/2.x/manual/configuration.html#Properties

# 7.値参照
システム環境変数やシステムプロパティなど、各種の値を設定ファイルの中で参照することができる。

- システム環境変数
    - `${env:os}`, `${env:computername}` など
- システムプロパティ
    - `${sys:java.home}`, `${sys:file.encoding}` など

参考：http://logging.apache.org/log4j/2.x/manual/lookups.html

#8.スクリプト
ScriptFilterやScriptPatternSelectorといった、スクリプトを使用して動作する仕組みがある。
スクリプトは設定ファイルの中に直書きすることもできるし、スクリプトファイルを参照することもできる。
前者の場合は`<Script>`タグを使用し、後者の場合は`<ScriptFile>`タグを使用して定義する。
language属性でスクリプトの種類を指定できるが、今回試したバージョン・環境において指定できる種類は、nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascriptとなっていた。

```xml
<Configuration>
    <Scripts>
        <Script name="checkSunday" language="javascript"><![CDATA[
            var result;
            var sunday = 0;
            var today = new Date();
            var dayOfWeek = today.getDay();
            if (dayOfWeek == sunday) {
                result = true;
            } else {
                result = false;
            }
            result;
        ]]></Script>
    </Scripts>
    <ScriptFilter>
        <ScriptRef ref="checkSunday"/>
    </ScriptFilter>
```

参考：
http://logging.apache.org/log4j/2.x/manual/configuration.html#Scripts
http://logging.apache.org/log4j/2.x/manual/filters.html#Script

#9.設定ファイルの分割
XIncludeを使用すると、設定ファイルを複数のファイルに分割できる。

```xml:log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns:xi="http://www.w3.org/2001/XInclude">
    <Loggers>
        <Root>
            <AppenderRef ref="STDOUT"/>
        </Root>
    </Loggers>
    <xi:include href="log4j2-appender.xml"/>
</Configuration>
```
```xml:log4j2-appender.xml
<?xml version="1.0" encoding="UTF-8"?>
<Appenders>
    <Console name="STDOUT">
        <PatternLayout pattern="%d %p %m%n"/>
    </Console>
</Appenders>
```
参考：http://logging.apache.org/log4j/2.x/manual/configuration.html#XInclude