Log4j2 예제 튜토리얼 – 구성, 레벨, 애펜더

로그4j2 예제 튜토리얼에 오신 것을 환영합니다. 응용 프로그램에 관한 전문가 개발자에게 가장 귀찮은 것은 로깅과 관련된 것일 수 있습니다. 응용 프로그램에 적합한 로깅이 없으면 유지 관리가 악몽이 될 것입니다. 대부분의 응용 프로그램은 개발 테스트, 단위 테스트, 통합 테스트를 거칩니다. 그러나 운영에 들어가면 항상 독특한 시나리오와 예외가 발생합니다. 따라서 특정 경우에 무슨 일이 발생했는지 알아내는 유일한 방법은 로그를 통해 디버깅하는 것입니다. 많은 프레임워크가 기본 로깅 방법을 제공하지만 업계 표준 로깅 메커니즘을 사용하는 것이 항상 가장 좋습니다. 아파치 로그4j는 가장 널리 사용되는 로깅 프레임워크 중 하나입니다. 아파치 로그4j 2는 로그4j보다 훨씬 더 나은 다음 버전입니다.

로그4j 예제 튜토리얼

이 Log4j2 예제 튜토리얼에서는 아파치 로그4j2를 시작하는 방법을 배우게 됩니다. 또한 Log4j2 아키텍처, log4j2 구성, log4j2 로깅 레벨, appenders, filters 등을 탐색하게 됩니다.

  1. Log4j2 개요
  2. Log4j2 아키텍처
  3. Log4j2 구성
  4. Log4j2 레벨
  5. Log4j2 Lookups
  6. Log4j2 Appenders
  7. Log4j2 Filters
  8. Log4j2 레이아웃
  9. 어떤 Log4j2 레벨을 사용해야 하나요
  10. Log4j2 튜토리얼 요약

Log4j2 개요

애플리케이션에서 로깅 API를 사용하는 것은 여유가 아니라 필수입니다. Log4j아파치 소프트웨어로 발행 및 라이선스가 부여된 오픈 소스 라이브러리입니다. 애플리케이션을 이클립스 디버깅 또는 다른 도구를 사용하여 디버깅할 수 있지만, 이것만으로는 생산 환경에서 충분하고 비실용적이지 않습니다. 로깅 메커니즘은 일반 디버깅에서 찾을 수 없는 여러 가지 이점을 제공할 것입니다.

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

위에서 볼 수 있듯이, 로깅 메커니즘을 사용하면 유지 보수 비용이 적게 드는 효율적인 방법이 될 것입니다. Java 애플리케이션에서 로깅에 대한 선두 주자 도구는 아파치 Log4j이므로 이를 사용해야 합니다.

Log4j2 아키텍처

로그4j 예제 튜토리얼을 진행하기 전에 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()에 그들의 이름을 전달하여 얻을 수 있습니다.

  • 로거 컨텍스트는 응용 프로그램 내에 여러 개의 로거 컨텍스트가 있을 수 있기 때문에 로깅 시스템의 중요한 요소입니다. 각 로거 컨텍스트마다 활성 구성이 설정되어야 합니다.

  • Log4j2 구성에는 모든 로깅 시스템 자산이 포함되어 있습니다. LoggerConfig(s), Appender(s), Filter(s) 및 기타 많은 요소들이 있습니다.

  • 동일한 이름을 전달하여 LogManager.getLogger()를 호출하면 항상 정확히 같은 로거 인스턴스에 대한 참조가 반환됩니다.

  • 로깅 시스템의 구성은 일반적으로 응용 프로그램 초기화 과정에서 수행됩니다. 이는 다양한 형태로 이루어질 수 있습니다. 프로그래밍 방식으로 또는 log4j2 구성 파일을 읽는 방식으로 수행될 수 있습니다.

모든 로거는 LoggerConfig 객체와 관련이 있으며, LoggerConfig 객체의 집합이 로거들의 계층을 이룹니다. 이 개념은 로거 계층으로 알려져 있습니다. 로거 계층은 부모-자식 관계를 가진 LoggerConfig 객체의 집합으로 이루어져 있습니다. 모든 로거 계층에서 가장 위에 있는 요소는 루트 로거입니다. Log4j2에서 구성 파일을 찾지 못하면, 로깅 레벨을 ERROR로 설정하여 로깅에만 루트 로거가 사용됩니다. 다음 이미지는 이 경우에 표시되는 경고 메시지를 보여줍니다. Error StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. 아래 표는 로거 계층에서 부모-자식 관계를 보여줍니다.

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 구성

