1. 概要

イベントのログ記録は、ソフトウェア開発の重要な側面です。 Javaエコシステムで利用できるフレームワークはたくさんありますが、Log4Jは、その柔軟性とシンプルさから、何十年もの間最も人気があります。

Log4j 2 は、従来のLog4jフレームワークの新しく改良されたバージョンです。

この記事では、実際の例を使用して、最も一般的なアペンダー、レイアウト、およびフィルターを紹介します。

Log4J2では、アペンダーは単にログイベントの宛先です。 コンソールのように単純な場合もあれば、他のRDBMSのように複雑な場合もあります。 レイアウトはログの表示方法を決定し、フィルターはさまざまな基準に従ってデータをフィルター処理します。

2. 設定

いくつかのロギングコンポーネントとその構成を理解するために、それぞれがlog4J2.xml構成ファイルとJUnit4テストクラスで構成されるさまざまなテストユースケースを設定しましょう。

2つのMaven依存関係は、すべての例に共通です。

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
    <type>test-jar</type>
    <scope>test</scope>
</dependency>

メインのlog4j-coreパッケージに加えて、パッケージに属する「test jar」を含めて、一般的でない名前の構成ファイルのテストに必要なコンテキストルールにアクセスする必要があります。

3. デフォルト設定

ConsoleAppender は、 Log4J2コアパッケージのデフォルト構成です。 単純なパターンでシステムコンソールにメッセージを記録します。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="ConsoleAppender" target="SYSTEM_OUT">
            <PatternLayout 
              pattern="%d [%t] %-5level %logger{36} - %msg%n%throwable"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="ERROR">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

この単純なXML構成のタグを分析してみましょう。

  • Configuration Log4J 2構成ファイルのルート要素と属性status は、必要な内部Log4Jイベントのレベルです。記録する
  • アペンダーこの要素は1つ以上のアペンダーを保持しています。 ここでは、標準出力でシステムコンソールに出力するアペンダーを構成します
  • ロガーこの要素は、複数の構成済みロガー要素で構成できます。 特別なルートタグを使用すると、アプリケーションからすべてのログメッセージを受信する名前のない標準ロガーを構成できます。 各ロガーは最小ログレベルに設定できます
  • AppenderRef この要素は、Appendersセクションからの要素への参照を定義します。 したがって、属性’ref‘はアペンダー’name‘属性とリンクされています。

対応する単体テストも同様に簡単です。 ロガー参照を取得し、2つのメッセージを出力します。

@Test
public void givenLoggerWithDefaultConfig_whenLogToConsole_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger(getClass());
    Exception e = new RuntimeException("This is only a test!");

    logger.info("This is a simple message at INFO level. " +
      "It will be hidden.");
    logger.error("This is a simple message at ERROR level. " +
    "This is the minimum visible level.", e);
}

4. ConsoleAppender With PatternLayout

別のXMLファイルでカスタマイズされたカラーパターンを使用して新しいコンソールアペンダーを定義し、それをメイン構成に含めましょう。

<?xml version="1.0" encoding="UTF-8"?>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
    <PatternLayout pattern="%style{%date{DEFAULT}}{yellow}
      %highlight{%-5level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green} 
      %message"/>
</Console>

このファイルは、実行時に Log4J2に置き換えられるいくつかのパターン変数を使用しています。

  • %sスタイル{…}{colorname} これにより、最初の角かっこペア()のテキストが特定の色()で印刷されます。 colorname )。
  • %highlight{…}{FATAL= colorname、…} これは「style」変数に似ています。 ただし、ログレベルごとに異なる色を指定できます。
  • %d ate {format} これは、指定されたformatの現在の日付に置き換えられます。 ここでは、「DEFAULT」DateTime形式 yyyy -MM-dd HH:mm:ss、SSS’を使用しています。
  • %-5le vel ログメッセージのレベルを右揃えで出力します。
  • %message 生のログメッセージを表します

しかし、にはさらに多くの変数とフォーマットが存在します PatternLayoutあなたはそれらを参照することができます Log4J 2 の公式ドキュメント。

次に、定義されたコンソールアペンダーをメイン構成に含めます。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" xmlns:xi="http://www.w3.org/2001/XInclude">
    <Appenders>
        <xi:include href="log4j2-includes/
          console-appender_pattern-layout_colored.xml"/>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

