[번역] Logback

rin·2020년 5월 7일
2

Document 번역

목록 보기
8/22
post-thumbnail

ref. http://logback.qos.ch/documentation.html

Logback Project

Introduction

Logback은 인기있는 프로젝트인 log4j의 후속 프로젝트로써 log4j가 떠나는 위치를 이어받기 위해 탄성하였다.

Logback 아키텍처는 다른 환경/상황에서도 적용 가능할 정도로 일반적이다. 현재 logback은 logback-core, logback-classic, logback-access의 세 가지 모듈로 나뉘어져있다.

logback-core 모듈은 다른 두 모듈의 기반이된다. logback-classic 모듈은 코어를 확장하는데, 이는 log4j의 매우 발전한 형태라고 생각하면 된다. 또한, logback-classic은 기본적으로 SLF4J API를 구현하므로 log4jjava.util.logging (JUL)와 같은 로깅 프레임 워크 사이에서 쉽게 전환 할 수 있다.

logback-access 모듈은 Tomcat이나 Jetty 같은 서블릿 컨테이너와 통합돼 HTTP-access 로그 기능을 제공한다. logback-core 위에는 자체 모듈을 쉽게 구축 할 수 있다.

Architecture

Logback's architecture

ogback의 기본 아키텍처는 여러 상황에서 적용 가능할 정도로 일반화 되어있다. 현재 logback은 logback-core, logback-classic, logback-access의 세 가지 모듈로 나뉘어져있다.

코어 모듈은 다른 두 개의 모듈을 위한 기반이다. logback-classic 모듈은 코어를 확장하는데, 이는 log4j의 매우 발전한 형태라고 생각하면 된다. 또한, logback-classic은 기본적으로 SLF4J API를 구현하므로 JDK1.4에 도입된 log4jjava.util.logging (JUL)와 같은 로깅 프레임 워크 사이에서 쉽게 전환 할 수 있다. 엑세스라는 세 번째 모듈은 서블릿 컨테이너와 통합돼 HTTP-access 로그 기능을 제공한다. 엑세스 모듈 설명서는 여기에 포함되어있다.

Logger, Appenders and Layouts

logback은 세 가지 주요 클래스(Logger, Appender, Layout)을 기반으로 구현되어있다. 이 세 가지 컴포넌트가 함께 작동함으로써 개발자는 메세지 타입이나 레벨에 따라 이를 기록할 수 있으며 메세지의 포맷과 리포팅 위치를 제어할 수 있다.

Logger 클래스는 logback-classic 모듈의 일부인 반면에 AppenderLayout 인터페이스는 logback-core의 일부이다. 범용 모듈인 logback-core는 logger에 대한 개념이 없다.

Logger context

일반적인 System.out.println에 비해 logging API의 첫 번째이자 가장 중요한 장점은 다른 로그 문들을 방해하지 않고 특정 로그 문을 비활성화 할 수 있다는 것이다. 이 기능은 로깅 공간, 즉 모든 가능한 로깅 문서 공간이 개발자가 선택한 일부 기준에 따라 분류된다고 가정한다. logback-classic에서 이러한 분류는 로거의 고유한 부분이다. 모든 싱글 로거는 로거를 만들고 계층 구조와 같은 트리에 이를 배치하는 역할을 가진 LoggerContext에 연결된다.

로거는 명명된 엔티티이며, 이 때 이름은 대소문자를 구분하며 계층적 명명 규칙을 따른다.

Named Hierarchy
로거의 이름이 .(dot)에 의해 하위 로거 이름의 접두어가 될 경우 이는 다른 로거의 조상이다. 사진과 하위 로거 사이에 조상이 없는 경우에는 아는 하위 로거의 부모가 된다.

예를 들어, com.foo인 로거는 com.foo.Bar인 로거의 부모이다. 마찬가지로 javajava.util의 부모이자 java.util.Vector의 조상이다.

