Spring에서 Logging 설정하기
실제 운영 서비스와 개발 단계의 로깅 전략을 분리하고 싶다는 생각에 로깅에 대해 찾아보았다.
목표는 개발 단계에서는 콘솔에서 로그를 확인하고, 운영 단계에서는 파일에 로그를 기록하고 날짜 별로 분리를 하고, Error 로그만 기록하는 Error 로그 파일을 따로 관리하는 것이다.
로깅이란?
서비스를 개발하거나 운영할 때 서비스의 에러를 파악하고, 디버깅하기 위해 사용한다.
그리고 유저의 행동와 같은 다양한 정보들도 로그를 통해서 기록하고 활용할 수 있다.
Log Level
로그를 나누는 기준은 크게 아래 다섯 개가 있다.
TRACE와 DEBUG는 개발 단계에서 자세한 내용을 확인할 때 사용하고, INFO는 정보를 기록할 때, WARN은 치명적이지는 않지만 일반적이지 않은 상황, ERROR는 서비스에 문제가 될 수 있는 중요한 상황에 사용한다.
TRACE < DEBUG < INFO < WARN < ERROR
만일 로그 레벨을 INFO로 하면 DEBUG와 TRACE는 출력되지 않고, TRACE로 하면 모든 레벨이 출력된다.
즉, 지정한 레벨의 상위 레벨의 로그만 출력된다.
Spring Logging Framework
스프링에서는 대표적인 로깅 프레임워크로 log4j와 logback을 사용하는데, logback의 경우 spring boot 의존성에 기본적으로 포함되어 있다.
logback은 log4j의 후속 버전으로 속도와 메모리 효율성이 향상되었다. 이후 logback 보다 멀티스레드 환경에서 높은 처리량을 가지는log4j2가 출시되었다.
현재로써는 logback이 가장 많이 사용되기 때문에 logback logging 설정 방법에 대해 알아보겠다.
Sl4fj
Simple Logging Facade for Java의 약자로 facade는 여러 개의 인터페이스를 하나로 묶은 통합 인터페이스를 활용하는 디자인 패턴이다. 즉, slf4j는 log4j를 사용하든 logback을 사용하든 @Slf4j를 사용하면 똑같이 사용할 수 있도록 지원해주는 기능을 제공한다.
Spring에서 logback 설정하기
Spring Logging 기본 설정 파일 이름은 logback-spring이며 xml과 groovy를 지원한다.
로그 설정은 .properties 파일과 logback 파일 모두 가능한데, logback 파일에서 전부 처리하는 것이 깔끔하다.
Logback Architecture
Logger
- 로그 메세지 생성을 담당하는 클래스
Appender
- 로거가 전달한 로깅 이벤트를 받아 실제로 로깅을 진행하는 담당
ConsoleAppender | 콘솔에 출력하는 역할 - 개발에 적합 |
FileAppender | 하나의 파일에 로그를 기록 - 운영에 사용할 수 있으나 파일이 너무 커지기는 문제가 있음 |
RollingAppender | 지정된 패턴에 따라 파일을 나누어 로그를 기록 - 운영에 적합 |
SMTPAppender | 로그 메일을 발송 |
DBAppender | DB에 로그 저장 |
Layout
- 로그 포맷을 담당하는 클래스
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="applicationName" converterClass="org.springframework.boot.logging.logback.ApplicationNameConverter" />
<!-- 변수 선언 -->
<property name="ROOT_PATH" value="./logs"/>
<property name="FILE_NAME_PATTERN" value="%d{yyyy-MM-dd}.%i.log"/>
<!-- Logging Pattern: SpringBoot Default pattern 사용 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- Console Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Daily Rolling File Appender -->
<appender name="DAILY-ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${ROOT_PATH}/daily/${FILE_NAME_PATTERN}</fileNamePattern>
<totalSizeCap>500MB</totalSizeCap>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- ERROR Rolling File Appender -->
<appender name="ERROR-ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${ROOT_PATH}/ERROR/%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Profile 적용 -->
<springProfile name="dev">
<root level="info">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="prod">
<root level="info">
<appender-ref ref="DAILY-ROLLING"/>
</root>
<logger name="ERROR-LOGGER" level="error" additivity="false">
<appender-ref ref="ERROR-ROLLING"/>
</logger>
</springProfile>
</configuration>