SLF4J、Logback、Log4Jの関係を挙動とともに整理する

SLF4J、Logback、Log4Jの違いや関係を挙動とともに整理してみます。

## SLF4Jとは
Javaのロギング実装の柔軟な切り替えを実現するFacadeのことをSLF4Jといいます。
※[SLF4J](https://www.slf4j.org/)
Facade（ファサード）とは「建物の正面」を意味していて、GoFのデザインパターンの1つ「Facadeパターン」のことを指します。以下はFacadeパターンのクラス図です（TECHSCOREより）。
![facade2.gif](https://qiita-image-store.s3.amazonaws.com/0/23101/4aa8a460-53e7-bce1-1eae-eddda8b59a80.gif)

Javaのロギング実装には、Log4JやLog4J 2、Logback等がありますが、これらの実装の窓口となるのがSLF4Jです。上記図では、FacadeがSLF4J、classAがLog4J、classBがLogbackなイメージです。

公式にある以下の図がイメージしやすいかと思います。
![concrete-bindings.png](https://qiita-image-store.s3.amazonaws.com/0/23101/9f6ee28c-5530-0335-ee19-1961422a3259.png)
つまり、アプリケーションから見るとSLF4Jはロギング実装のインターフェースの役割を担い、Logback、Log4Jはロギング実装そのものになります。

## SLF4Jの威力
SLF4Jの威力、と言いつつもFacadeパターン（突き詰めるとインターフェースの威力）になるのですが、SLF4Jを利用することでどんなメリットがあるかを以下のコードで確認してみます。

```App.java
public class App {
	public static void main(String[] args) {
		Logger logger = LoggerFactory.getLogger(App.class);
		logger.info("INFO LOG!!  Logger Class=" + logger.getClass());
	}
}
```

#### ロギング実装にLogbackを使う
まずは、ロギング実装にLogbackを使ってみます。Logbackを使うためにpom.xmlに以下の通り依存関係を追加します。

```pom.xml
// Logbackの場合、この追加だけでMavenの推移的依存関係により、SLF4J(slf4j-api)とlogback-coreも依存関係に追加されます。
<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
	<version>1.2.3</version>
</dependency>
```

ビルドしApp.javaを実行してみます。すると、コンソールに以下が出力されます。

```Console.
23:53:02.124 [main] INFO co.jp.ars.App - INFO LOG!!  Logger Class=class ch.qos.logback.classic.Logger
```

「Logger Class=class ch.qos.logback.classic.Logger」とあるように、ロギング実装にLogbackが使われていることが確認できます。

#### ロギング実装にLog4Jを使う
次に、ロギング実装にLog4Jを使ってみます。Log4Jを使うためにpom.xmlに以下の通り依存関係を追加します。

```pom.xml
// Log4Jの場合、この追加だけでMavenの推移的依存関係により、log4j-x.x.x.jarも依存関係に追加されます。
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.25</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.7.25</version>
</dependency>
```

Log4Jを使う場合、log4j.xml（もしくはlog4j.properties）が必要なので、クラスパス上に以下の通りlog4j.xmlを作成します。

```log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	<appender name="demo" class="org.apache.log4j.ConsoleAppender">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%t %-5p %c{2} - %m%n" />
		</layout>
	</appender>
	<logger name="co.jp.ars">
		<level value="info" />
		<appender-ref ref="demo" />
	</logger>
</log4j:configuration>
```

ビルドしApp.javaを実行してみます。すると、コンソールに以下が出力されます。

```Console.
main INFO  ars.App - INFO LOG!!  Logger Class=class org.slf4j.impl.Log4jLoggerAdapter
```

「Logger Class=class org.slf4j.impl.Log4jLoggerAdapter」とありますが、Log4jLoggerAdapterの実装は以下の通りとなっており、Loggerの実態は「org.apache.log4j.Logger」になっていることが確認できます（つまりロギング実装にLog4Jが使われていることが確認できます）。

```Log4jLoggerAdapter.java
public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, Serializable {

    private static final long serialVersionUID = 6182834493563598289L;

    final transient org.apache.log4j.Logger logger;

    // ～省略～

    public void info(String msg) {
        logger.log(FQCN, Level.INFO, msg, null);
    }

    // ～省略～
}
```

SLF4J + Log4Jの場合、両者の間にSLF4Jバインディングと呼ばれる中間アダプターが入ります。その中間アダプターが上記の「Log4jLoggerAdapter 」になります。以下再掲です。
![concrete-bindings.png](https://qiita-image-store.s3.amazonaws.com/0/23101/9f6ee28c-5530-0335-ee19-1961422a3259.png)

#### 何が嬉しいのか？
ロギング実装を設定で切り替えただけで、ログ出力を行っているアプリ（App.java）には一切手を加えていません。冒頭で「Javaのロギング実装の柔軟な切り替えを実現するFacadeのことをSLF4Jといいます」と述べましたが、SLF4JというFacadeを使うことでロギング実装を柔軟に切り替えることが可能となります。

## Logback、Log4jのトレンド
ちなみに、ロギング実装のトレンド的にはさほど変わりはありません（2018/12/6時点）
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/23101/5cce6766-8cd8-4754-27af-835ba2c50709.png)


以上です。

## 参考
* [SLF4J](https://www.slf4j.org/)