루트 로거는 로거 계층의 맨 위에 있다. 시작점부터 모든 계층의 일부라는 점에서 예외적이다. 모든 로거와 마찬가지로 다음과 같이 이름으로 가져올 수 있다. Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

다른 모든 로거 또한 org.slf4j.LoggerFactory 클래스에서 찾을 수 있는 static getLogger 메소드로 가져올 수 있다. 이 메소드는 가져올 로거의 이름을 매개 변수로 사용한다. Logger interface의 기본 메소드 중 일부는 다음과 같다. :

package org.slf4j; 
public interface Logger {

  // Printing methods: 
  public void trace(String message);
  public void debug(String message);
  public void info(String message); 
  public void warn(String message); 
  public void error(String message); 
}

Effective Level aka Level Inheritance

로거에는 level이 지정 될 수 있다. 가능한 레벨들 (TRACE, DEBUG, INFO, WARN, ERROR)은 ch.qos.logback.classic.Level 클래스에 정의되어 있다. Logback에서 Levelfinal 클래스이며 유연한 접근 방식인 Marker 오브젝트의 형식으로 존재하기 때문에 하위 클래스가 될 수 없다.

만약 주어진 로거가 레벨이 지정되지 않았다면 지정된 레벨을 가진 가장 가까운 조상에서 상속받는다. 즉, 주어진 로거 L의 유효 레벨은 L로부터 root 로거를 향해 계층 구조를 거슬러 올라가면서 만나는 첫 번째 널이 아닌 레벨과 같다.

모든 로거가 결국 레벨을 상속 받을 수 있도록 root 로거에는 항상 지정된 레벨이 있다.( default : DEBUG )

다음은 다양한 계층에 할당된 레벨 값과 상속 규칙에 따른 결과적인 레벨에 대한 예시이다.

🔎 Example 1. 루트 로거만 레벨이 명시되어 있다. DEBUG인 이 레벨은 다른 로거인 X, X.Y, X.Y.Z에 상속된다.

Logger nameAssined levelEffective level
rootDEBUGDEBUG
XnoneDEBUG
X.YnoneDEBUG
X.Y.ZnoneDEBUG

🔎 Example 2. 모든 로거가 레벨이 명시되어 있다. 레벨 상속은 이뤄지지 않는다.

Logger nameAssined levelEffective level
rootERRORERROR
XINFOINFO
X.YDEBUGDEBUG
X.Y.ZWARNWARN

🔎 Example 3. root, X, X.Y.Z가 각각 DEBUG, INFO, ERROR 레벨을 명시하고 있다. 로거 X.Y 계층의 레벨은 부모인 X로 부터 상속 받는다.

Logger nameAssined levelEffective level
rootDEBUGDEBUG
XINFOINFO
X.YnoneINFO
X.Y.ZERRORERROR

🔎 Example 4. root, XDEBUG, INFO 레벨을 명시하고 있다. 로거 X.Y, X.Y.Z는 가장 가까운 부모인 X로부터 레벨을 상속받는다.

Logger nameAssined levelEffective level
rootDEBUGDEBUG
XINFOINFO
X.YnoneINFO
X.Y.ZnoneINFO

Printing methods and the basic selection rule

정의에 따라, 출력 방식이 로깅 요청 레벨을 결정한다. 예를 들어 L이라는 로거 인스턴스가 있을 때 L.info("..") 명령문은 INFO 레벨의 로깅 명령문이다.
로깅 요청 레벨이 해당 로거의 유효 레벨 이상인 경우에만 사용가능하며 그렇지 않으면 요청은 비활성화된다. 위에서 말했듯, 할당된 레벨이 없는 로거는 가장 가까운 조상에서 로거를 상속 받는다.

Basic Selection Rule
유효 레벨 q를 갖는 로거에 발행된 레벨 p의 로그 요청은 p>=q인 경우에만 사용 가능하다.

