Log4j2の例チュートリアル – 設定、レベル、Appenders

ようこそ、Apache Log4j2 の例のチュートリアルへ。アプリケーションについて専門家の開発者に尋ねると、最も面倒なことはログに関連しているかもしれません。アプリケーションに適切なログがない場合、メンテナンスは悪夢になります。ほとんどのアプリケーションは、開発テスト、ユニットテスト、統合テストを経ます。しかし、本番環境では常にユニークなシナリオや例外に直面します。したがって、特定のケースで何が起こったのかを把握する唯一の方法は、ログをデバッグすることです。多くのフレームワークはデフォルトのログ記録方法を提供していますが、常に業界標準のログ記録メカニズムを採用するのが最善です。Apache Log4j は最も広く使用されているログ記録フレームワークの1つです。Apache Log4j 2 は、Log4j よりもはるかに優れた次のバージョンです。

Log4j の例のチュートリアル

この Log4j2 の例のチュートリアルでは、Apache Log4j2 を始める方法について学びます。また、Log4j2 のアーキテクチャ、Log4j2 の設定、Log4j2 のログレベル、アペンダー、フィルターなどを探求します。

  1. Log4j2 の概要
  2. Log4j2 のアーキテクチャ
  3. Log4j2 の設定
  4. Log4j2 のレベル
  5. Log4j2 のルックアップ
  6. Log4j2 のアペンダー
  7. Log4j2 のフィルター
  8. Log4j2のレイアウト
  9. どのLog4j2レベルを使用すべきか
  10. Log4j2チュートリアル要約

Log4j2概要

アプリケーションでのログ記録APIの使用は贅沢ではなく、必須です。Log4jは、オープンソースのライブラリであり、Apache Softwareの元で公開およびライセンスされています。Eclipseデバッグやその他のツールを使用してアプリケーションをデバッグできますが、それだけでは本番環境では十分ではなく実現可能ではありません。ログ記録メカニズムは、通常のデバッグでは見つけられないいくつかの利点を提供します。

Category / Operation (Debugging, Logging) Debugging Logging
Human Intervention There’s a need for human intervention No need for human intervention
Persistent Medium Can’t be integrated with persistent storage Can be integrated with persistent storage (Files, Database, NoSQL database, etc.)
May used for Auditing Can’t be used for achieving auditing Can be used for achieving auditing if it’s used efficiently
Sufficient for complicated structure and flow Not sufficient; you may get lost with flow. Sufficient
Productivity Less productive More productive

上記のように、ログ記録メカニズムの使用は、保守コストが少なく効率的です。Apache Log4jはJavaアプリケーションでのログ記録の先駆けであり、それを使用すべきです。

Log4j2アーキテクチャ

Log4jの例チュートリアルに進む前に、Log4j2のアーキテクチャを見てみるのは良いことです。以下の画像は、Log4j2 APIの重要なクラスを示しています。上記のアーキテクチャの詳細な説明は次の通りです:

  • アプリケーションは、特定の名前でLoggerを持つためにLogManagerに問い合わせます。

  • LogManagerは適切なLoggerContextを見つけ、それからLoggerを取得します。

  • ロガーがまだ作成されていない場合、以下の三つの選択肢に従って作成され、LoggerConfigに関連付けられます:

    1. ロガーのインスタンスは、同じ名前を持つLoggerConfigに関連付けられます。たとえば、getLogger(App.class)内のApp.classは、文字列com.journaldev.Appに評価されます。 LoggerConfigの名前は完全修飾クラス名(ソフトウェアコンポーネント)と同じです。
    2. ロガーのインスタンスは、同じロガーの親パッケージを持つLoggerConfigに関連付けられます。たとえば、getLogger("com.journaldev")内のcom.journaldevです。
    3. ロガーのインスタンスは、ルートLoggerConfigに関連付けられます。構成ファイルがない場合や、ロガーの宣言で定義されていない名前のロガーを取得する場合にルートLoggerConfigが使用されます。
  • LoggerConfigオブジェクトは、設定ファイル内のLogger宣言から作成されます。LoggerConfigはまた、LogEventsを処理し、それらを定義されたLog4j2 Appendersに委任します。

  • ルートロガーは、存在の点で特別なケースです。常に存在し、どんなロガー階層の先頭にもあります。

  • 以下の文を使用してルートロガーを取得できます:

    Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    Logger logger = LogManager.getRootLogger();
    
  • log4j2のロガーの名前は大文字と小文字を区別します。

  • ルートロガー以外のすべてのロガーは、その名前をLogManager.getLogger()に渡すことで取得できます。

  • LoggerContextは、アプリケーション内に複数のLoggerContextがある可能性があるため、ログシステムの要となります。各LoggerContextごとにアクティブな設定を行う必要があります。

  • Log4j2の構成には、すべてのログシステムの資産であるLoggerConfig、Appender、Filterなどが含まれます。

  • 同じ名前を渡してLogManager.getLogger()を呼び出すと、常にまったく同じロガーインスタンスへの参照が返されます。

  • ログシステムの構成は、通常、アプリケーションの初期化時に行われます。これには、プログラムで行う方法や、log4j2の構成ファイルを読み取る方法など、さまざまな形式があります。

