logback은 log4j 프로젝트의 후속 버전으로 log4j 1.x가 중단된 이후 버전을 이어받아 발전시켜 왔다.
spring boot는 기본적으로 logback 로깅 시스템을 채택하여 제공한다.
logback-spring.xml 작성을 통해서 logback에 대한 확장 설정을 통해서 로그를 커스텀하게 기록할 수 있다.
이번 포스팅에서는 logback에 대한 몇 가지 중요한 내용에 대한 설명과 이를 활용하여 logback 확장설정(logback-spring.xml) 하는 방법에 대해서 기록한다.
sprng boot dependency
org.springframework.boot:spring-boot-starter-logging 종속성에 logback에 대한 종속성이 포함되어 있다.
org.springframework.boot:spring-boot-starter-logging 종속성은 org.springframework.boot:spring-boot-starter 종속성에 포함되어 있다.
Logback Architecture
logback 아키텍처는 Logger, Appender, Layout 세 가지 클래스로 구성된다.
Logger는 로그 메시지의 콘텍스트이다. 애플리케이션이 로그 메시지를 생성하기 위해서 상호 작용하는 클래스를 의미한다.
Appender는 로그 메시지를 최종 대상에 배치한다. Logger는 둘 이상의 Appender를 가질 수 있다.
Layout은 출력할 메시지를 준비하는 역할을 한다. logback은 메시지 서식 지정을 위한 사용자 지정 클래스 생성을 지원하고 기존 클래스에 대한 구성 옵션도 지원한다.
로그 레벨
logback에서 지원하는 레벨은 아래와 같다.
- TRACE
- DEBUG
- INFO
- WARN
- ERROR
로그 레벨은 ch.qos.logback.classic.Level 클래스에 정의되어 있다.
로그 레벨 상속
Logger에 로그 레벨이 할당되지 않은 경우 로그 레벨이 할당된 가장 가까운 조상 Logger로부터 레벨을 상속받는다.
모든 Logger가 레벨을 상속받을 수 있도록 하기 위해서 root Logger에는 항상 할당된 레벨이 있고 디폴트 레벨은 DEBUG이다.
몇 가지 예시를 들어 내용을 살펴본다.
아래 예시는 logback document를 참고하였다.
예시 1)
Logger name | 지정된 레벨 | 적용되는 레벨 |
root | DEBUG | DEBUG |
X | 없음 | DEBUG |
X.Y | 없음 | DEBUG |
X.Y.Z | 없음 | DEBUG |
X, X.Y, X.Y.Z Logger에는 로그 레벨이 할당되지 않았기 때문에 root Logger의 로그 레벨을 상속받는다.
예시 2)
Logger name | 지정된 레벨 | 적용되는 레벨 |
root | ERROR | ERROR |
X | INFO | INFO |
X.Y | DEBUG | DEBUG |
X.Y.Z | WARN | WARN |
모든 Logger에 로그 레벨이 지정되어 있기 때문에 상속은 일어나지 않는다. 각 Logger에 지정된 로그 레벨이 적용된다.
예시 3)
Logger name | 지정된 레벨 | 적용되는 레벨 |
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | 없음 | INFO |
X.Y.Z | ERROR | ERROR |
X.Y Logger에는 로그 레벨이 지정되지 않았기 때문에 가장 가까운 조상인 X Logger로부터 로그 레벨을 상속받는다.
예시 4)
Logger name | 지정된 레벨 | 적용되는 레벨 |
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | 없음 | INFO |
X.Y.Z | 없음 | INFO |
X.Y, X.Y.Z Logger에는 로그 레벨이 지정되지 않았기 때문에 가장 가까운 조상인 X Logger로부터 로그 레벨을 상속받는다.
로그 레벨 유효 범위
적용된 로그 레벨 q를 가진 Logger에게 발행된 레벨 p의 로그 요청을 p >= q일 때 활성화 된다.
logback에 적용된 로그 레벨의 정렬은 아래와 같다.
TRACE < DEBUG < INFO < WARN < ERROR
세로 열은 로그 요청 레벨이고 가로 열은 Logger에 지정된 로그 레벨이다.
Logger에 적용된 레벨이 TRACE인 경우에는 모든 요청 레벨에 대해서 로그가 기록된다.
Logger에 적용된 레벨이 ERROR인 경우에는 ERRO 요청 레벨인 경우에만 로그가 기록된다.
Logback Configuration
Status Data
상태 데이터 출력을 활성화하면 일반적으로 logback 관련 문제를 진단하는데 도움이 된다.
디스크가 차거나 권한 오류로 인해 로그 파일을 아카이브 할 수 없는 경우와 같이 구성 후에도 오류가 발생할 수 있는 문제를 진단할 수 있다. 이를 위해서는 아래와 같이 StatusListener를 등록하여 logback에 대한 상태 로그도 출력할 수 있다.
<configuration>
<!-- Recommendation: place status listeners towards the the top of the configuration file -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
<!-- ... the rest of the configuration file -->
</configuration>
Listener는 <configuration>의 상단에 배치하여 등록하는 것을 권장한다.
추가로 OnErrorConsoleStatusListener나 OnFileStatusListener를 등록할 수 있다.
debug=true를 지정하여 위 설정을 간단하게 지정할 수도 있다.
<configuration debug="true">
...
</configuration>
debug="true"를 설정한 것은 OnConsoleStatusListener를 설치한 것과 동일하다.
OnConsoleStatusListener를 설정 후 애플리케이션을 실행하면 아래와 같이 logback에 대한 상태 로그가 출력된다.
15:49:52,466 |-INFO in ch.qos.logback.classic.LoggerContext[default] - This is logback-classic version 1.4.11
15:49:52,467 |-INFO in ch.qos.logback.classic.util.ContextInitializer@4648ce9 - Here is a list of configurators discovered as a service, by rank:
15:49:52,467 |-INFO in ch.qos.logback.classic.util.ContextInitializer@4648ce9 - org.springframework.boot.logging.logback.RootLogLevelConfigurator
15:49:52,467 |-INFO in ch.qos.logback.classic.util.ContextInitializer@4648ce9 - They will be invoked in order until ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY is returned.
15:49:52,467 |-INFO in ch.qos.logback.classic.util.ContextInitializer@4648ce9 - Constructed configurator of type class org.springframework.boot.logging.logback.RootLogLevelConfigurator
15:49:52,470 |-INFO in ch.qos.logback.classic.util.ContextInitializer@4648ce9 - org.springframework.boot.logging.logback.RootLogLevelConfigurator.configure() call lasted 1 milliseconds. ExecutionStatus=INVOKE_NEXT_IF_ANY
15:49:52,470 |-INFO in ch.qos.logback.classic.util.ContextInitializer@4648ce9 - Trying to configure with ch.qos.logback.classic.joran.SerializedModelConfigurator
...
15:49:52,652 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating WARN level on Logger[org.apache.coyote.http11.Http11NioProtocol] onto the JUL framework
15:49:52,652 |-INFO in ch.qos.logback.classic.model.processor.LoggerModelHandler - Setting level of logger [org.apache.sshd.common.util.SecurityUtils] to WARN
15:49:52,652 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating WARN level on Logger[org.apache.sshd.common.util.SecurityUtils] onto the JUL framework
15:49:52,652 |-INFO in ch.qos.logback.classic.model.processor.LoggerModelHandler - Setting level of logger [org.apache.tomcat.util.net.NioSelectorPool] to WARN
15:49:52,652 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating WARN level on Logger[org.apache.tomcat.util.net.NioSelectorPool] onto the JUL framework
15:49:52,652 |-INFO in ch.qos.logback.classic.model.processor.LoggerModelHandler - Setting level of logger [org.eclipse.jetty.util.component.AbstractLifeCycle] to ERROR
15:49:52,652 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating ERROR level on Logger[org.eclipse.jetty.util.component.AbstractLifeCycle] onto the JUL framework
15:49:52,652 |-INFO in ch.qos.logback.classic.model.processor.LoggerModelHandler - Setting level of logger [org.hibernate.validator.internal.util.Version] to WARN
15:49:52,652 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating WARN level on Logger[org.hibernate.validator.internal.util.Version] onto the JUL framework
15:49:52,653 |-INFO in ch.qos.logback.classic.model.processor.LoggerModelHandler - Setting level of logger [org.springframework.boot.actuate.endpoint.jmx] to WARN
15:49:52,653 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating WARN level on Logger[org.springframework.boot.actuate.endpoint.jmx] onto the JUL framework
15:49:52,653 |-INFO in ch.qos.logback.core.model.processor.AppenderModelHandler - Processing appender named [CONSOLE]
15:49:52,653 |-INFO in ch.qos.logback.core.model.processor.AppenderModelHandler - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
15:49:52,654 |-INFO in ch.qos.logback.core.model.processor.ImplicitModelHandler - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
15:49:52,659 |-INFO in ch.qos.logback.classic.model.processor.RootLoggerModelHandler - Setting level of ROOT logger to INFO
15:49:52,659 |-INFO in ch.qos.logback.classic.jul.LevelChangePropagator@4ebea12c - Propagating INFO level on Logger[ROOT] onto the JUL framework
15:49:52,659 |-INFO in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Attaching appender named [CONSOLE] to Logger[ROOT]
15:49:52,659 |-INFO in ch.qos.logback.core.model.processor.DefaultProcessor@2baa8d82 - End of configuration.
15:49:52,659 |-INFO in org.springframework.boot.logging.logback.SpringBootJoranConfigurator@791cbf87 - Registering current configuration as safe fallback point
configuration file 자동 재로딩
구성 파일의 변경 사항을 검색하고 구성 파일이 변경되면 자동으로 구성 정보를 재로딩 할 수 있다.
<configuration scan="true" scanPeriod="30 seconds" >
...
</configuration>
기본적으로 scan="true"인 경우 검사 주기는 1분이지만 scanPeriod 속성을 지정하여 검사 주기를 설정할 수 있다.
시간 단위를 지정하지 않으면 디폴트로 밀리초로 간주된다.
지정할 수 있는 시간 단위는 다음과 같다.
- milliseconds (ex. 30 milliseconds)
- seconds (ex. 30 seconds)
- minutes (ex. 30 minutes)
- hours (ex. 1 hours)
configuration 파일 구조
configuration 파일의 가장 기본적인 구조는 0개 이상의 <appender> 요소와 그 뒤에 0개 이상의 <logger> 요소, 그리고 최대 하나의 <root> 요소를 포함하는 <configuration> 태그 구조다.
위 이미지 출처는 https://logback.qos.ch/manual/configuration.html이다.
기본적인 configuration 파일의 구조 예이다.
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO"/>
<!-- Strictly speaking, the level attribute is not necessary since -->
<!-- the level of the root level is set to DEBUG by default. -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
<configuration> 요소 안에 하나의 <appender> 요소와 <logger> 요소 그리고 <root> 요소가 포함되어 있다.
<appender> 요서 안에 정의된 <encoder> 요소는 인스턴스화할 encoder 클래스의 정규화된 full 이름을 지정하는 class 속성을 지정하는데 encoder 클래스가 PattenrLayoutEncoder인 경우 class 속성을 생략할 수 있다.
context name 설정
- 모든 Logger는 Logger Context에 연결되는데 기본적으로 Logger Context는 "default"로 사용된다.
- <contextName> 태그를 사용하여 Logger Context 이름을 변경할 수 있다.
- Logger Context의 이름은 한번 설정하면 변경할 수 없다.
- Context 이름을 설정하는 것은 동일한 대상에 로깅하는 여러 애플리케이션을 구분하기 위한 간단한 방법이다.
<configuration>
<contextName>myAppContext</contextName>
...
</configuration>
변수 설정
- 변수는 구성 파일 내에서 한 번에 하나씩 정의하거나 외부 속성 파일 또는 외부 리소스에서 일괄 로드 할 수 있다.
- 변수를 정의하는 XML 요소는 <property>이지만 logback 1.0.7 이상에서는 <variable> 요소와 혼용해서 사용할 수 있다.
구성 파일 내에서 사용하기 위한 단일 변수 생성 예이다.
<configuration>
<variable name="USER_HOME" value="/home/sebastien" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
...
</configuration>
외부 System property 속성을 변수로 사용하는 예이다.
java -DUSER_HOME="/home/sebastien" app
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
...
</configuration>
변수를 정의한 설정 파일을 로드하여 사용하는 예이다.
src/main/resources/logback.properties
USER_HOME=/home/sebastien
<configuration>
<variable file="src/main/resources/logback.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
...
</configuration>
class path에서 리소스 파일을 로드 하여 사용하는 예이다.
<configuration>
<variable resource="logback.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
...
</configuration>
variable scope 속성이 있는데 3가지 정의가 있다.
- local : 구성 파일에서 정의된 시점부터 해당 구성 파일의 해석/실행이 끝날 때까지 존재한다.
- context : context에 해당 변수가 삽입되며 context가 삭제될 때까지 유지된다. 원격 호스트로 전송되는 이벤트를 포함하여 모든 로깅 이벤트에서 사용할 수 있다.
- system : JVM의 system property에 삽입되며 JVM이 지속되는 동안 유지된다.
<springProperty> 요소를 사용하여 scope을 지정하여 사용하는 경우에도 동일하게 적용되는 듯하다.
# application.yml
spring:
application:
name: logging-test
<configuration>
<springProperty scope="local" name="PKG_NAME" source="spring.application.name" />
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${PKG_NAME}.log</file>
...
</configuration>
PKG_NAME 변수는 local scope로써 spring.application.name 속성 값을 사용한다.
configuration 파일의 구문 분석이 완료될 때까지 유지된다.
변수의 default 값
외부 설정 파일로부터 변수 값을 주입받거나 하는 경우 해당 설정이 주입되지 않은 경우 변수의 default 값 표현은 아래와 같다.
${변수명:-defaultValue}
<configuration>
<variable resource="logback.properties"/>
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
...
<!-- rolling 정책 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
...
<maxFileSize>${logging.application.file.max-size:-10MB}</maxFileSize>
<maxHistory>${logging.application.file.max-history:-7}</maxHistory>
</rollingPolicy>
</appender>
</configuration>
class path에 있는 logback.properties 파일에
logging.application.file.max-size 설정이 없는 경우 10MB 값으로 동작한다.
logging.application.file.max-history 설정이 없는 경우 7 값으로 동작한다.
Appender
Appender는 쉽게 말해 로그의 출력 대상을 의미한다.
지원되는 Appender 유형은 다음과 같다.
- console
- file
- remote server socket
- DB
- JMS
- UNIX syslog demon
- SMTP
- ...
Logger는 두 개 이상의 Appender 유형을 적용할 수 있다.
additivity
Appender는 Logger 계층 구조에서 추가적으로 상속된다.
예를 들어 Console Appender가 root Logger에 추가되면 활성화된 모든 로깅 요청이 최소한 콘솔에 적용된다.
L이라는 Logger에 File Appender가 추가되면 L 및 L의 자식 Logger에 대해서 활성화된 로깅 요청이 파일과 콘솔에 모두 출력된다.
Logger의 additivity 속성을 false로 지정하면 이러한 상속이 이루어지지 않도록 할 수 있다. (additivity는 default true)
아래 표는 additivity 속성에 대한 예시 표이다.
root Logger는 additivity flag를 설정할 수 없다.
위 표에서 x, x.y, x.y.z는 상위 Logger로부터 appender를 상속받아 적용됨을 알 수 있다.
security.access Logger는 additivity가 true 지만 상위 security Logger의 additivity가 false 이므로 root Logger의 appender를 상속받지 않는다.
logback에서 기본적으로 지원하는 appender는 아래와 같다.
각 항목의 appender에 대한 문서 링크 연결해 두었다.
- OutputStreamAppender (logback-core)
- ConsoleAppender (logback-core)
- FileAppender (logback-core)
- RollingFileAppender (logback-core)
- SocketAppender and SSLSocketAppender (logback-classic)
- ServerSocketAppender and SSLServerSocketAppender (logback-classic)
- SMTPAppender (logback-classic)
- DBAppender (logback-classic)
- SyslogAppender (logback-classic)
- SiftingAppender (logback-classic)
- AsyncAppender (logback-classic)
access 로그를 위한 logback-access 모듈에도 logback-classic에서 제공하는 appender와 동일한 appender를 제공한다.
가장 일반적으로 많이 사용되는 RollingFileAppender에 대해서 알아보자.
나머지 Appender에 대해서는 각 링크의 문서를 참고하면 도움이 된다.
RollingFileAppender
RollingFIleAppender는 로그 파일을 롤오버 하는 기능으로 FileAppender를 확장한다.
RollingFileAppender와 상호작용하는 두 가지 하위 컴포넌트가 있는데 다음과 같다.
- RollingPolicy : 롤오버에 필요한 작업을 수행한다. (무엇을)
- TriggeringPolicy : 롤오버가 발생할지 여부와 정확한 시기를 결정한다. (언제)
RollingFileAppender를 사용하려면 RollingPolicy와 TriggeringPolicy 모두 설정되어야 하는데 RollingPolicy가 TriggeringPolicy 인터페이스도 구현하는 경우 RollingPolicy만 명시적으로 지정하면 된다.
대표적으로 사용되는 RollingPolicy는 SizeAndTimeBaseRollingPolicy인데 이것은 TriggeringPolicy도 함께 구현하고 있다.
properties
RollingFileAppender의 속성은 아래와 같다.
Property Name | Type | Description |
file | String | null인 경우 로그는 RollingPolicy에 지정된 대상에만 기록된다. |
append | boolean | true인 경우 기존 파일 끝에 로그가 추가된다. false인 경우 기존 파일은 truncate 된다. |
encoder | Encoder | 로그 이벤트 기록되는 방식을 결정한다. Encoder 페이지를 참고. |
rollingPolicy | RollingPolicy | 롤오버가 발생할 때 RollingFileAppender의 동작을 설정하는 컴포넌트이다. |
triggeringPolicy | TriggeringPolicy | 롤오버가 발생할 조건을 설정하는 컴포넌트이다. |
prudent | boolean | prudent 모드는 동일한 파일에 쓰는 모든 JVM간의 I/O 작업을 효과적으로 직렬화 한다. exclusive file lock에 크게 의존하게 되어 prudent 모드를 사용하지 않을때와 비교하여 로그 이벤트 쓰기 비용을 약 3배 정도 증가시키는 것으로 나타났다. prudent 모드에서는 FixedWindowRollingPolicy가 지원되지 않는다. TimeBaseRollingPolicy와 함께 prudent 모드에서는 두가지 제약이 있다. 1. prudent 모드에서는 파일 압축이 지원되지 않는다. 2. FileAppender의 file 속성은 설정할 수 없으며 비워둬야 한다. 대부분의 운영체제 에서는 다른 프로세스에서 파일이 열려 있는 동안에는 파일 이름을 변경할 수 없다. 디폴트는 false 로 동작한다. |
RollingPolicy
- TimeBasedRollingPolicy
- 일별 또는 월별등 시간을 기준으로 롤오버 정책을 정의한다.
- RollingPolicy와 TriggeringPolicy 인터페이스를 모두 구현한다.
- fileNamePattern 속성은 필수 속성이며 이 속성으로 롤오버 되는 로그 파일의 이름을 정의한다.
- 파일 이름과 적절하게 배치된 %d 변환 지정자로 구성되어야 한다.
- %d 변환 지정자에는 java.text.SimpleDateFormat 클래스에 지정된 날짜 및 시간 패턴이 포함될 수 있다.
- 날짜 및 시간 패턴을 생략하면 기본 패턴인 yyyy-MM-dd가 사용된다.
- 롤오버 기간은 fileNamePattern의 값에서 추론된다.
public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements TriggeringPolicy<E>
example
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
TimeBasedRollingPolicy에 대한 상세한 스펙은 해당 링크를 참고하기 바란다.
- SizeAndTimeBasedRollingPolicy
- 파일을 기본적으로 날짜별로 아카이브 하면서 동시에 각 로그 파일의 크기를 제한하고 싶은 경우 사용한다.
- totalSizeCap 속성을 사용하여 이미 아카이브 된 로그 파일의 합산 크기를 제한할 수 있다.
- fileNamePattern에서 %i와 %d 토큰은 필수로 사용해야 한다.
- maxHistory 속성을 사용하여 파일 보관 기간을 지정하여 오래된 파일의 삭제를 지원한다.
- RollingPolicy와 TriggeringPolicy 인터페이스를 모두 구현한다.
exampel
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
- FixedWindowRollingPolicy
- FixedWindowRollingPolicy는 더 이상 사용되지 않는 정책으로 간주하며 사용을 권장하지 않는다.
TriggeringPolicy
TriggeringPolicy는 롤오버 시점을 RollingFileAppender에 지시하는 역할을 담당한다.
TriggeringPolicy 인터페이스를 구현하는 구현체는 아래와 같다.
RollingFileAppender에서 SizeAndTimeBasedRollingPolicy 혹은 TimeBasedRollingPolicy가 주로 사용되므로 TriggeringPolicy를 따로 지정해서 사용하는 일은 거의 없을 듯하다.
example
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>test.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
Layout
- Layout은 로그 이벤트를 String으로 변환하는 logback 컴포넌트다.
- Layout을 Appender와 연결하여 출력 형식을 지정할 수 있다.
- Layout은 사용자가 원하는 대로 로깅 요청의 형식을 지정하는 역할을 한다.
- 표준 logback 배포의 일부인 PatternLayout을 사용하여 C 언어의 printf 함수와 유사한 패턴으로 출력 형식을 지정할 수 있다.
PatternLayout
- PatternLay의 변환 패턴은 C 프로그래밍 언어의 printf() 함수의 변환 패턴과 밀접한 관련이 있다.
- 변환 패턴은 리터럴 텍스트와 변환 지정자라고 하는 형식 제어 표현식으로 구성된다.
- 각 변환 지정자는 '%'로 시작하고 중괄호 사이에 선택적 형식 수정자, 변환 단어 및 선택적 매개변수가 이어진다.
- 변환 단어는 Logger 이름, 로그 레벨, 날짜 또는 스레드 이름등 변환할 데이터 필드를 제어한다.
- 형식 수정자는 필드 너비, 패딩, 왼쪽 또는 오른쪽 맞춤을 제어한다.
- FileAppender와 그 서브 클래스(e.g. RollingFileAppender)들은 Encoder를 필요로 한다.
- FileAppender 혹은 그 서브 클래스와 함께 사용할 때 PatternLayout은 Encoder 안에 래핑 되어야 한다.
- PatternLayoutEncoder는 PatternLayout 인스턴스를 인코더로 처리할 수 있도록 래핑 한 클래스이다.
변환 단어표
대표적인 변환 단어와 해당 옵션에 대한 표이다. 동일 셀에 여러 개의 변환 단어가 있는 경우 별칭으로 간주한다.
자세한 사항은 PatternLayout 링크를 참고하면 도움이 된다.
변화 단어 | 효과 |
%c{length} %lo{length} %logger{length} |
Logger의 이름을 출력한다. %logger : mainPackage.sub.sample.Bar %logger{10} : m.s.s.Bar |
%contextName %cn |
Logger가 연결된 Logger context의 이름을 출력한다. |
%d{pattern} %date{pattern} %d{pattern, timezone} %date{pattern, timezone} %d{pattern, timezone, locale} %date{pattern, timezone, locale} |
로깅 이벤트의 날짜를 출력한다. 날짜 변환 단어는 패턴 문자열을 매개변수로 허용한다. 패턴 구문은 java.text.SimpeDateFormat(logback 1.2.X) java.time.format.DateTimeFormatter (logback 1.3.X) 에서 허용하는 형식과 호환된다. 패턴 매개변수가 없는 경우 기본적으로 ISO 8601 날짜 형식을 사용한다. %d : 2006-10-20 14:06:49,812 %date : 2006-10-20 14:06:49,812 %date{HH:mm:ss.SSS} 14:06:49.812 ',' 문자는 매개 변수 구분 기호로 사용되므로 패턴 구문에 ',' 문자가 포함되면 오류가 발생할 수 있다. 날짜 패턴에 ',' 문자를 포함하려면 패턴을 작은따옴표 혹은 큰따옴표로 묶어야 한다. %date{"HH:mm:ss,SSS"} 혹은 %date{'HH:mm:ss,SSS'} |
%L %line |
로깅 요청이 발생한 라인 번호를 출력한다. 라인 번호 정보를 생성하는 속도가 빠르지 않기 때문에 가급적 사용하지 않을 것을 권고 |
%m %msg %message |
애플리케이션에서 제공한 로그 메시지를 출력한다. 애플래케이션에서 작성한 log.info("log message"); 내용 이라고 보면 된다. |
%n | 줄바꿈 문자를 출력한다. |
%p %le %level |
로그 레벨을 출력한다. |
%t %thread |
로깅 이벤트가 발생한 스레드 이름을 출력한다. |
%X{key:-defaultValue} %mdc{key:-defaultValue} |
로깅 이벤트를 생성한 스레드와 연관된 MDC를 출력한다. %mdc{userid} : userid 키에 해당하는 MDC 값이 출력된다. 값이 null 이면 :- 연산자 뒤에 지정된 default 값이 출력된다. 키를 지정하지 않으면 MDC의 전체 내용이 "key1=val1, key2=val2" 형식으로 출력된다. |
- % 기호는 특별한 의미를 가지므로 리터럴로 포함하려면 백슬래시로 escape 처리를 해야 한다.
- 로그 레벨에 대해서 TRACE, DEBUG, INFO, WARN, ERROR를 출력하는 대신 T, D, I, W, E 만 출력하려면 %.-1level 을 지정
- Grouping with parentheses 링크 참고
- Coloring 링크 참고
HTMLLayout
- HTMLLayout은 HTML 형식으로 로그를 생성한다.
- HTML 테이블의 각 행에 로깅 이벤트를 출력한다.
- 테이블 열의 콘텐츠는 변환 패턴을 사용하여 지정된다.
- HTMLLayout과 함께 PatternLayout을 사용할 대 변환 지정자를 공백 문자나 더 일반적으로 리터럴 텍스트로 구분해서는 안된다.
- 패턴에서 발견되는 각 지정자는 별도의 열을 생성한다.
- 패턴에 있는 각 리터럴 텍스트 블록에 대해 별도의 열이 생성되므로 화면에서의 공간을 낭비할 수 있다.
- example
<configuration debug="true">
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%relative%thread%mdc%level%logger%msg</pattern>
</layout>
</encoder>
<file>test.html</file>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
test.html 파일이 생성되며 파일의 내용은 다음과 같은 형식이 될 것이다.
지금까지 내용 외에도 관련된 내용이 상당히 많은데 해당 내용은 logback 사이트 document를 참고하면 도움이 된다.
logback-spring.xml 샘플
지금까지의 내용을 기반으로 logback-spring.xml 샘플을 작성하였다.
샘플 logback configuration은 기능상 4가지 요소로 분리하여 각 요소별로 로그 파일을 분리하여 기록하도록 하였다.
별도의 logging properties(logback.properties) 파일을 두어 variable을 하나의 파일로 관리하도록 하였다.
- SMTP 프로토콜 기반으로 이메일을 수신하는 MTA 모듈 (appender 이름 : MTA)
- Spring Boot 기본 제공 모듈 (appender 이름 : APPLICATION)
- JPA 모듈 (appender 이름 : QUERY)
- 이메일 전송 모듈 (appender 이름 : DELIVERY)
logback.properties
파일 위치는 resources/logback.properties이다.
logging.mta.filename=mta.log
logging.mta.file.max-history=7
logging.mta.file.max-size=10MB
logging.mta.file.logpattern=%-5level %d{yy-MM-dd HH:mm:ss.SSS} %msg%n
logging.application.filename=application.log
logging.application.file.max-history=7
logging.application.file.max-size=10MB
logging.application.file.logpattern=%-5level %d{yyyy/MM/dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n
logging.query.filename=qry.log
logging.query.file.max-size=10MB
logging.query.file.max-history=7
logging.query.file.logpattern=%-5level [%d{yy-MM-dd HH:mm:ss}] %msg%n
logging.delivery.filename=delivery.log
logging.delivery.file.max-size=10MB
logging.delivery.file.max-history=7
logging.delivery.file.logpattern=%-5level [%d{yy-MM-dd HH:mm:ss}] %msg%n
설정 항목에서 짐작할 수 있겠지만 RollingFileAppender와 SizeAndTimeBasedRollingPolicy를 적용하였다.
롤오버 조건은 일별로 10MB 단위로 로그 파일이 생성된다. 로그 보관기간은 7일이다.
logback-spring.xml
파일 위치는 resources/logback-spring.xml이다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- 60 초마다 설정파일의 변경을 확인 후 갱신 -->
<configuration scan="true" scanPeriod="60 seconds">
<!--
LOG_DIR을 직접 변수로 선언하는 것 대신에
application.yml 에서 logging.file.path: log 로 설정하여 바로 ${LOG_PATH}로 사용해도 된다.
-->
<property name="LOG_DIR" value="./log"/>
<!-- class path의 logback.properties 파일을 로딩하여 variables로 사용한다 -->
<property resource="logback.properties"/>
<!-- file appender -->
<appender name="MTA" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/${logging.mta.filename}</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${logging.mta.file.logpattern}</pattern>
</encoder>
<!-- rolling 정책 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/${logging.mta.filename}.%d{yyyy-MM-dd}.%i.log}</fileNamePattern>
<maxFileSize>${logging.mta.file.max-size:-10MB}</maxFileSize>
<maxHistory>${logging.mta.file.max-history:-7}</maxHistory>
</rollingPolicy>
</appender>
<!-- 에러의 경우 파일 로그 처리 -->
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/${logging.application.filename}</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${logging.application.file.logpattern}</pattern>
</encoder>
<!-- rolling 정책 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/${logging.application.filename}.%d{yyyy-MM-dd}.%i.log}</fileNamePattern>
<maxFileSize>${logging.application.file.max-size:-10MB}</maxFileSize>
<maxHistory>${logging.application.file.max-history:-7}</maxHistory>
</rollingPolicy>
</appender>
<appender name="QUERY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/${logging.query.filename}</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${logging.query.file.logpattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/${logging.query.filename}.%d{yyyy-MM-dd}.%i.log}</fileNamePattern>
<maxFileSize>${logging.query.file.max-size:-10MB}</maxFileSize>
<maxHistory>${logging.query.file.max-history:-7}</maxHistory>
</rollingPolicy>
</appender>
<appender name="DELIVERY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/${logging.delivery.filename}</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${logging.delivery.file.logpattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/${logging.delivery.filename}.%d{yyyy-MM-dd}.%i.log}</fileNamePattern>
<maxFileSize>${logging.delivery.file.max-size:-10MB}</maxFileSize>
<maxHistory>${logging.delivery.file.max-history:-7}</maxHistory>
</rollingPolicy>
</appender>
<!-- 로거 설정 -->
<logger name="delivery" level="DEBUG" additivity="false">
<appender-ref ref="DELIVERY"/>
</logger>
<logger name="com.mail.smtp" level="TRACE" additivity="false">
<appender-ref ref="MTA"/>
</logger>
<!-- about hibernate query log -->
<!-- 쿼리 결과 : level 을 TRACE 로 변경하면 결과 로그가 생성된다. -->
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" additivity="false">
<appender-ref ref="QUERY"/>
</logger>
<!-- 쿼리 파라미터 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" additivity="false">
<appender-ref ref="QUERY"/>
</logger>
<!-- 쿼리문 -->
<logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
<appender-ref ref="QUERY"/>
</logger>
<!-- root 레벨 설정 -->
<root level="DEBUG">
<appender-ref ref="APPLICATION"/>
</root>
</configuration>
- ConsoleAppender는 사용하지 않기 때문에 console로그는 생성되지 않는다.
- 각 Logger의 additivity 속성을 false로 지정하여 root Logger의 appender를 상속받지 않는다.
- 지정된 Logger는 각자 별도 파일에만 기록이 된다.
- QUERY appender는 JPA 쿼리 및 binding 정보 로그를 기록한다.
logback 관련 문서의 내용이 상당히 방대하기 때문에 모든 내용을 이 글에 담지는 못해서 아쉽지만 logback 문서를 살펴보면 많은 도움이 될 것이다.
참고링크
https://logback.qos.ch/manual/configuration.html
https://logback.qos.ch/manual/appenders.html
https://logback.qos.ch/manual/encoders.html
https://logback.qos.ch/manual/layouts.html#customConversionSpecifier
'스프링부트' 카테고리의 다른 글
spring boot 3.1 docker compose support (0) | 2023.10.22 |
---|---|
spring boot container 기반 테스트 (0) | 2023.10.19 |
spring boot logging (0) | 2023.10.12 |
STOMP와 ACTIVEMQ를 이용한 메시지 broadcasting (0) | 2023.10.11 |
spring boot scheduler basic (0) | 2023.10.09 |
댓글