Programming/Spring Boot

spring boot 로그 파일 남기는 방법 (logback-spring.xml 설정)

Jan92 2023. 8. 20. 19:25
반응형

스프링 부트 logback-spring.xml 파일을 통한 로깅 설정

 

logback-spring.xml 설정

해당 포스팅은 '스프링 부트 환경에서 로그를 콘솔 또는 파일 등으로 기록하는 방법'에 대해 정리한 내용입니다.

.properties(또는 .yml) 파일을 통해서도 로깅 관련 설정을 할 수 있지만 logback-spring.xml 파일을 통해 조금 더 세부적인 설정을 할 수 있기 때문에 logback-spring.xml에 대해 더 중점을 두고 정리되었다는 점 참고 부탁드립니다.

 

(작업 환경은 'org.springframework.boot' version '3.1.2'에서 진행되었습니다.)

 


로그와 로깅이란?

'로그(Log)'의 사전적 의미는 시스템에서 발생되는 모든 행위와 이벤트 정보를 시간에 따라 남겨둔 데이터를 뜻하며, '로깅(Logging)'이란 시스템의 행위와 이벤트, 상태 등을 추적하기 위해 로그를 생성하고 저장하는 것을 이야기합니다.

 

개발자는 로깅을 통해 사용자의 행동에 대한 통계 및 분석을 확인할 수 있으며, 예상치 못한 문제가 발생했을 때 당시 정보를 파악할 수 있는 등, 여러 방면으로 기록된 로그를 활용할 수 있는데요.

하지만 로그를 남기는 것에도 리소스적인 비용이 발생하기 때문에 무조건 로그를 많이 남기는 것보다는 운영되는 시스템에 맞게 적절한 로그만을 남기는 것이 좋습니다.

 

 


spring boot의 로깅

spring-boot-starter-logging

스프링 부트에서는 spring-boot-starter-web 의존성 내부적으로 spring-boot-starter-logging을 포함하고 있으며, spring-boot-starter-logging에는 'Slf4j(Simple Logging Facade for Java)'와 그 구현체인 'Logback'이 포함되어 있습니다.

때문에 spring-boot-starter-web을 사용한다면 로깅을 위한 의존성을 따로 추가하지 않아도 되는 것인데요.

 

***

Slf4j는 Logback 등의 로깅 프레임워크에 대한 추상화(인터페이스) 역할을 하는 라이브러리입니다.

퍼사드 패턴(Facade Pattern)을 통해 구현체의 종류와 상관없이 일관된 로깅을 할 수 있도록 지원하며, 때문에 구현체가 변경되더라도 최소한의 수정으로 교체가 가능합니다.

(Slf4j는 compile 시에 실제 로깅 구현체와 바인딩됩니다.)

 

 


logback-spring.xml 파일을 통한 로깅 설정

logback-spring.xml 파일은 로그성 데이터에 대한 상세 설정이 필요할 때 사용되는 파일인데요. 스프링 부트는 src/main/resources 경로에 있는 해당 파일을 참조해서 로그에 대한 설정을 하게 됩니다.

 

이때, 스프링 부트에서 로깅 관련 설정을 읽어 들이는 순서가 중요한 부분인데요.

 

1. classpath(resources 디렉터리 아래)에 logback-spring.xml 파일이 있으면 해당 파일의 설정을 읽습니다.

2. logback-spring.xml 파일이 없는 경우 .properties(또는 .yml) 파일의 설정을 읽습니다.

