지난 정리를 이어서 이번에는 Logback의 구조를 상세히 알아보고자 한다. 실습보다는 정의가 많은 챕터라 지루하게 느껴질 수 있지만 간단하게라도 쭉 읽어보길 바란다. 빠르게 실무에 적용하는 것도 중요하지만 기초가 단단한 것이 장기적으로 보았을 때 훨씬 중요하다고 생각한다. 혹시 본 정리내용이 틀리거나 보완해야 하는 점이 있다면 언제든지 댓글로 조언해 주길 바란다..
Logback의 구조
이전 정리에서 언급한것처럼 Logback은 아래 세 가지 모듈로 나뉜다.
- logback-core
- logback-classic
- logback-access
logback-core
는 다른 두 모듈의 토대를 마련한다. 이후 설명하겠지만 Appender
와 Layout
인터페이스가 이 모듈에 속한다.
logback-classic
은 위 언급한것 처럼 logback-core
의 확장 모듈로 logback-core
와 SLF4J API
를 가진다.
이후에 설명할 Logger
클래스가 이 모듈에 속해있다.
logback-access
는 Tomcat
및 Jetty
와 같은 Servlet Container와 통합되어 HTTP 액세스 로그 기능을 제공한다. 따라서 웹 애플리케이션 레벨이 아닌 컨테이너 레벨에서 설치되어야 한다.
Logger context
Logger
는 Console에 간단하게 로그를 찍을 때 주로 사용할 수 있는 System.out.println에 비해 특정 로그를 비활성화하여 불필요한 로그는 출력하지 않고 필요한 로그만 출력할 수 있다는 장점을 가지고 있다. 이 기능을 통해 로그를 개발자가 설정한 기준에 따라 분류할 수 있다.
모든 Logger
는 LoggerContext
에 부착되며, 계층구조를 가진다. Logger의 이름은 대소문자를 구분되며, 계층적 명명 규칙을 따른다. 예를들어, "com.hero"라는 Logger와 "com.hero.logger"라는 Logger가 있을 때 "com.hero"는 "com.hero.logger"의 부모 Logger다.
Root Logger
는 최상위 계층의 Logger
이며 중요한 개념이니 잘 기억해 두도록 하자.
모든 Logger
는 org.slf4j.LoggerFactory 클래스의 정적 클래스 메세드를 사용하여 아래 예시처럼 정의할 수 있다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest
{
public static void main( String[] args )
{
// Root Logger
Logger rootLogger = LoggerFactory.getLogger("org.slf4j.Logger.ROOT_LOGGER_NAME");
// 사용자 정의 Logger
Logger userLogger = LoggerFactory.getLogger("com.backhero.LogbackTest");
}
}
이전 정리글을 보았다면 익숙할 것이다.
참고로 로거이름을 꼭 클래스 이름으로 지정할 필요는 없다. 로거의 이름을 짓는 방법 중 하나일 뿐이라는 걸 기억하자. 그럼에도 불구하고 로거가 위치한 클래스 이름을 따서 명명하는 것이 가장 직관적이고 효율적이라고 생각한다.
Logback의 Log Level
ogback-classic모듈의 ch.qos.logback.classic.Level에 로거가 사용 가능한 다섯 가지 레벨인 TRACE, DEBUG, INFO, WARN, ERROR가 정의되어 있다. Logback의 로그 레벨은 아래와 같은 순서로 정의되어 있으며, 지정하지 않을 시 상위 로거의 레벨을 상속받고 기본 Level은 "DEBUG"다.
TRACE < DEBUG < INFO < WARN < ERROR
로거에 설정한 Log level보다 하위 Level의 로그는 기록되지 않는다. (중요한 개념이니 기억해 두자.)
예를 들어 INFO로 지정된 로거의 경우 INFO, WARN, ERROR로그만 기록한다. 아래 표를 참고해도 좋다.
로그 레벨들과 관련하여 Logger인터페이스에 아래와 같이 정의되어 있다.
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);
}
사용 예시는 아래와 같다. 이전 정리글에서 이미 사용해봤으니 익숙할 것이다.
package com.backhero;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest
{
public static void main( String[] args )
{
Logger logger = LoggerFactory.getLogger("com.backhero.LogbackTest");
logger.debug("It is debug level log");
logger.info("It is info level log");
}
}
Appenders와 Layouts
로거를 기반으로 로깅요청을 활성화 또는 비활성화 하는 것은 Logback의 기능 중 하나다. Logbackdml appender속성을 사용하면 로그를 출력하는 대상을 지정해 줄 수 있다. 대표적으로 콜솔(console), 파일(files), 원격 소켓 서버(remote socket servers), MySQL, PostgreSQL, Oracle 등 기타 데이터베이스, JMS 및 remote UNIX Syslog등이 있다.
Logger에 addAppender메서드를 사용하여 하나 이상의 Appender
를 부착할 수 있다. 또한 로깅 요청은 자신의 Appender뿐 아니라 상위 로거의 Appender까지 전달이 된다. 쉽게 이야기하면 위에서 최상위 로거인 Root Logger
에 ConsoleAppender를 붙였다면 하위 로거들로부터 전달받은 로깅 요청등은 Console에 출력된다는 뜻이다.
그렇다면 "Root Logger에 Console로 설정했지만 하위 로거에서 설정한 것만 출력하고 싶은데?"라는 의문이 생길 수 있다. Additivity
속성을 사용하여 이를 해결할 수 있다. 해당 옵션을 false로 지정하면 상위 로거의 설정(Log Level, Appender등)을 상속받지 않는다. 이해하기 쉽게 아래 표로 설명하겠다.
위 표처럼 Logger들이 설정되어있다면 Additivity설정이 true인 Hero 로거에서 발생되는 로그의 경우 INFO단계까지 File와 Console에 기록될 것이다. Additivitiy설정이 false인 Devil 로거의 경우 ERROR단계까지 File에만 기록하게 된다.
어디에 로그를 기록할지는 알게되었다. Layout
는 어떤 형식으로 기록될지는 설정하는 것이다. 대표적으로 변환 패턴이 "%-4relative [%thread] %-5level %logger{32} - %msg%n"인 PatternLayout은 아래와 같은 내용을 출력한다.
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
지금은 이런 것이 있구나 하고 넘어가는것이 좋을 것 같다 각각의 Appender와 Layout들의 자세한 사용 방법은 이후에 정리해서 올릴 예정이다.
매개변수를 사용한 로깅
logback-classic의 로거가 SLF4J의 로거 인터페이스를 상송 했기에 SLF4J와 같이 매개변수를 허용한다. 매개변수를 사용하는 이유는 코드의 가독성에 미치는 영향을 최소화하면서 성능을 향상시키기 위한 것이다.
// + 사용
logger.debug("My number is: " + number + " Age is "+ String.valueOf(age) + ".");
// {} 사용
Object name = new SomeObject();
logger.debug("My number is {} Age is {}", number, String.valueOf(age));
위와 같이 파라미터를 사용하여 로그를 출력하는 방법이 존재한다. 두 방식은 동일한 출력을 생성할 것이다.
하지만 프로그램 성능 면에서 차이가 존재한다. '+' 연산자를 사용한 로그 출력의 경우 number와 age를 문자로 변환하고 중간의 문자열을 연결하는 구성 비용이 발생한다. '{}'를 사용한 로그 출력의 경우 로깅 여부를 먼저 판단하고 '{}'를 변수와 변환하게 된다.
조금 더 쉽게 설명하자면 Error Level로 설정한 로거에서 INFO나 DEBUG LEVEL의 로그가 발생했을 때 + 사용의 경우 변수를 변환하는데 비용이 발생하지만 {}를 사용한 경우 해당 비용이 발생하지 않는다. 홈페이지 설명에 따르면 최소 30배 이상의 성능차이가 존재한다고 한다.
로그의 성능을 높이기 위해서는 아래와 같은 방식으로 로거를 생성할 필요가 있다.
마무리
지난 포스팅에 이어서 전체적으로 Logback이 무엇인가를 알아보았다. 다음 포스팅에서는 이제 실질적으로 Logback을 설정하고 활용하는 법에 대해서 다뤄볼까 한다. 로거 성능의 대해서는 일부 로거의 레벨의 단계를 OFF로 설정하여 성능을 향상 시킬수 있는 방법이 있고, 로깅이 켜져 있을 때 로깅 여부를 결정시킴으로 성능을 향상시키는 등 여러 가지 방법이 있으나, 공부해 보고 다룰 수 있으면 다뤄보고 싶다.
'Logback' 카테고리의 다른 글
Logback- 1. Logback이 무엇인가? 개념부터 알아보자 [Introduction to logback] (5) | 2022.12.28 |
---|