すべてのロガーはLoggerConfigオブジェクトと関連付けられており、LoggerConfigオブジェクトのセットがロガーの階層を構成しています。この概念はロガー階層として知られています。ロガー階層は、親子関係を持つLoggerConfigオブジェクトのセットで構成されています。すべてのロガー階層で最上位の要素はルートロガーです。Log4j2が構成ファイルを見つけられない場合、エラーとしてのログレベルでのみ、ルートロガーが使用されます。以下の画像は、この場合に表示される警告メッセージを示しています。エラー StatusLogger log4j2 構成ファイルが見つかりません。デフォルトの構成を使用しています:コンソールにエラーのみをログに記録します。 以下の表は、ロガー階層内の親子関係を示しています。

LoggerConfig (Is A) Root com com.journaldev com.journaldev.logging
Root X Child descendant descendant
com Parent X Child descendant
com.journaldev Ancestor Parent X Child
com.journaldev.logging Ancestor Ancestor Parent X

親子関係を明確にするため、上記の表は次のように読み取られます:

  • Rootはcomの親です。
  • Rootはcom.journaldevの先祖です。
  • Rootはcom.journaldev.loggingの先祖です。
  • comはRootの子です。
  • comはcom.journaldevの親です。
  • comはcom.journaldev.loggingの先祖です。
  • com.journaldev.loggingはcom.journaldevの子であり、これ以降も同様です。

LoggerConfigのインスタンスは、その名前に続くドットが子孫の名前の接頭辞である場合、他のLoggerConfigの祖先と言われます。LoggerConfigのインスタンスは、両者の間に交互にする名前がない場合、別のLoggerConfigの親と言われます。

Log4j2の構成

アプリケーションでLog4j2構成を使用する多くの方法があります。

  1. XML、JSON、YAML、またはプロパティファイルで書かれた構成ファイルを使用します。
  2. プログラムで、構成ファクトリと構成実装を作成します。
  3. 構成インターフェースで公開されたAPIを呼び出すことによって、プログラムで。
  4. 内部ロガークラスのメソッドを呼び出すことによって、プログラムで。

主に構成ファイルに焦点を当てますが、特定のロガーに特定のロギング戦略を構成する場合のプログラミングアプローチも知っておくと良いでしょう。まず最初に、構成ファイルを提供しなかった場合を考えてみましょう。 Log4j2の実装は、log4j2構成ファイルの場所を指すSystem変数があると仮定します。log4j.configurationFile