3. logback-spring.xml 파일과 .properties(또는 .yml) 파일이 동시에 있을 경우 .properties(또는 .yml) 파일의 설정이 적용된 후 logback-spring.xml 파일의 설정이 적용됩니다.

 

 

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 60초마다 설정 파일의 변경을 확인하여 변경시 갱신 -->
<configuration scan="true" scanPeriod="60 seconds">
    <!-- 로그 패턴에 색상 적용 %clr(pattern){color} https://logback.qos.ch/manual/layouts.html#coloring -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />

    <!-- springProfile 태그를 사용하여 profile 별 property 값 설정 -->
    <springProfile name="local">
        <!-- local log file path -->
        <property name="LOG_PATH" value="/Users/username/Desktop/log"/>
    </springProfile>
    <springProfile name="dev">
        <!-- dev log file path -->
        <property name="LOG_PATH" value="/home/instance/log"/>
    </springProfile>

    <!-- Environment 내의 프로퍼티들을 개별적으로 설정 -->
    <springProperty scope="context" name="LOG_LEVEL" source="logging.level.root"/>

    <!-- log file name -->
    <property name="LOG_FILE_NAME" value="log"/>
    <!-- err log file name -->
    <property name="ERR_LOG_FILE_NAME" value="err_log"/>
    <!-- console log pattern -->
    <property name="CONSOLE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}:%-3relative]  %clr(%-5level) %clr(${PID:-}){magenta} %clr(---){faint} %clr([%15.15thread]){faint} %clr(%-40.40logger{36}){cyan} %clr(:){faint} %msg%n"/>
    <!-- file log pattern -->
    <property name="FILE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} : %msg%n"/>

    <!-- Console Appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- File Appender -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 파일경로 설정 -->
        <file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>

        <!-- 출력패턴 설정 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>

        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz .zip 등을 넣으면 자동 일자별 로그 파일 압축 -->
            <fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 kb, mb, gb -->
                <maxFileSize>10KB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그 파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거 -->
            <maxHistory>30</maxHistory>
            <!-- 전체 파일 크기를 제어하며, 전체 크기 제한을 조과하면 가장 오래된 파일을 삭제 -->
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!-- 에러의 경우 파일에 로그 처리 -->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${ERR_LOG_FILE_NAME}.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>

        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz .zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOG_PATH}/${ERR_LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 kb, mb, gb -->
                <maxFileSize>10KB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그 파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- root 레벨 설정 -->
    <root level="${LOG_LEVEL}">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="ERROR"/>
    </root>
</configuration>

(logback-spring.xml 설정 파일)

 

logback 구성 파일의 기본적인 구조는 크게 <appender>, <logger>, <root>로 나뉘는데요.

 

'Appender'는 로그의 형태를 설정하는 것으로 로그 메시지를 콘솔에 출력할 것인지, 파일에 출력할 것인지에 대한 출력 대상과 함께 세부적인 설정이 담기게 됩니다.

'Logger'는 <appender-ref> 요소를 포함할 수 있으며, <appender-ref>를 통해 참조된 Appender를 해당 로거에 추가하는 방식입니다.

Logger가 특정 역영에 대한 로깅을 설정하는 것이라면 'Root'는 전체 영역에 대한 로깅 설정을 하는 것으로 Logger의 상위에 있다고 볼 수 있습니다.

(Logger는 여러 개를 가질 수 있지만 Root는 최대 하나만 가질 수 있습니다.)

 

 

<logger name="com.example.logging" level="INFO">
    <appender-ref ref="CONSOLE"/>
</logger>

<!-- root 레벨 설정 -->
<root level="${LOG_LEVEL}">
    <appender-ref ref="CONSOLE"/>
</root>

***

만약 다음과 같이 logger와 root를 설정한 경우 com.example.logging 패키지 하위에 대한 로깅은 logger로 인해 발생하는 것과 root로 인해 발생하는 것 두 번이 중복으로 출력되게 됩니다.

 

 

이어지는 내용을 통해 나머지 부분에 세부적으로 적용된 옵션에 대해서 자세하게 살펴보도록 하겠습니다.

 

 


logback 설정 세부적인 내용

1. Appender

Logback은 로그 이벤트를 쓰는 작업을 Appender에게 위임하는데요.

Appender는 로그 메시지가 출력될 대상을 결정하는 요소이며 ConsoleAppender, FileAppender, RollingFileAppender 등이 있습니다.

(로깅을 메일로 남기는 SMTPAppender, 데이터베이스에 남기는 DBAppender 등)

 

- encoder

<encoder>는 Appender에서 사용되는 요소로 로그 메시지를 사용자가 지정한 형식으로 변환하는 역할을 담당하는 요소입니다.

FileAppender 및 FileAppender의 하위 Appender에서는 layout이 아닌 encoder가 사용되기 때문에 전체적으로 layout 보다는 encoder가 많이 사용되고 있습니다.

 

- pattern

<pattern> 요소에 사용되는 옵션은 아래와 같습니다.

 

패턴 내용
%logger 패키지를 포함한 클래스 정보를 출력
%logger{0} 패키지를 제외한 클래스 이름만 출력
%logger{length} Logger name을 축약할 수 있으며, length는 최대 자리 수
%-5level 로그 레벨, -5는 출력의 고정폭 값(5글자), 로깅 레벨이 Info인 경우 빈칸을 하나 추가
${PID:-} 프로세스 아이디
%d 로그 기록 시간 출력
%d{yyyy-MM-dd HH:mm:ss:sss} %d는 date를 의미하며 중괄호에 들어간 문자열은 dateformat을 의미
%-4relative %relative는 초 아래 단위 시간(ms)를 나타내며, -4는 4칸의 출력폼을 고정으로 출력하는 것을 의미
%p 로깅 레벨 출력
%F 로깅이 발생한 프로그램 파일명 출력
%M 로깅이 발생한 메소드명 출력
%L 로깅이 발생한 호출지의 라인
%line 로깅이 발생한 호출지의 라인
%thread 현재 Thread 명
%t 로깅이 발생한 Thread 명
%c 로깅이 발생한 카테고리
%C 로깅이 발생한 클래스명(%C{2}는 package.class가 출력됨)
%m 로그 메세지
%msg 로그 메세지
%n 줄바꿈(new line)
%r 애플리케이션 시작 이후부터 로깅이 발생한 시점까지의 시간(ms)

 

 

1-1. ConsoleAppender

OutputStream을 통해 로그를 콘솔에 출력하는 어펜더입니다.

 

 

1-2. FileAppender

로그를 파일에 출력하는 어펜더입니다.

로그 파일에 대한 옵션을 지정할 수 있으며, 파일을 분리할 수 있습니다.

 

 

1-3. RollingFileAppender

FileAppender를 확장한 어펜더입니다.

로깅 중 특정 조건이 충족되었을 때 로깅 대상을 다른 파일로 변경하는 rollover 기능이 주요 기능이며, RollingFileAppender를 사용하기 위해서는 RollingPolicyTriggeringPolicy 두 하위 요소가 필수로 설정되어야 합니다.

 

 

1-3-1. RollingPolicy

rollover에 필요한 action을 정의합니다. TimeBasedPolicy와 SizeAndTimeBasedRollingPolicy 등이 있습니다.

 

- TimeBasedRollingPolicy

가장 많이 사용하는 롤링 정책으로 일별 또는 월별과 같이 시간을 기준으로 rollover 정책을 설정합니다.

롤오버와 해당 롤오버의 트리거에 대한 책임을 담당하며 RollingPolicy와 TriggeringPolicy 인터페이스를 모두 구현합니다.

 

- SizeAndTimeBasedRollingPolicy

시간을 기준으로 rollover 정책을 설정하는 것과 더불어 파일의 크기를 제한할 수 있습니다.

 

- fileNamePattern

<fileNamePattern> 옵션을 통해 아카이브 될 로그 파일의 패턴을 정의할 수 있으며, default(%d)는 yyyy-mm-dd로 매일 자정에 새로운 로그 파일로 rollover 합니다.

 

 

1-3-2. TriggeringPolicy

rollover가 발생하는지의 여부와 발생할 정확한 시점을 정의합니다.

(RollingPolicy는 what에 대한 책임을 가지고 있고 TriggeringPolicy는 when에 대한 책임을 가지고 있다고 볼 수 있습니다.)

 

- SizeBasedTriggeringPolicy

현재 로그 파일의 크기를 확인하여 지정된 크기보다 커지는 경우 Appender에 신호를 보내 rollover를 수행합니다.

옵션으로는 maxFileSize 매개변수 하나만 허용되며 default 값은 10MB입니다.

(사용할 수 있는 용량 단위는 KB, MB, GB입니다.)

 

- SizeAndTimeBasedFNATP

SizeBasedTriggeringPolicy의 maxFileSize 옵션 외에 로그 파일을 보관할 보관 주기를 설정하는 maxHistory 옵션과 전체 파일 크기를 제한하는 totalSizeCap 옵션을 적용할 수 있습니다.

 

maxHistory의 경우 rollover 기준이 일간일 경우 maxHistory 값은 일로 적용되며, 기준이 월간일 경우 월로 적용됩니다.

totalSizeCap에 지정된 전체 파일 크기에 대한 제한이 초과되는 경우 가장 오래된 파일을 삭제합니다.

 

 


적용 중 발생했던 문제

1. log.config.path_IS_UNDEFIND

로그를 저장할 경로인 LOG_PATH 값을 .properties(또는 .yml)을 통해 불러오려고 했을 때, 다음과 같이 지정한 경로에 로그가 생성된 것이 아니라 log.config.path_IS_UNDEFIND라는 디렉터리에 로그가 생성되는 문제가 발생했었습니다.

 

이는 앞서 이야기한 것처럼 spring boot에서 .properties 보다 logback-spring.xml 파일을 먼저 읽어 들이기 때문에 발생하는 문제인데요. 해당 문제는 위 파일에서와 같이 외부 설정 파일에서 LOG_PATH를 불러오지 않고 springProfile에 따른 경로를 직접 지정해 줌으로써 해결하였습니다.

***
(+ 추가) 해당 문제에 대해서는 <property> 요소가 아닌 <springProperty> 요소를 사용하여 해결할 수 있는 것으로 확인되었습니다.

 

 

2. 콘솔에 출력되는 로그의 색상이 바뀌지 않는 문제

%clr(pattern){color} 형식을 통해 출력되는 로그의 색상을 변경할 수 있는 것으로 알고 있는데요.

%clr(%-5level){magenta}와 같이 지정했을 때는 콘솔에 출력되는 로그에 대한 색상이 변경되지 않고 %magenta(%-5level)와 같이 직접 색상을 지정했을 때만 색상이 출력되는 것을 확인했습니다.

 

 


스프링 부트에 기본적으로 적용되고 있는 로깅 설정

<include resource="org/springframework/boot/logging/logback/defaults.xml" />

(base.xml 파일 중 일부)

 

org/springframework/boot/logging/logback 디렉터리의 base.xml 파일을 살펴보면 defaults.xml 파일을 include 하고 있는 것을 볼 수 있는데요.

defaults.xml 파일을 통해 spring boot에서 기본적으로 적용되는 CONSOLE_LOG_PATTERN, FILE_LOG_PATTERN 등의 property와 logger에 대한 정보를 확인할 수 있습니다.

 

 

<!-- use Spring default values -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN}"/>
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN}"/>

활용 방안으로는 패턴을 직접 설정하지 않고 defaults.xml 파일을 include 하여 다음과 같이 기본 설정의 LOG_PATTERN을 그대로 사용할 수도 있습니다.

 

 

 

 

< 참고 자료 >

https://goddaehee.tistory.com/206
https://loosie.tistory.com/829
https://lovethefeel.tistory.com/88

 

 

< 공식 문서 >

https://logback.qos.ch/manual/configuration.html

반응형