어플리케이션에서 Log4j2 구성을 사용하는 여러 가지 방법이 있습니다.

  1. XML, JSON, YAML 또는 속성 파일로 작성된 구성 파일 사용.
  2. 프로그래밍 방식으로, 구성 팩토리 및 구성 구현을 생성함으로써.
  3. 프로그래밍 방식으로, 구성 인터페이스에서 노출된 API를 호출함으로써.
  4. 내부 로거 클래스의 메서드를 호출함으로써 프로그래밍 방식으로.

주로 구성 파일에 중점을 둘 것입니다. 그러나 특정 로거에 대한 특정 로깅 전략을 구성하려는 경우 프로그래밍 접근 방법도 알아두는 것이 좋습니다. 먼저 구성 파일을 제공하지 않은 경우를 고려해 보겠습니다. Log4j2 구현은 log4j2 구성 파일의 위치를 가리키는 시스템 변수인 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 메서드를 호출하여 루트 로거를 참조했습니다.
  • LogManager에서 로거를 참조하는 것으로 Log4j 시스템이 시작되었습니다.
  • Log4j는 log4j.configurationFile 시스템 속성을 검사하여 log4j2 구성 파일을 결정합니다. Log4j 구성은 JSON, YAML 및 XML로 작성할 수 있습니다.
  • 우리는 log4j.configurationFile 시스템 속성을 System.setProperties("log4j.configurationFile","FILE_PATH")를 통해 설정하거나 아래 그림에서 볼 수 있는 것처럼 JVM 매개변수로 전달할 수 있습니다. 또한 파일 프로토콜 접두사에도 주목하십시오.

  • 시스템 속성이 정의되지 않은 경우 설정 순서는 아래 우선 순위를 갖습니다:
    • Property ConfigurationFactory는 클래스패스에서 log4j2-test.properties를 찾습니다.
    • YAML ConfigurationFactory는 클래스패스에서 log4j2-test.yaml 또는 log4j2-test.yml를 찾습니다.
    • JSON ConfigurationFactory는 클래스패스에서 log4j2-test.jsn 또는 log4j2-test.json를 찾습니다.
    • XML ConfigurationFactory는 클래스패스에서 log4j2-test.xml를 찾습니다.
    • Property ConfigurationFactory는 클래스패스에서 log4j2.properties를 찾습니다.
    • YAML ConfigurationFactory는 클래스패스에서 log4j2.yml 또는 log4j2.yaml를 찾습니다.
    • JSON ConfigurationFactory는 클래스패스에서 log4j2.jsn 또는 log4j2.json를 찾습니다.
    • XML ConfigurationFactory는 클래스패스에서 log4j2.xml를 찾습니다.
    • 구성 파일이 제공되지 않은 경우, DefaultConfiguration가 사용되며 기본 동작 세트로 이어집니다:
      • 루트 로거가 사용됩니다.
      • 루트 로거의 레벨이 ERROR로 설정됩니다.
      • 루트 로거가 콘솔로 로깅 메시지를 전파합니다.
      • PatternLayout이 %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n로 설정됩니다.

log4j2 구성 파일을 사용하면 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);
 
    	// 애펜더 추가
    	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 ::");
 
    	// 루트에서 처리 될 ERROR 메시지의 LogEvent
    	logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
    }
}
  • Log4j2에서 제공하는 ConfigurationFactory 중 하나를 사용하거나 기본 값을 사용할 수 있습니다. 우리는 XMLConfigurationFactory를 사용하여 ConfigurationFactory의 인스턴스를 가져 왔습니다.
  • Factory는 해당하는 구성 파일을 전달하여 필요한 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 인스턴스가 설명 될 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 로거에서이고 두 번째 줄은 루트 로거에서입니다. com 로거 오류 메시지는 레벨이 Fatal이기 때문에 출력되지 않습니다.

Log4j2 Levels