이 규칙은 logback의 핵심이며 레벨은 다음과 같이 정렬된다. (아래로 갈수록 더 많은 정보를 포함함. 로그 요청 레벨(p)은 로거의 레벨(q)과 같거나 우측에 위치해야한다.)
⭐️ TRACE < DEBUG < INFO < WARN < ERROR ⭐️

다음 표에서 세로 헤더는 로깅 요청의 레벨(p)이고 가로 헤더는 로거의 유효 레벨(q)이다. 행(요청 레벨)과 열 (유효 레벨)의 교차는 기본 선택 규칙에 의한 boolean 결과이다.
여기 기본 선택 규칙에 대한 예시가 있다. :

import ch.qos.logback.classic.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
....
// "com.foo" 라는 이름의 로거를 얻는다.
// 추가적으로 로거가 "ch.qos.logback.classic.Logger" 타입이라 가정했을 때, 
// 우리는 이것의 레벨을 설정할 수 있다.
ch.qos.logback.classic.Logger logger = 
        (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.foo");
        
// 이 로거의 레벨을 INFO로 지정하였다.
// setLevel() 메소드는 logback logger가 필수적이다.
logger.setLevel(Level.INFO);

// WARN >= INFO이므로 이 요청은 수행된다.
logger.warn("Low fuel level.");

// DEBUG < INFO이므로 이 요청은 수행되지 않는다.
logger.debug("Starting search for nearest gas station.");

Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");

// "com.foo.Bar" 라는 이름의 로거 인스턴스는 "com.foo"라는 이름의 로거로부터 레벨을 상속 받는다.
// 따라서 INFO >= INFO("com.foo"의 명시된 레벨 = com.foo.Bar"가 상속받은 레벨) 다음의 요청은 수행된다.
barlogger.info("Located nearest gas station.");

// DEBUG < INFO이므로 다음 요청은 수행되지 않는다.
barlogger.debug("Exiting gas station search");

Retrieving Loggers

이름이 같은 LoggerFactory.getLogger 메소드를 호출하면 항상 동일한 로거 오브젝트에 대한 참조(reference)가 리턴된다.

Logger x = LoggerFactory.getLogger("wombat"); 
Logger y = LoggerFactory.getLogger("wombat");

즉, x와 y는 정확히 동일한 로거 객체를 가리킨다.

따라서 로거를 구성한 다음 reference를 전달하지 않고 코드의 다른 곳에서 동일한 인스턴스를 가져 올 수 있다. 또한, logback logger는 순서(부모객체-자식객체..)에 상관없이 만들어지고 구성 될 수 있다. 특히 "부모" 로거는 이후에 인스턴스화 된 경우에도 자신의 하위 항목을 찾아서 링킹 할 수 있다.

logback 환경 구성은 일반적으로 어플리케이션의 초기화시에 수행된다. 선호되는 방식은 configuration 파일을 읽는 것이다. 이 접근법은 곧 논의 될 것이다.

logback을 사용하면 간단하게 소프트웨어 컴포넌트별로 로거 이름을 지정 할 수 있다. 클래스의 유효한 이름과 동일한 로거 이름을 이용해 각 클래스의 로거를 인스턴스화 시킴으로써 이를 가능하게 한다. 이것은 로거를 정의하는 유용하고 간단한 방법이다. 로그 출력에 이를 생성한 로거의 이름이 있으므로 이름 지정 전략을 사용하면 로그 메세지의 출처를 쉽게 식별 할 수 있다. 하지만 이는 로거의 이름을 지정하기위한 하나의 전략일 뿐이며 logback은 가능한 로거의 설정을 제한하지 않는다. 개발자는 원하는대로 자유롭게 로거의 이름을 지정 할 수 있다.

그럼에도 불구하고, 클래스 생성 후에 로거의 이름을 지정하는 것이 가장 잘 알려진 일반적인 전략이긴하다.

Appenders and Layouts

그들의 로거를 기본으로 로깅 요청을 선택적으로 활성화 혹은 비활성화하는 기능은 picture의 일부일 뿐이다. logback을 통해 로깅 요청을 다양한 대상으로 print 할 수 있다. logback에서 출력 대상은 appender라고 불린다. 현재 콘솔, 파일, 원격 소켓 서버, MySQL/PostgreSQL/Oracle 등과 같은 데이터베이스, JMS, 원격 UNIX Syslog daemon에 대한 appender가 있다. 로거에 하나 이상의 appender을 추가 할 수 있다.

addAppender 메소드는 주어진 로거에 appender를 추가한다. 지정된 로거에 대해 사용 가능한 각 로깅 요청은 해당 로거 뿐만아니라 계층상 더 높은 로거의 appender에게도 전달된다. 즉, 어펜더는 로거 계층에서 추가적으로 상속된다. 예를 들어, 콘솔 appender가 root 로거에 추가되면 사용 가능한 모든 로깅 요청이 최소한 콘솔에서 출력된다. 또한 파일 appender가 로거 L에 추가되면 LL의 하위 로거에 대해 사용 가능한 로깅 요청이 파일(L에서 추가) 및 콘솔(root에서 추가)에 출력된다. 로거의 additivity 플래그를 false로 설정하여 appender가 누적되는 것을 막을 수 있다.

appender의 추가에 대한 규칙은 아래와 같다.

🔎 Appender Additivity
로거 L의 로그 명령문의 출력은 LL의 모든 조상의 appender로 이동한다. 이것이 "appender additivity"라는 용어의 의미이다.

하지만 L의 조상 로거인 Padditivity 플래그를 false로 했다면, L의 출력은 L를 비롯한 P까지의 모든 조상의 모든 appender에 이뤄지고 P이상의 조상의 appender는 사용되지 않는다.
로거의 additivity 플래그의 기본값은 true이다.

Logger NameAttached AppendersAdditivity FlagOutput TargetsComment
rootA1not applicableA1루트 로거는 로거 계층의 최상위이므로 Additivity Flag가 적용되지 않는다.
xA-x1, A-x2trueA1, A-x1, A-x2xroot의 Appenders
x.ynonetrueA1, A-x1, A-x2xroot의 Appenders
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1x, x.y.z, root의 Appenders
securityA-secfalseA-secAdditivity Flag가 false이므로 appender가 누적되지않는다. 따라서 security의 appender인 A-sec만 사용된다.
security.accessnonetrueA-secsecurity의 Additivity Flag가 false이므로 security의 Appender만 사용된다.

종종 사용자는 출력 대상 뿐만 아니라 출력 형식 또한 커스텀 마이징 하길 원한다. 이는 appender와 함께 레이아웃을 연관시킴으로써 수행된다. 레이아웃은 사용자의 요구에 따라 로깅 요청의 포맷을 정의하는 반면, appender는 포맷이 갖춰진 출력을 목적지로 전송한다. logback이 일반적으로 제공하는 PatternLayout을 통해 C 언어의 printf 함수와 유사한 변환 패턴을 따라 출력 형식을 지정할 수 있다.

예를 들어, 변환 패턴이 %-4relative [%thread] %-5level %logger{32} - %msg%n인 PatternLayout은 다음과 같은 결과를 출력한다. :
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.

첫 번째 필드 %-4relative는 프로그램 시작 이후 경과 된 시간(밀리 초)이다. 두 번째 필드 %thread는 로그 요청을 하는 스레드이다. 세 번째 필드 %-5level는 로그 요청의 레벨이다. 네 번째 필드 %logger{32}는 로그 요청과 연관된 로거의 이름이다. '-'뒤의 텍스트 %msg%n는 요청 메시지이다.

profile
🌱 😈💻 🌱

1개의 댓글

comment-user-thumbnail
2022년 12월 30일

매우 감사드립니다..........

답글 달기