ユニットテスト:

@Test
public void givenLoggerWithConsoleConfig_whenLogToConsoleInColors_thanOK() 
  throws Exception {
    Logger logger = LogManager.getLogger("CONSOLE_PATTERN_APPENDER_MARKER");
    logger.trace("This is a colored message at TRACE level.");
    ...
}

5. JSONLayoutおよびBurstFilterを使用した非同期ファイルアペンダー

ログメッセージを非同期的に書き込むと便利な場合があります。 たとえば、アプリケーションのパフォーマンスがログの可用性よりも優先される場合です。

このようなユースケースでは、AsyncAppender。を使用できます。

この例では、非同期のJSONログファイルを構成しています。 さらに、指定されたレートでログ出力を制限するバーストフィルターを含めます。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        ...
        <File name="JSONLogfileAppender" fileName="target/logfile.json">
            <JSONLayout compact="true" eventEol="true"/>
            <BurstFilter level="INFO" rate="2" maxBurst="10"/>
        </File>
        <Async name="AsyncAppender" bufferSize="80">
            <AppenderRef ref="JSONLogfileAppender"/>
        </Async>
    </Appenders>
    <Loggers>
        ...
        <Logger name="ASYNC_JSON_FILE_APPENDER" level="INFO"
          additivity="false">
            <AppenderRef ref="AsyncAppender" />
        </Logger>
        <Root level="INFO">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

次のことに注意してください。

  • JSONLayout は、行ごとに1つのログイベントを書き込むように構成されています
  • BurstFilter は、イベントが2つ以上ある場合、「INFO」レベル以上のすべてのイベントをドロップしますが、最大で10個のイベントがドロップされます。
  • AsyncAppenderは80個のログメッセージのバッファーに設定されています。 その後、バッファはログファイルにフラッシュされます

対応するユニットテストを見てみましょう。 追加されたバッファをループで埋め、ディスクに書き込んでログファイルの行数を調べます。

@Test
public void givenLoggerWithAsyncConfig_whenLogToJsonFile_thanOK() 
  throws Exception {
    Logger logger = LogManager.getLogger("ASYNC_JSON_FILE_APPENDER");

    final int count = 88;
    for (int i = 0; i < count; i++) {
        logger.info("This is async JSON message #{} at INFO level.", count);
    }
    
    long logEventsCount 
      = Files.lines(Paths.get("target/logfile.json")).count();
    assertTrue(logEventsCount > 0 && logEventsCount <= count);
}

6. RollingFileアペンダーとXMLLayout

次に、ローリングログファイルを作成します。 構成されたファイルサイズの後、ログファイルは圧縮およびローテーションされます。

今回はXMLlayoutを使用しています。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <RollingFile name="XMLRollingfileAppender"
          fileName="target/logfile.xml"
          filePattern="target/logfile-%d{yyyy-MM-dd}-%i.log.gz">
            <XMLLayout/>
            <Policies>
                <SizeBasedTriggeringPolicy size="17 kB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="XML_ROLLING_FILE_APPENDER" 
       level="INFO" additivity="false">
            <AppenderRef ref="XMLRollingfileAppender" />
        </Logger>
        <Root level="TRACE">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

次のことに注意してください。

  • RollingFile appenderには、’filePattern’属性があります。これは、ローテーションされたログファイルに名前を付けるために使用され、プレースホルダー変数を使用して構成できます。 この例では、ファイルのサフィックスの前に日付とカウンターが含まれている必要があります。
  • XMLLayout のデフォルト構成では、ルート要素なしで単一のログイベントオブジェクトが書き込まれます。
  • ログファイルのローテーションには、サイズベースのポリシーを使用しています。

ユニットテストクラスは、前のセクションのクラスのようになります。

@Test
public void givenLoggerWithRollingFileConfig_whenLogToXMLFile_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger("XML_ROLLING_FILE_APPENDER");
    final int count = 88;
    for (int i = 0; i < count; i++) {
        logger.info(
          "This is rolling file XML message #{} at INFO level.", i);
    }
}

7. Syslogアペンダー