위의 코드 예제에서 확인할 수 있듯이 LoggerConfig를 정의할 때마다 로깅 레벨을 제공합니다. 기본적으로 log4j2 로깅은 누적적입니다. 즉, 특정 로거를 사용할 때 모든 부모 로거도 사용됩니다. 아래 이미지는 이 상황을 명확히 보여줍니다. 그리고 여기에 대한 명확화 포인트가 있습니다:

  • 이전에 설명한대로 각 로거에는 LoggerConfig 인스턴스가 연결되어 있습니다. 이 LoggerConfig는 구성 범위에서 정의되었습니다.
  • 로깅 수준은 LoggerConfig 범위에서 결정할 수 있습니다.
  • 이름, 상위 패키지 또는 루트 로거 자체를 사용하여 로거를 얻을 수 있습니다.
  • 루트 로거는 모든 LoggerConfig 계층의 최상위 노드입니다.
  • com.journaldev 로거를 획득하고 로깅을 위한 logEvent를 시작하면 loggerConfig (net.journaldev)가 메시지를 로깅하고 해당 메시지가 계층 구조 상위로 전파됩니다. 부모 로깅 수준과 관계없이 메시지가 각각 정의된 수준에 따라 com 및 루트 로거에 메시지가 기록됩니다.
  • com 로거를 얻고 로깅을 위한 logEvent를 시작하면 loggerConfig(com)가 메시지를 기록하고 해당 메시지가 부모의 로깅 수준을 고려하지 않고 계층 구조 상위로 전파됩니다. 즉, 루트 로거가 로그 이벤트를 전파하고 메시지를 기록합니다.
  • net.journaldev 계층에 대해서도 동일한 경우입니다.
  • 다음 섹션에서는 추가 개념에 대한 추가 설명을 추가할 것입니다.
  • 부모가 메시지를 무시할 수 있는 가능성이 있습니다. Filter 개념을 사용하거나 추가 지시자를 false로 설정하여 로그 이벤트가 부모로 전파되지 않습니다.
  • 해당 로거Config의 레벨이 로그 이벤트 레벨보다 큰 경우 로거가 메시지를 무시할 수 있습니다.

이제 위에서 설명한 추가성 개념과 관련된 예제를 살펴보겠습니다:

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>

Main 클래스가 실행되면 아래 결과를 찾을 수 있습니다:

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 ::

위에 나열된 코드에 대한 자세한 설명은 다음과 같습니다:

  • 구성 파일에는 다섯 개의 loggerConfig 인스턴스가 정의되어 있으며 이들은 Root, com, com.journaldev, net 및 net.journaldev입니다. 위에 표시된 Logger Hierarchy와 마찬가지로입니다.
  • Root의 레벨은 ERROR로 구성되어 있으며 이것이 실제로 기본값입니다.
  • com 및 com.journaldev의 레벨은 TRACE로 구성됩니다.
  • net 및 net.journaldev의 레벨은 ERROR로 구성됩니다.
  • ComAPP 및 ComJournalDevApp 로거 메시지가 각각 두 번과 세 번 표시되었음을 알 수 있습니다. 이러한 메시지는 ComApp 및 ComJournalDevApp의 Logger Hierarchy에 따라 표시됩니다. 여기에는 각각 com 및 com.journalDev 패키지에 있습니다. NetApp 및 NetJournalDevApp 클래스에도 비슷한 경우가 있습니다.
  • 부모는 추가 지시자가 기본적으로 true로 설정되어 있기 때문에 전파됩니다.

로그 기록 공간은 로그 이벤트의 수준과 로거 구성의 수준을 고려합니다. 로거 계층도 고려합니다.

그렇다면 우리가 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

분명히 위의 표는 말보다 훨씬 명확하며 Log 이벤트 TRACE가 표시되지 않는 주된 이유를 제공합니다. 로거 구성의 수준이 INFO인 경우.

로그 이벤트의 로거 계층을 위로 전파하는 것은 이 계산을 넘어서며 수준을 무시합니다.