package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App
{
    public static void main( String[] args ) {
    	Logger logger = LogManager.getRootLogger();
    	logger.trace("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
    }
}

A simple log4j2 configuration file will look like below. configuration.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

そして、上記のコードの詳細な説明は次のとおりです:

  • アプリは、LogManager getRootLoggerメソッドを呼び出すことでRootロガーを参照しました。
  • LogManagerからのロガーの参照がLog4jシステムを開始しました。
  • Log4jは、log4j.configurationFileシステムプロパティを調査して、log4j2の設定ファイルを決定します。Log4jの設定はJSON、YAML、XMLで書くことができます。
  • log4j.configurationFileシステムプロパティは、System.setProperties("log4j.configurationFile","FILE_PATH")を介して設定するか、以下の図に示すようにJVMパラメータとして渡すことができます。また、ファイルプロトコルの接頭辞にも注意してください。

  • システムプロパティが定義されていない場合、構成順序は以下の優先順位を取ります:
    • プロパティ構成ファクトリは、クラスパス内の log4j2-test.properties を探します。
    • YAML 構成ファクトリは、クラスパス内の log4j2-test.yaml または log4j2-test.yml を探します。
    • JSON 構成ファクトリは、クラスパス内の log4j2-test.jsn または log4j2-test.json を探します。
    • XML 構成ファクトリは、クラスパス内の log4j2-test.xml を探します。
    • プロパティ構成ファクトリは、クラスパス上の log4j2.properties を探します。
    • YAML 構成ファクトリは、クラスパス内の log4j2.yml または log4j2.yaml を探します。
    • JSON 構成ファクトリは、クラスパス内の log4j2.jsn または log4j2.json を探します。
    • XML 構成ファクトリは、クラスパス内の log4j2.xml を探します。
    • 構成ファイルが提供されなかった場合、DefaultConfiguration が適用され、デフォルトの動作が設定されます:
      • ルートロガーが使用されます。
      • ルートロガーレベルが ERROR に設定されます。
      • ルートロガーはコンソールにログメッセージを伝播します。
      • PatternLayout は %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n に設定されます。

log4j2 の設定ファイルを使用することで、設定が非常に簡単になりますが、プログラムでどのように設定できるかを見てみましょう。これはすべて ConfigurationFactory の使用に関するものです。

package com.journaldev;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
public class App
{
    public static void main( String[] args ) throws FileNotFoundException, IOException {
 
    	// 設定ファクトリーのインスタンスを取得します。オプションは、デフォルトのConfigurationFactory、XMLConfigurationFactory、
    	// YamlConfigurationFactory、およびJsonConfigurationFactoryです。
    	ConfigurationFactory factory =  XmlConfigurationFactory.getInstance();
 
    	// この構成のソースを見つけます。この場所にあるファイルは、空の構成タグだけを含むダミーファイルです。
    	ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
 
    	// 構成からの参照を取得します。
    	Configuration configuration = factory.getConfiguration(configurationSource);
 
    	// デフォルトのコンソールアペンダーを作成します。
    	ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
 
    	// コンソールアペンダーを構成に追加します。
    	configuration.addAppender(appender);
 
    	// loggerConfigを作成します。
    	LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
 
    	// appenderを追加します。
    	loggerConfig.addAppender(appender,null,null);
 
    	// ロガーを作成し、それをloggerConfigインスタンスに関連付けます。
    	configuration.addLogger("com", loggerConfig);
 
    	// コンテキストのインスタンスを取得します。
    	LoggerContext context = new LoggerContext("JournalDevLoggerContext");
 
    	// ロギングシステムを開始します。
    	context.start(configuration);
 
    	// ロガーの参照を取得します。
    	Logger logger = context.getLogger("com");
 
    	// DEBUGメッセージのLogEventを記録します。
    	logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
 
    	// FATALとして構成されたロガーのエラーメッセージのLogEventを記録します。
    	logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
 
    	// Rootによって処理されるはずのERRORメッセージのLogEventを記録します。
    	logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
    }
}
  • Log4j2が提供するConfigurationFactoryのいずれかを使用するか、デフォルトのものを使用できます。XMLConfigurationFactoryを使用してConfigurationFactoryのインスタンスを取得しました。
  • ファクトリーは、対応する構成ファイルを渡すことで必要なConfiguration参照のインスタンスを提供します。
  • Configurationインスタンスは、LoggerContextと共に使用してログシステムを開始します。
  • A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
  • 指定された名前、LEVEL、および使用されていないフィルターでLoggerConfigインスタンスが作成されました。作成されたAppenderは、このLoggerConfigインスタンスに割り当てられます。
  • LoggerConfigインスタンスが構成インスタンスに追加されました。
  • A new instance of LoggerContext is created with defined name.
  • 設定インスタンスがLoggerContextインスタンスに渡され、後者に対してstartが呼び出されました。
  • A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
  • Loggerインスタンスが3つのイベントを発火しましたが、それらはLog4j2 Levelsセクションで説明されます。
  • comロガーは、レベルがFATALであるメッセージを出力するように構成されています。
  • デフォルトでは、RootロガーはレベルがERRORであるメッセージを出力するように構成されています。
  • ERRORメッセージは「com」ロガーによって記録されません。なぜなら、そのレベルがFATALだからです。

同じ構成は、YAML、JSON、またはプロパティファイルを使用して行うこともできます。ただし、log4j2プロパティファイルの構成は、log4jプロパティファイルの構成と異なるため、log4jプロパティファイルの構成をlog4j2で使用しようとしないでください。以下のエラーが発生します。

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

上記のコードを処理すると、以下の出力が得られます:

Logger Name :: com :: Passed Message ::
00:01:27.705 [main] ERROR - Root Logger:: Passed Message As Root Is Configured For ERROR Level messages

最初のログの行はcomロガーからであり、2番目はRootロガーからです。comロガーのエラーメッセージは、そのレベルがFatalであるため、出力されません。

Log4j2 Levels

上記のコード例で分かるように、LoggerConfigを定義するたびに、ログレベルも指定します。デフォルトでは、log4j2のログは加算されます。つまり、特定のロガーが使用されると、すべての親ロガーも使用されます。以下の画像がこの状況を明確に示しています。およびこれに関する説明のポイントは次のとおりです:

  • 前述のように、各ロガーにはLoggerConfigインスタンスが関連付けられています。このloggerConfigは構成スコープで定義されています。
  • ログのレベルはLoggerConfigスコープで決定できます。
  • ロガーはその名前、親パッケージ、またはルートロガー自体を指定して取得できます。
  • ルートロガーは、すべてのLoggerConfig階層の最上位ノードです。
  • com.journaldevロガーを取得し、ログを記録するためのログイベントを開始すると、loggerConfig(net.journaldev)がメッセージをログに記録し、メッセージは階層をさかのぼって伝播します。親のログレベルに関係なく。したがって、ログイベントはcomおよびRootロガーに伝播され、それぞれ定義されたレベルに従ってメッセージがログに記録されます。
  • comロガーを取得し、ログを記録するためのログイベントを開始すると、loggerConfig(com)がメッセージをログに記録し、メッセージは階層をさかのぼって伝播します。親のログレベルを尊重しません。つまり、Rootロガーにログイベントが伝播され、メッセージも記録されます。
  • net.journaldev階層についても同様です。
  • 次のセクションでは、付加的な概念のさらなる説明を追加します。
  • 親がメッセージを無視する可能性があります。フィルター概念を使用するか、付加指標を false に設定して、ログイベントが親に伝播しないようにします。
  • 該当するloggerConfigのレベルがログイベントのレベルよりも大きい場合、ロガーがメッセージを無視する可能性があります。

以上で説明した付加性の概念に関連する例を見てみましょう:

import net.NetApp;
import net.journaldev.NetJournalDevApp;
import com.ComApp;
import com.journaldev.ComJournalDevApp;
public class Main {
	public static void main(String [] args){
		new ComApp();
		new ComJournalDevApp();
		new NetApp();
		new NetJournalDevApp();
	}
}
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ComJournalDevApp {
	public ComJournalDevApp(){
		Logger logger = LogManager.getLogger(ComJournalDevApp.class);
		logger.trace("COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::");
	}
}
package net;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetApp {
	public NetApp(){
		Logger logger = LogManager.getLogger(NetApp.class);
		logger.error("NET :: LEVEL :: NetApp ERROR Message ::");
	}
}
package net.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetJournalDevApp {
	public NetJournalDevApp(){
		Logger logger = LogManager.getLogger(NetJournalDevApp.class);
		logger.error("NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::");
	}
}

次に、以下のようにlog4j2構成ファイルが見えます:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

メインクラスが実行されると、以下の結果が表示されます:

10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

上記のコードの詳細な説明は以下のとおりです:

  • 構成ファイルには、Root、com、com.journaldev、net、net.journaldevの5つのloggerConfigインスタンスが定義されており、上記のロガーヒエラルキーと同様です。
  • RootのレベルはERRORに設定されていますが、これは実際にはデフォルト値です。
  • comとcom.journaldevのレベルはTRACEに設定されています。
  • netとnet.journaldevのレベルはERRORに設定されています。
  • ComAPPとComJournalDevAppのロガーメッセージがそれぞれ2回と3回表示されていることに気付いたかもしれません。これらのメッセージは、ComAppとComJournalDevAppがそれぞれcomとcom.journalDevパッケージにあるロガーヒエラルキーに従って表示されます。NetAppとNetJournalDevAppクラスでも同様のケースがあります。
  • 親は、付加指標がデフォルトでtrueに設定されているため、伝播されます。

ログ記録スペースでは、ログイベントのレベルとLoggerの階層に加えて、loggerConfigのレベルも考慮されます。

では、comのLoggerConfigをINFOに変更して、プログラム全体をそのままにした場合の結果は以下のようになります:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="INFO">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

結果は次のようになります:

11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
  • 確かに、ComAPPのログイベントは無視されていることに気づくでしょう。これは、comパッケージのLoggerConfigの定義されたレベルが影響しています。ここでは、INFO(400)レベルがログイベントのレベル(TRACE(600))よりも低いため、ComAppのメッセージはもはや表示されません。それを表示するには、comのLoggerConfigのレベルをTRACE(600)またはALL(Integer.MAX_VALUE)に変更する必要があります。

ログイベントが表示されていることを確認するには、LoggerConfigのレベルがログイベントのレベル以上である必要があります。

以下の表は、log4j2のレベルとそれぞれの重みを示しています:

LEVEL Weight
OFF 0
FATAL 100
ERROR 200
WARN 300
INFO 400
DEBUG 500
TRACE 600
ALL Integer.MAX_VALUE

もちろん、上記の表は言葉よりもはるかに明確に説明しており、LoggerConfigのレベルがINFOのままである間にログイベントTRACEが表示されない主要な原因を示しています。

ログイベントの伝播は、この計算の範囲を超えてロガー階層で行われ、レベルは無視されます。

しかし、com.journaldevのLoggerConfigを構成から削除し、新しいものをcom.journaldev.loggingに追加した場合、構成ファイルは以下のようになります:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

上記のlog4j2設定で何が起こったかを理解するのに、下の図を参照すると便利です。上記の図に関して以下の説明と、ログイベントの動作にどのように影響するかを説明します: 上の図についてのいくつかの説明と、それがログイベントの動作にどのように影響するかについてのいくつかの説明を以下に示します:

  • ログイベントがcom.journaldev.loggingという名前のLoggerによってスローされた場合、その名前(つまりcom.journaldev.logging)に関連付けられたLoggerConfigが使用され、メッセージが出力されます。
  • com.journaldev.logging LoggerConfigのadditive属性がデフォルトでtrueに設定されているため、ログイベントは親に伝播されます。この場合、com.journaldevに対応します。
  • com.journaldev LoggerConfigが構成で定義されていないため、何も行われず、ログイベントはcom、そしてRoot LoggerConfigインスタンスに伝播されます。
  • Com&Rootはログイベントを受信し、それを出力します。送信されるレベルに関係なく。

上記のポイントの結果、次の出力が表示されます:

14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

そして、次のことに気づくかもしれません:

  • comパッケージのログイベントが2回表示されていることに注意してください。comのための1つ、Rootのための2つ目。
  • コム.journaldevのログイベントが2回表示されました。1回目はcomに、2回目はRootに対してです。以前は3回でしたが、現時点ではcom.journaldevのLoggerConfigが存在せず、com.journaldevパッケージでのログ記録は行われなかった可能性があり、イベントはcomとRootに伝播されました。
  • コム.journaldev.loggingのログイベントが3回表示されました。1回目はcom.journaldev.loggingパッケージ用で、2回目はcom用、3回目はRoot用です。ロガーヒエラルキーの伝播によると、4回目に表示されるはずですが、com.jounraldevのLoggerConfigが存在しないため、3回表示されます。

com.journaldev LoggerConfigのインスタンスを定義していて、レベルが指定されていない場合、その親からレベルを継承します。

しかし、構成ファイルでcom.journaldev LoggerConfigを定義し、LoggerConfigのレベルを指定し忘れた場合はどうなるでしょうか。幸いなことに、ロガーヒエラルキーの概念がここで役立ちます。com.journaldevは親からそのレベル値を継承します。以下はサンプル構成ファイルと、各ロガーコンフィグのログレベルのテーブルが続きます。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>
Logger Name Assigned LoggerConfig LoggerConfig Level Logger Level
Root Root ERROR ERROR
com com TRACE TRACE
com.journaldev com TRACE TRACE
com.journaldev.logging com.journaldev.logging TRACE TRACE
  • com.journaldev.loggingパッケージは、ログレベルTRACEでロガーコンフィグに関連付けられています。
  • com.journaldevパッケージは、ログレベルが指定されていないLoggerConfigに既に関連付けられているため、その親のログレベルを継承します。そして、確かにcomパッケージの値はTRACEになります。
  • comパッケージは、ログレベルTRACEでロガー設定に関連付けられています。
  • デフォルトでは、RootはログレベルERRORです。
  • comパッケージが宣言されていない場合、com.journaldev LoggerConfigはRootのログレベルを継承します。

以下は、com.journaldevがcomログレベルを継承している間の実行結果です:

14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

そして、comパッケージのLoggerConfigを削除した場合の結果は以下の通りです:

14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

comおよびcom.journaldevのメッセージが記録されていないことに気付くかもしれません。以下にその理由を示します。

  • comパッケージに関連するLoggerConfigを削除すると、そのパッケージで言及されているすべてのログイベントが無視されます。
  • 構成でcomパッケージのLoggerConfigが定義されていないため、com.journaldevに関連付けられているLoggerConfigは親からログレベルを継承します。Comが定義されておらず、Logger階層が最上位に達し、現在はRootを参照しています。ルートログレベルはERROR(200)であり、com.journaldevのログイベントレベルはTRACE(600)です(ComJournalDevAppを参照)。以前に定義された式に従うと、LoggerConfigレベルはログイベント以上でなければならず、これは間違っています。したがって、com.journaldevのメッセージはここに表示されません。

最後に、以下の表は、ログシステムを使用する際に遭遇する可能性のあるすべてのログシナリオを示しています:

X (N/A) LoggerConfig Level OFF(0) FATAL(100) ERROR(200) WARN(300) INFO(400) DEBUG(500) TRACE(600) ALL(MAX)
Event Level X X X X X X X X X
OFF(0) X YES NO NO NO NO NO NO NO
FATAL(100) X NO YES YES YES YES YES YES YES
ERROR(200) X NO NO YES YES YES YES YES YES
WARN(300) X NO NO NO YES YES YES YES YES
INFO(400) X NO NO NO NO YES YES YES YES
DEBUG(500) X NO NO NO NO NO YES YES YES
TRACE(600) X NO NO NO NO NO NO YES YES
ALL(MAX) X NO NO NO NO NO NO NO YES
  • OFF/ALLログイベントをスローするために使用できる直接的なメソッドはありません。
  • 主に、OFF/ALLログイベントをスローするためには、logger.log(Level.OFF, “Msg”)またはlogger.log(LEVEL.ALL,“Msg”)を使用します。
  • logメソッドは、述べられた式に従ってログイベントを処理する責任があります。

処理の方程式では、LoggerConfigレベルがログイベントのレベル以上である場合、イベントはさらなる処理のために受け入れられます。

ログイベントはさらなる処理のために受け入れられます – これは非常に重要です。なぜなら、Log4j2フィルターを使用して、イベントの処理を防止する能力があるからです。 additiveプロパティをfalseに設定して、親ロガーへのログイベントの伝播を回避できます。以前に見た例と同じ例を以下に示しますが、今回はadditivity属性を使用しているため、違いに気付くことができます。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

そして実行結果は以下のようになります:

17:55:30.558 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
17:55:30.560 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
17:55:30.561 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
17:55:30.561 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
17:55:30.562 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

そして、ログイベントが親ロガーに伝播されていないことに気付くことができます。

Log4j2ルックアップ

理想的には、ルックアップを定義することができ、ログ構成ファイルに値を渡す方法として使用できます。 Log4j2は、異なるコンテキストから値を設定するために独立して使用できる異なるセットのルックアップを提供します:

  • コンテキストマップルックアップ
  • 日付ルックアップ
  • 環境ルックアップ
  • Javaルックアップ
  • JNDIルックアップ
  • JVM入力引数ルックアップ(JMX)
  • メイン引数ルックアップ
  • マップルックアップ
  • 構造化データルックアップ
  • システムプロパティルックアップ
  • Webルックアップ

Log4j2のドキュメントを参照して、ルックアップの各種詳細を確認できますが、ここではLog4j2 Lookupの基本をカバーするための例を見てみましょう。環境ルックアップは、環境値を渡す方法を示します(Linuxの/etc/profile、Windowsシステム環境、またはアプリケーションの起動スクリプトなど)。ほとんどの人が知っているように、アプリケーションが使用する環境値のセットを定義する機能があります。環境変数を定義する最も一般的な方法を見てみましょう。

  1. Windows環境機能を使用して環境変数を定義します:
    • コンピューターアイコンを右クリックし、プロパティを選択します。コントロールパネルのホームが表示されます。
    • 詳細システム設定をクリックし、環境変数ウィンドウを開きます。
    • システム変数セクションの下で、JournalDevVarという変数にJournalDevの値を定義します。
    • log4j2.xml内のPatternLayoutを更新して、新しく追加された変数を含めます。

  1. 環境変数を定義するには、スタートアップスクリプト機能を使用します。
    • 通常のデフォルトスクリプトを使用する代わりに、Eclipse IDEの実行スクリプト機能を使用できます。[実行]メニューをクリックし、[実行構成]を選択します。
    • 環境タブに移動し、そこで変数を定義します。

変更されたlog4j2.xmlファイルを見て、環境変数の使用に注意してください。

<Console name="Console" target="SYSTEM_OUT">
    <PatternLayout pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n"/>
</Console>

そして、実行の結果は以下のようになります:

23:57:02.511 JournalDev www.journaldev.com [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
23:57:02.517 JournalDev www.journaldev.com [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
23:57:02.520 JournalDev www.journaldev.com [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
23:57:02.523 JournalDev www.journaldev.com [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
23:57:02.527 JournalDev www.journaldev.com [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

しかし、ここで少し問題が発生する可能性があります、特にOSの環境変数とEclipseキャッシュを定義する場合です。理想的には、Eclipseは実行されるときにすべてのシステム変数をキャッシュしており、それらすべてをRun – Run Configuration – Environmentタブ – Selectボタンをクリックして見つけることができます。ですので、それを定義したつもりでも、アプリケーションがそれを認識しない場合があります。たとえEclipseを再起動しても、解決策が得られず、解決するにはEclipseのインストール時にeclipse.exe -cleanを実行する必要があります。環境変数が適切に定義され、システムがそれらを自発的に見つけることができることを確認するために、Log4j2 APIが提供する対応プラグインタイプを使用できます。EnvironmentLookupのインスタンスを作成し、特定の変数を検索するように依頼し、定義されている場合はそれらを簡単に見つけることができます。

EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));

Log4j2 Appenders

以前に見たことがあるかもしれませんが、構成ファイルに変数を注入するためにLookupsを使用する方法を説明しました。ただし、メッセージが直接コンソールを介してではなく、ファイルやデータベースリポジトリを介して送信されるように変更したい場合があります。これにより、メッセージが恒久的に保持されます。Log4j2は多くのAppenderを提供しており、Appenderの詳細についてはlog4j2のドキュメントを参照してください。簡潔に述べると、以下にLog4j2 Appenderの一覧があります。

  1. ConsoleAppender
  2. AsyncAppender
  3. FailoverAppender
  4. FileAppender
  5. FlumeAppender
  6. JDBCAppender
  7. JMSAppender
  8. JPAAppender
  9. MemoryMappedFileAppender
  10. NoSQLAppender
  11. OutputStreamAppender
  12. RandomAccessFileAppender
  13. RewriteAppender
  14. RollingFileAppender
  15. RollingRandomAccessFileAppender
  16. RoutingAppender
  17. SMTPAppender
  18. SocketAppender
  19. SyslogAppender

ログイベントを記録するために最もよく使用されるメディアは、コンソール、ファイル、およびデータベースです。ファイルはメッセージを保存するため、データベースは監査のために使用される場合があります。この目的のために、このセクションではJDBCAppenderが効率的に使用される方法に焦点を当てます。

JDBCAppender

JDBCAppenderの主な目的は、JDBC接続を介してログイベントをリレーショナルテーブルに書き込むことです。このチュートリアルはその目的ではないため、接続プールを最適化する方法については詳細に説明しませんが、データベースへのログイベントの書き込みを支援する完全な機能の例を提供します。

Parameter Name Type Description
Name String Required, The name of the Appender
ignoreExceptions boolean Default value is set to true, making exceptions thrown to be logged also and then ignored. False value means the exception will be propagated for the caller.
filter Filter The filter that should be used to make a decision whether the log events are going to be handled by this Appender or not.
bufferSize int Default value is zero, indicating there’s no buffering have been done upon log events. Value greater than 0 would lead the Appender to buffer log events and then flush them once the buffer reaches the limit specified.
connectionSource ConnectionSource Required, the connections source from which the database connections should be retrieved.
tableName String Required, the name of the Table on which your log events should be persisted.
columnConfigs ColumnConfig[] Required, additional information may be set upon those used columns and how the data should be persisted on each of them. This can be handled with multiple <Column> elements.
Parameter Name Type Description
jndiName String Required, full prefixed JNDI name that the javax.sql.Datasource is bound to.
Parameter Name Type Description
class String Requird, The fully qualified name for a class containg a static factory method for obtaining JDBC connections.
method boolean Required, The name of the static factory method for obtaining JDBC connections.
Parameter Name Type Description
name String Required, the name of the database column
pattern String Ability to specify any legal pattern that Log event would be formatted with
literal String Ability to specify literal value in this column (i.e. SEQ.NEXTVAL)
isEventTimestamp boolean Indicating whether the event would consider Timestamp
isUnicode boolean For unicode purpose as you may refer for Log4j2 documentation for further details
isClob boolean For storing character large object, you may refer for Log4j2 documentation for further details.

JNDIを使用することが必須なので、当社の例ではOracleデータベースとApache Tomcat 7用の接続データソースを構成します。

  • 環境にOracleデータベースをインストールしていない場合は、できるだけインストールしてください。Oracleに詳しくない場合は、そのExpress Editionをインストールすることをお勧めします。
  • 環境にApache Tomcat 7をインストールしてください。
  • EclipseにMaven WebAppプロジェクトを作成します。

  • プロジェクトが正常に作成されていることを確認し、pomでエラーがある場合は修正してください。
  • Log4j2の依存関係を追加します。
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-api</artifactId>
   <version>2.2</version>
</dependency>
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>2.2</version>
</dependency>
  • コンテキストを構成して、MySQLデータソース宣言を含めます。Apacheのドキュメントによると、このファイルはWebアプリケーションのMETA-INFフォルダ内に配置する必要があります。

<Context path="/JournalDevWebLogging"
	privileged="true" antiResourceLocking="false" antiJARLocking="false">
	<Resource name="jdbc/JournalDevDB" auth="Container"
			factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
			type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
			username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
			url="jdbc:mysql://localhost:3306/journaldev" />
</Context>
  • データベースを構成し、ログテーブルを作成します。

CREATE TABLE `logging` (
  `EVENT_ID` int(11) NOT NULL AUTO_INCREMENT,
  `EVENT_DATE` datetime DEFAULT NULL,
  `LEVEL` varchar(45) DEFAULT NULL,
  `LOGGER` varchar(45) DEFAULT NULL,
  `MSG` varchar(45) DEFAULT NULL,
  `THROWABLE` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`EVENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
  • 以下のようにlog4j2.xmlを構成してください:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
		<JDBC name="databaseAppender" tableName="journaldev.logging">
			<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
			<Column name="EVENT_DATE" isEventTimestamp="true" />
			<Column name="LEVEL" pattern="%level" />
			<Column name="LOGGER" pattern="%logger" />
			<Column name="MSG" pattern="%message" />
			<Column name="THROWABLE" pattern="%ex{full}" />
		</JDBC>
	</Appenders>
	<Loggers>
		<Root level="ERROR">
			<AppenderRef ref="Console" />
		</Root>
		<logger name="com" level="TRACE" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
		<logger name="com.journaldev" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
	</Loggers>
</Configuration>
  • Webリソースを作成して、ロガーの参照を取得し、イベントをログに記録できるようにします。
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
			Logger logger = LogManager.getLogger(JournalDevServlet.class);
			logger.trace("JournalDev Database Logging Message !");
	}
}
  • オプションで、ServletContextListenerを設定して、データソースの初期化が適切に行われるようにします。
package com.journaldev;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.LogManager;
public class JournalDevServletContextListener implements ServletContextListener{
	private InitialContext context = null;
	public void contextDestroyed(ServletContextEvent event) {
	}
	public void contextInitialized(ServletContextEvent event) {
		try {
			// 初期コンテキストを取得
			context = new InitialContext();
			// サブコンテキストenvの参照を取得
			Context envContext = (Context)context.lookup("java:comp/env");
			// サブコンテキストjdbcの参照を取得し、定義されたデータソースを検索します
			LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
		} catch (NamingException e) {
			LogManager.getRootLogger().error(e);
		}
	}
}
  • Web.xmlファイル内にServletを定義してください。
  • アプリケーションを実行し、上記で定義したServletにアクセスしてください。以下のログが表示されます。
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_26\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk1.6.0_26\jre\bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin/server;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/lib/amd64;D:\OracleWebCenter\OracleWC\Oracle11g\app\oracle\product\11.2.0\server\bin;;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;D:\OracleDB\app\product\11.2.0\dbhome_1\bin;org.C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\SpringRoo\spring-roo-1.2.5.RELEASE\bin;D:\Ant\apache-ant-1.9.2\bin;C:\Python27;C:\Program Files\Java\jdk1.6.0_26\bin;D:\Maven\apache-maven-3.2.1/bin;D:\bower-master\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\nodejs\;C:\Program Files\Microsoft Windows Performance Toolkit\;D:\Grails\grails-2.4.0\bin;D:\Gradle\gradle-2.0\bin;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\TortoiseSVN\bin;D:\Strawberry\perl\bin;D:\Strawberry\perl\site\bin;D:\Strawberry\c\bin;C:\Users\mohammad.amr\AppData\Roaming\npm;D:\JournalDev\eclipse;;.
Mar 15, 2015 2:31:41 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:JournalDevWebLogging' did not find a matching property.
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:41 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1020 ms
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.35
14:31:43.847 [localhost-startStop-1] ERROR  - org.apache.tomcat.jdbc.pool.DataSource@10fd0a62{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100; maxIdle=30; minIdle=10; initialSize=10; maxWait=10000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=root; url=jdbc:mysql://localhost:3306/journaldev; username=root; validationQuery=null; validatorClassName=null; validationInterval=30000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; }
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:43 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1909 ms

Log4j2フィルタ

Logイベントを処理するためのLoggerConfig候補がある場合でも、それをバックエンドAppendersにパスさせないように構成することができます。これは、log4j2フィルタによって行われます。このセクションは、Log4j2のフィルタの使用に関する侵襲的で大規模で膨大なチュートリアルを提供するためのものではありません。なぜなら、それらはそれぞれをカバーする多数の記事が必要だからです。しかし、ここでは、コンセプトを理解するために最も単純なフィルタの使用方法を示します。使用できる最も単純なフィルタの1つは、BurstFilterです。BurstFilterは、イベントが最大限に達した後にイベントを無音で破棄するメカニズムを提供します。今のところ、BurstFilterを使用するために必要なすべての詳細を以下に示します。

Parameter Name Type Description
level String Level of messages to be filtered
rate float The average number of events per second to allow
maxBurst integer The maximum number of events that can occur before events are filtered for exceeding the average rate. The default is 10 times the rate.
onMatch String Action to take when filter matches. May be Accept, DENY or NEUTRAL. The default is NEUTRAL
onMismatch String Action to tale when filter doesn’t match. May be Accept, DENY or NEUTRAL. The default is NEUTRAL

今、データベースAppender内のBurstFilterの場所を見てみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
		<JDBC name="databaseAppender" tableName="journaldev.logging">
			<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
			<BurstFilter level="TRACE" rate="20" maxBurst="2"/>
			<Column name="EVENT_DATE" isEventTimestamp="true" />
			<Column name="LEVEL" pattern="%level" />
			<Column name="LOGGER" pattern="%logger" />
			<Column name="MSG" pattern="%message" />
			<Column name="THROWABLE" pattern="%ex{full}" />
		</JDBC>
	</Appenders>
	<Loggers>
		<Root level="ERROR">
			<AppenderRef ref="Console" />
		</Root>
		<logger name="com" level="TRACE" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
		<logger name="com.journaldev" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
	</Loggers>
</Configuration>
  • Database AppenderはBurstFilterを考慮しますが、コンソールAppenderは考慮しません。
  • コンソールロガーを使用すると、すべてのログイベントがログに記録されますが、データベースAppenderはBurstFilterによっていくつかのイベントが進行を妨げられ、ログに記録されません。
  • このログイベントの拒否は、使用されているロガーがログイベントを処理する候補であっても実現されます。これは、以下のJournalDevServletで示されているように非常に合理的です。

package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		Logger logger = LogManager.getLogger(JournalDevServlet.class);
		for(int i = 0 ; i < 1000 ; i++){
			logger.trace("Index :: "+i+" :: JournalDev Database Logging Message !");
			LogManager.getRootLogger().error("Index :: "+i+" :: JournalDev Database Logging Message !");
		}
	}
}

これらのLoggerConfigsはログイベントを処理する候補であるにもかかわらず、フィルターが一部のイベントの処理とログ出力を妨げています。ログのスペースの概念を理解するために、これをログスペースの概念に追加することができます。

Log4j2 レイアウト

さまざまなAppendersがログイベントを消費し、各Appenderの性質により、レイアウトはログイベントを消費する人のニーズに合った形式で構成されます。Log4j 1.xおよびLogback APIでは、Log Eventsのレイアウト変換は文字列に行われますが、Log4j2のレイアウトは異なる変換方法を考慮しています。それは、LogEventをバイト配列に変換することです。この新しいタイプの変換では、バイト配列に正しい値が含まれるようにCharsetを設定する必要があります。Apache Log4j2公式サイトに戻って、Log4j2が提供するレイアウトと異なるタイプについて詳しく見ることを強くお勧めします。このセクションでは、ほとんどの開発者が使用している最も有名なレイアウトを考慮します。おそらく聞いたことがあるであろうレイアウトです。PatternLayout

Log4j2のPatternLayout

パターンレイアウトは、設定可能で柔軟な文字列パターンで、LogEventのフォーマットを行うことを目的としています。この種のフォーマットは、変換パターンの概念に依存しています。このセクションでは、パターンレイアウトが提供する最も重要な機能を説明します。変換パターンは、言語Cのprintfが提供する変換パターンに関連しています。一般的に、変換パターンはリテラルテキストフォーマット制御式(変換指定子と呼ばれる)から構成されています。以下の図は、変換パターンがどのような部分から構成されているかを示しています:この図は変換パターンを単純化しようとしたものですが、詳細については、Apache Log4j2のドキュメントを参照してください。また、ログイベントに関しては、メッセージのフォーマットに使用される変換パターンをいつでも確認できます。

どのLog4j2レベルを使用するべきか

自分自身に尋ねるかもしれない最も大きな質問は、特定のログイベントレベルをいつ使用すべきかです。開発フィールドでは、DEBUGログイベントを使用するのが一般的ですが、本番環境ではINFOまたはWARNレベルを使用する必要があります。以下の表は、どの場合にどのlog4j2レベルを使用すべきかを案内します。

Log Event Level When It Should Be Used
OFF When no events will be logged
FATAL When a severe error will prevent the application from continuing
ERROR When an error in the application, possibly recoverable
WARN When an event that might possible lead to an error
INFO When an event for informational purposes
DEBUG When a general debugging event required
TRACE When a fine grained debug message, typically capturing the flow through the application
ALL When all events should be logged

Log4j2 チュートリアルの要約

Log4j2 は、Apache Logging フレームワークの刷新版です。Log4j2 は、Log4j1.x からの一連の新機能とパフォーマンスの向上を提供しています。この log4j2 チュートリアルは、すべてを一か所で手に入れるのを助けることを目的としています。これらの概念の一部は一度にすべてカバーするのがそんなに簡単ではないため、私たちは概念の説明とさらなる明確化のためにいくつかのサンプルを使用することにしました。アペンダ、フィルタ、レイアウト、およびルックアップもこのルールの対象です。いくつかの重要なポイント 以下のアプリケーションを実行できるようにし、障害を回避するために、以下を確認してください:

  • あなたの Eclipse IDE は Maven が有効になっています。
  • あなたの Apache Tomcat には、Apache ホームの lib フォルダ内に mysql-connector JAR が含まれています。
  • Maven の使用方法を把握しています。

Apache Log4j 2 の例プロジェクトをダウンロード

これで log4j2 チュートリアルは終わりです。重要なポイントのほとんどがカバーされており、Log4j2 をアプリケーションで使用する準備が整っていることを願っています。

Source:
https://www.digitalocean.com/community/tutorials/log4j2-example-tutorial-configuration-levels-appenders