ログに記録されたイベントをネットワーク経由でリモートマシンに送信する必要があるとします。 Log4J2を使用してこれを行う最も簡単な方法は、 Syslog Appender:を使用することです。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        ...
        <Syslog name="Syslog" 
          format="RFC5424" host="localhost" port="514" 
          protocol="TCP" facility="local3" connectTimeoutMillis="10000" 
          reconnectionDelayMillis="5000">
        </Syslog>
    </Appenders>
    <Loggers>
        ...
        <Logger name="FAIL_OVER_SYSLOG_APPENDER" 
          level="INFO" 
          additivity="false">
            <AppenderRef ref="FailoverAppender" />
        </Logger>
        <Root level="TRACE">
            <AppenderRef ref="Syslog" />
        </Root>
    </Loggers>
</Configuration>

Syslog タグの属性:

  • name はアペンダーの名前を定義し、一意である必要があります。 同じアプリケーションと構成に対して複数のSyslogアペンダーを使用できるため
  • format BSDまたはRFC5424のいずれかに設定でき、Syslogレコードはそれに応じてフォーマットされます
  • ホストとポート リモートSyslogサーバーマシンのホスト名とポート
  • プロトコルTCPまたはUPDのどちらを使用するか
  • ファシリティイベントが書き込まれるSyslogファシリティ
  • connectTimeoutMillis 確立された接続を待機する期間。デフォルトはゼロです。
  • reconnectionDelayMillis 接続を再試行する前に待機する時間

8. FailoverAppender

これで、1つのアペンダーがログイベントの処理に失敗し、データを失いたくない場合があります。 このような場合、FailoverAppenderが便利です。

たとえば、 Syslog アペンダーがリモートマシンへのイベントの送信に失敗した場合、そのデータを失う代わりに、一時的にFileAppenderにフォールバックする可能性があります。

FailoverAppender は、プライマリアペンダーとセカンダリアペンダーの数を受け取ります。 プライマリが失敗した場合、プライマリが成功するか、試行するセカンダリがなくなるまで、セカンダリのログイベントを順番に処理しようとします。

<Failover name="FailoverAppender" primary="Syslog">
    <Failovers>
        <AppenderRef ref="ConsoleAppender" />
    </Failovers>
</Failover>

それをテストしてみましょう:

@Test
public void givenLoggerWithFailoverConfig_whenLog_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger("FAIL_OVER_SYSLOG_APPENDER");
    Exception e = new RuntimeException("This is only a test!"); 

    logger.trace("This is a syslog message at TRACE level.");
    logger.debug("This is a syslog message at DEBUG level.");
    logger.info("This is a syslog message at INFO level. 
      This is the minimum visible level.");
    logger.warn("This is a syslog message at WARN level.");
    logger.error("This is a syslog message at ERROR level.", e);
    logger.fatal("This is a syslog message at FATAL level.");
}

9. JDBC Appender

JDBCアペンダーは、標準のJDBCを使用してログイベントをRDBMSに送信します。 接続は、任意のJNDIデータソースまたは任意の接続ファクトリを使用して取得できます。

基本構成は、DataSourceまたはConnectionFactory、ColumnConfigs、および tableName:で構成されます。

<JDBC name="JDBCAppender" tableName="logs">
    <ConnectionFactory 
      class="com.baeldung.logging.log4j2.tests.jdbc.ConnectionFactory" 
      method="getConnection" />
    <Column name="when" isEventTimestamp="true" />
    <Column name="logger" pattern="%logger" />
    <Column name="level" pattern="%level" />
    <Column name="message" pattern="%message" />
    <Column name="throwable" pattern="%ex{full}" />
</JDBC>

では、試してみましょう。

@Test
public void givenLoggerWithJdbcConfig_whenLogToDataSource_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger("JDBC_APPENDER");
    final int count = 88;
    for (int i = 0; i < count; i++) {
        logger.info("This is JDBC message #{} at INFO level.", count);
    }

    Connection connection = ConnectionFactory.getConnection();
    ResultSet resultSet = connection.createStatement()
      .executeQuery("SELECT COUNT(*) AS ROW_COUNT FROM logs");
    int logCount = 0;
    if (resultSet.next()) {
        logCount = resultSet.getInt("ROW_COUNT");
    }
    assertTrue(logCount == count);
}

10. 結論

この記事では、Log4J2でさまざまなロギングアペンダー、フィルター、およびレイアウトを使用する方法と、それらを構成する方法の非常に簡単な例を示します。

記事に付属する例は、GitHubから入手できます。