그러나 구성에서 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 패키지의 로그 이벤트가 두 번 표시되었습니다. 하나는 com을 위한 것이고 다른 하나는 Root를 위한 것입니다.
  • com.journaldev에서의 로그 이벤트가 두 번 표시되었습니다. 하나는 com을 위한 것이고 두 번째는 Root를 위한 것입니다. 이전에는 세 번이었지만, 현재 com.journaldev의 LoggerConfig가 없으므로 com.journaldev 패키지에서 로깅이 발생하지 않았을 것이며, 이벤트가 com 및 Root로 전파될 것입니다.
  • com.journaldev.logging에서의 로그 이벤트가 세 번 표시되었습니다. 하나는 com.journaldev.logging 패키지를 위한 것이고 두 번째는 com을 위한 것이며 세 번째는 Root를 위한 것입니다. 로거 계층 전파에 따르면 네 번째로 표시되어야하지만, com.jounraldev LoggerConfig가 없으므로 세 번 표시됩니다.

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로 설정된 LoggerConfig와 연결되어 있습니다.
  • com.journaldev 패키지는 로그 레벨이 지정되지 않은 LoggerConfig와 이미 연관되어 있으므로 부모 로그 레벨을 상속 받을 것이며, com 패키지에 대한 값은 확실히 TRACE가 될 것입니다.
  • com 패키지는 이미 로그 레벨이 TRACE로 설정된 Loggerconfig와 연관되어 있습니다.
  • 기본적으로 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는 상위에서 Log Level을 상속받게 됩니다. Com이 정의되지 않았고 로거 계층이 최상위로 도달하여 이제는 루트를 참조합니다. 루트 로그 레벨은 ERROR(200)이며 com.journaldev의 로그 이벤트 레벨은 TRACE(600)입니다. 이전에 정의된 식에 따르면 LoggerConfig 레벨은 Log 이벤트보다 크거나 같아야 합니다. 그러나 그것은 거짓입니다. 따라서 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 로그 이벤트를 throw하는 데 사용할 수 있는 직접적인 방법은 없습니다.
  • 주로 OFF/ALL 로그 이벤트를 throw하기 위해 logger.log(Level.OFF, “Msg”) 또는 logger.log(LEVEL.ALL,”Msg”)을 사용할 수 있습니다.
  • 로그 메소드는 언급된 식에 따라 로그 이벤트를 처리하는 데 책임이 있습니다.

처리 방정식에 따르면 LoggerConfig 레벨이 Log 이벤트 레벨보다 크거나 같으면 이벤트가 추가 처리를 위해 수락됩니다.

로그 이벤트는 추가 처리를 위해 수락될 것입니다 – 이것은 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는 다음과 같은 다양한 종류의 룩업을 제공하여 다른 컨텍스트에서 값을 설정하는 데 독립적으로 사용할 수 있습니다:

  • 컨텍스트 맵 룩업
  • 날짜 룩업
  • 환경 룩업
  • 자바 룩업
  • JNDI 룩업
  • JVM 입력 인수 룩업 (JMX)
  • 주요 인수 룩업
  • 맵 룩업
  • 구조화된 데이터 룩업
  • 시스템 속성 룩업
  • 웹 룩업

Log4j2 문서에서 모든 유형의 룩업에 대한 자세한 내용을 참조할 수 있지만, 여기서 Log4j2 Lookup의 기본을 다루는 몇 가지 예시를 살펴보겠습니다. 환경 룩업은 환경 값(리눅스 등의/profile, 윈도우 시스템 환경 또는 응용 프로그램을 위한 시작 스크립트)을 전달하는 방법을 나타냅니다. 대부분의 경우, 우리는 응용 프로그램이 사용할 환경 값 집합을 정의할 수 있습니다. 환경 변수를 정의하는 가장 유명한 방법을 살펴보겠습니다.

  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의 환경 변수를 정의할 때입니다. 특히 이클립스 캐시입니다. 이클립스가 실행되면 이상적으로 모든 시스템 변수가 캐시됩니다. 그리고 이들 중 일부를 Run – Run Configuration – Environment Tab – Click Select 버튼 아래에서 찾을 수 있습니다. 그러므로 정의했지만 응용 프로그램이 인식하지 못하는 경우 혼란스러울 수 있습니다. 이클립스를 다시 시작해도 해결책을 얻을 수 없으며 이를 해결하려면 이클립스 설치 시 eclipse.exe -clean을 실행해야 합니다. 환경 변수가 올바르게 정의되어 있고 시스템이 쉽게 찾을 수 있도록 하려면 Log4j2 API에서 제공하는 일치하는 플러그인 유형을 사용할 수 있습니다. EnvironmentLookup 인스턴스를 만들고 특정 변수를 조회하도록 요청하면 정의된 경우 쉽게 찾을 수 있습니다. 

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

Log4j2 Appenders

이전에 변수를 설정 파일에 주입하는 룩업을 어떻게 사용할 수 있는지 보았습니다. 그러나 메시지가 직접 콘솔을 통해 전송되는 대신 메시지가 영구적으로 유지되도록 파일 또는 데이터베이스 저장소를 수정하고 싶을 수 있습니다. 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 연결을 통해 로그 이벤트를 관계형 테이블에 작성하는 것입니다. 연결 풀을 최적화하는 방법에 대해 자세히 설명하는 데 시간을 쏟지 않습니다. 이 튜토리얼은 이러한 목적으로 제작되지 않았습니다. 그러나 로그 이벤트를 데이터베이스에 작성하는 데 도움이 되는 완전한 기능 예제를 얻게 될 것입니다. 계속하기 전에 JDBCAppender를 올바르게 구성하기 위해 필요한 모든 매개변수와 각 설명을 살펴 보겠습니다.

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을 설치하는 것이 좋습니다.Express Edition.
  • 환경에 Apache Tomcat 7을 설치하십시오.
  • 환경에 Maven WebApp 프로젝트를 Eclipse에 생성하십시오.

  • 프로젝트가 성공적으로 생성되었는지 확인하고 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 Application 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>
  • 로거에 대한 참조를 가져올 수 있는 웹 리소스를 생성하고 이벤트를 기록합니다.
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);
		}
	}
}
  • 웹.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 필터

