CLOVER🍀

That was when it all began.

他のロギングライブラリの呼び出しをLog4j2へ転送する+α

Log4j2は、他のロギングライブラリの呼び出しを、Log4j2に転送するコンポーネントを提供しています。

要は、他のロギングライブラリのふりをしてLog4j2へ流すコンポーネントがあるということですね。

以下のパターンがあります。

  • Log4j 1.2 → Log4j2
  • Commons Logging(JCL) → Log4j2
  • SLF4J → Log4j2
  • Log4j2 → SLF4J

最後だけ、逆ですね。これが+αの部分です。

それぞれのMaven依存関係は、こちらに網羅されています。

Maven and Ivy Artifacts
http://logging.apache.org/log4j/2.x/maven-artifacts.html

では、それぞれ簡単に使っていってみます。なお、基本的にこれらのAPIを動作させるのに、本物のライブラリ(例えばLog4j 1.2のJARファイル)は不要です。

以降の例では、少なくともpom.xmlに以下のLog4j2 Core APIの依存関係が定義されているものとします。

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.0-rc2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.0-rc2</version>
    </dependency>

また、log4j2.xmlは以下の定義とします。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{yyy/MM/dd HH:mm:ss.SSS} %-5level - [log4j2] %msg%n%ex"/>
    </Console>
  </Appenders>

  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

*Log4j2で出力したことがわかりやすいように、[log4j2]を入れました

Log4j 1.2 → Log4j2

Log4j 1.2のAPIを使用して出力された内容を、Log4j2へ送るコンポーネントです。

Log4j 1.2 Bridge
http://logging.apache.org/log4j/2.x/log4j-1.2-api/index.html

Maven依存関係には、以下を加えます。

    <!-- Log4j 1.2 Bridge -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-1.2-api</artifactId>
      <version>2.0-rc2</version>
    </dependency>

Log4j 1.2自体は不要です。他のライブラリの依存関係にLog4j 1.2が含まれている場合は、excludeして使うような利用方法になるでしょう。

サンプルコード。
src/main/java/org/littlewings/log4j2/Log4jToLog4j2.java

package org.littlewings.log4j2;

import org.apache.log4j.Logger;

public class Log4jToLog4j2 {
    public static void main(String... args) {
        Logger logger = Logger.getLogger(Log4jToLog4j2.class);
        logger.info("Log4j 1.2のAPIから、Log4j2へ出力");
    }
}

実行結果。

2014/06/28 22:02:00.784 INFO  - [log4j2] Log4j 1.2のAPIから、Log4j2へ出力

Log4j 1.2のAPIを使用して、Log4j2へログ出力を転送できましたね。

Commons Logging → Log4j2

Commons LoggingのAPIを使用して出力された内容を、Log4j2へ送るコンポーネントです。

Commons Logging Bridge
http://logging.apache.org/log4j/2.x/log4j-jcl/index.html

Maven依存関係には、以下を加えます。

    <!-- Log4j2 Commons Logging Bridge -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-jcl</artifactId>
      <version>2.0-rc2</version>
    </dependency>

サンプルコード。
src/main/java/org/littlewings/log4j2/JclToLog4j2.java

package org.littlewings.log4j2;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JclToLog4j2 {
    public static void main(String... args) {
        Log logger = LogFactory.getLog(JclToLog4j2.class);
        logger.info("Commons LoggingのAPIから、Log4j2へ出力");
    }
}

実行結果。

2014/06/28 22:04:58.327 INFO  - [log4j2] Commons LoggingのAPIから、Log4j2へ出力

Commons Loggingのふりをして、Log4j2へログ出力を転送できました。

SLF4J → Log4j2

SLF4JのAPIを使用して出力された内容を、Log4j2へ送るコンポーネントです。

Log4j 2 SLF4J Binding
http://logging.apache.org/log4j/2.x/log4j-slf4j-impl/index.html

なぜ、Bridgeと言ったりBindingと言ったりするのか…?なお、後述のSLF4JへのAdapterと一緒に使用してはいけません。

Maven依存関係には、以下を加えます。

    <!-- SLF4J Bridge -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>2.0-rc2</version>
    </dependency>

サンプルコード。
src/main/java/org/littlewings/log4j2/Slf4jToLog4j2.java

package org.littlewings.log4j2;

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

public class Slf4jToLog4j2 {
    public static void main(String... args) {
        Logger logger = LoggerFactory.getLogger(Slf4jToLog4j2.class);
        logger.info("SLF4JのAPIから、Log4j2へ出力");
    }
}

実行結果。

2014/06/28 22:11:05.113 INFO  - [log4j2] SLF4JのAPIから、Log4j2へ出力

こちらも、SLF4JのふりをしてLog4j2へログ出力を転送しています。

Log4j2 → SLF4J

先ほどまでとは、方向が逆のコンポーネントになります。

Log4j 2 to SLF4J Adapter
http://logging.apache.org/log4j/2.x/log4j-to-slf4j/index.html

これまでは、

「とあるロギングライブラリのAPI呼び出し」をLog4j2へ転送していましたが、

こちらは

「Log4j2の呼び出しをSLF4Jへ転送する」となります。

このため、先ほど紹介した「Log4j 2 SLF4J Binding」と一緒に使うと、Log4j2のAPIとSLF4JのAPIの行き来を繰り返すことになってしまうため、両方同時に使用してはいけません。

今回のMaven依存関係は、このようになります。

    <!-- SLF4J Adapter -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-to-slf4j</artifactId>
      <version>2.0-rc2</version>
    </dependency>

    <!-- SLF4J & Logback -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.7</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.2</version>
    </dependency>

「Log4j 2 to SLF4J Adapter」に加えて、SLF4J自体が必要です。裏は、Logbackとします。

ログ出力はLogbackなので、設定ファイルも用意しました。
src/main/resources/logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy/MM/dd HH:mm:ss.SSS} %-5level - [SLF4J &amp; Logback] %msg%n</pattern>
    </encoder>
  </appender>

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

SLF4JとLogbackを自己主張。

サンプルコードは、このようになります。
src/main/java/org/littlewings/log4j2/Log4j2ToSlf4j.java

package org.littlewings.log4j2;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Log4j2ToSlf4j {
    public static void main(String... args) {
        Logger logger = LogManager.getLogger(Log4j2ToSlf4j.class);
        logger.info("Log4j2のAPIから、SLF4Jへ出力");
    }
}

今までと違い、表に出てくるのはLog4j2のAPIとなります。

実行結果。

2014/06/28 22:18:23.460 INFO  - [SLF4J & Logback] Log4j2のAPIから、SLF4Jへ出力

注意点としては

Use of this adapter may cause some loss of performance as the Log4j 2 Messages must be formatted before they can be passed to SLF4J. With Log4j 2 as the implementation these would normally be formatted only when they are accessed by a Filter or Appender.

http://logging.apache.org/log4j/2.x/log4j-to-slf4j/index.html

とのことなので、Log4j2のAPIからSLF4Jにメッセージを渡す時に、変換が必要なためパフォーマンスが犠牲になるとのことです。まあ、そりゃそうですよね…。

というか、このパターンは使うのでしょうか??

「Log4j 2 to SLF4J Adapter」はちょっと特殊ですが、その他のコンポーネントを使ってLog4j2が使えるといいなぁ、と。特にLog4j 1.2の置き換えに。