로그 이벤트를 처리할 LoggerConfig 후보가 있더라도 해당 이벤트를 백엔드 Appender로 전달하지 않도록 구성할 수 있습니다. 이는 log4j2 필터를 통해 수행될 수 있습니다. 이 섹션은 Log4j2에서 필터를 사용하는 방법에 대한 광범위한 튜토리얼을 제공하는 것을 목적으로 하지 않으며 각각을 다루는 많은 기사가 필요합니다. 그러나 여기에서는 개념을 이해하기 위해 가장 간단한 필터를 사용하는 방법을 보여줍니다. 사용할 수 있는 가장 간단한 필터 중 하나는 LogEvents가 최대 한계에 도달한 후 이벤트를 조용히 삭제하는 메커니즘을 제공하는 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>
  • 데이터베이스 앱렌더는 BurstFilter를 고려하지만 콘솔 앱렌더는 그렇지 않습니다.
  • 콘솔 로거를 사용하는 경우 전체 로그 이벤트가 로깅되지만, 데이터베이스 앱렌더는 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 !");
		}
	}
}

이 로거 구성은 로그 이벤트를 처리할 후보이지만, 필터가 일부 이벤트를 처리하지 못하게 하고 기록되지 않도록합니다. 전체 로깅 개념을 이해하기 위해 Logging Space 개념을 추가할 수 있습니다.

Log4j2 레이아웃

각각의 로그 이벤트를 사용하는 다른 Appender 및 각 앱렌더의 특성으로 인해 레이아웃은 로그 이벤트를 소비하는 사람의 요구를 충족하는 형식으로 구성됩니다. Log4j 1.x 및 Logback API에서는 Log 이벤트의 레이아웃 변환을 문자열로 수행했지만, Log4j2 레이아웃은 Log 이벤트를 바이트 배열로 변환하는 다른 방식을 고려했습니다. 이 새로운 유형의 변환은 바이트 배열이 올바른 값을 포함하도록 Charset를 구성해야 합니다. Apache Log4j2 공식 사이트로 돌아가 Log4j2가 제공하는 레이아웃 및 다른 유형에 대해 자세히 알아보는 것이 매우 권장됩니다. 이 섹션에서는 대부분의 개발자가 항상 사용하는 가장 유명한 레이아웃을 고려할 것이며, 분명히 이에 대해 들어보셨을 것입니다. 그것은 PatternLayout입니다.

Log4j2 PatternLayout

패턴 레이아웃은 로그 이벤트를 서식 지정하기 위한 구성 가능하고 유연한 문자열 패턴입니다. 이러한 서식 지정은 변환 패턴 개념에 의존합니다. 이 섹션에서는 패턴 레이아웃이 제공하는 가장 중요한 기능을 설명합니다. 변환 패턴은 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 튜토리얼은 여러분이 모두 한 곳에서 이를 이해하는 데 도움을 주기 위해 목적을 두고 있습니다. 이러한 개념 중 일부는 한 번에 모두 다루기가 그리 쉽지 않기 때문에, 개념을 설명하고 몇 가지 예제를 사용하여 더 명확하게 설명하려는 노력을 포함시켰습니다. Appenders, Filters, 레이아웃 및 lookups도 이 규칙의 대상입니다. 중요한 포인트 몇 가지 아래 애플리케이션을 실행할 수 있도록하고 장애물을 피하기 위해 아래 사항을 확인하십시오:

  • 귀하의 Eclipse IDE가 메이븐 활성화되어 있습니다.
  • 귀하의 Apache Tomcat에는 Apache Home lib 폴더 내에 mysql-connector JAR가 있습니다.
  • 메이븐 사용 방법을 알고 계십니다.

Apache Log4j 2 예제 프로젝트 다운로드

이것으로 log4j2 튜토리얼을 마칩니다. 중요한 포인트 대부분이 애플리케이션에서 Log4j2를 사용하는 데 시작할 수 있도록 다루어졌기를 바랍니다.

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