Logback 으로 로그 작성하기

Logback

스프링에서 로깅을 할때는 대표적으로 Slf4j 를 사용합니다.

근데 Slf4j 가 무엇이고, Logback 이 무엇인지 모른채 사용하는 것은 수정이 필요할 때 문제가 될 수 있습니다.

그렇기에 조금은 알아둘 필요가 있어 기본적인 것들을 정리해볼려고 합니다.



Slf4j 와 Logback

Slf4j (Simple Logging Facade for Java)는 다양한 로깅 프레임 워크에 대한 일관된 API 를 제공하는 추상화 라이브러리입니다.


Logback 은 자바를 위한 로깅 프레임워크로, Slf4j 의 구현체입니다.

Logback 에서는 주요한 객체들이 있는데 Appender, Encoder, RollingPolicy 등이 있습니다.




Appender

Appender 는 로그 메시지를 특정 출력 대상으로 보내는 역할을 하는 구성 요소입니다.

즉, 콘솔이나 파일로 메세지를 남겨주는 객체입니다.


다이아그램으로 보면 다음과 같습니다.

LogbackDiagram


image

함수중에 doAppend 라는 함수가 존재하는데 이 함수의 역할은 로그 이벤트를 받아 처리하고 출력하는 역할입니다.


그다음으로는 비동기 Appender 가 있고, 상속 받아 만들어진 OutputStreamAppender 가 있습니다.

image

  • Encoder:
    • 로그 이벤트를 원하는 형식으로 바꿔주는 역할
  • OutputStream:
    • 인코딩된 로그 메세지를 기록할 출력 스트림

나중에 설정할때 필요하기때문에 encoder 부분을 기억해주세요.


Appender 의 구현체는 여러개가 있지만 가장 많이 사용하는 두가지를 알아볼려고 합니다.

  • ConsoleAppender
  • RollingFileAppender

두개는 이름 그대로 콘솔에 출력해주는 Appender 와 파일에 로깅해주는 Appender 입니다.

image

ConsoleAppender 는 말그대로 콘솔에 찍어주는 역할이기 때문에 크게 찾아볼 부분은 없습니다.


FileAppener 는 파일에 로깅하는 Appender 입니다.

  • fileName:
    • 로그를 남길 file 의 이름입니다. 해당 파일이 없으면 새로 생성합니다.
  • append:
    • ture 이면 파일이 존재하는 경우 이어서 쓰고, false 이면 기존 파일을 덮어씌웁니다.


그 다음으로 살펴볼건 RollingFileAppender 입니다. 가장 많이 사용하는 Appender 입니다.

만약 하나의 파일을 계속 사용하다보면 로그를 찾기도 관리하기도 어려워 지는데 특정 조건에 따라 관리하면 로그 파일의 크기도 작고 검색하기도 편해집니다.

이러한 이유로 RollingFileAppender 를 사용합니다.

  • CurrentlyActiveFile:
    • 현재 활성화 되어 있는 파일을 가르킵니다. 메세지가 추가될 경우 이 파일에 작성합니다.
  • TriggeringPolicy:
    • 롤링 파일을 롤링할 시점을 결정하는 정책입니다. 파일 크기나 시간이 특정 조건에 도달했을때 롤링은 트리거 됩니다.
    • currentlyActiveFile 은 새로운 파일로 변경되고, 기존 파일은 fileNamePattern 에 따라 이름이 변경됩니다.
  • RollingPolicy:
    • 롤링된 로그 파일의 이름과 보관 방식을 정의합니다. 또한 오래된 파일을 어떻게 처리할지도 설정할 수 있습니다.


Logback 을 사용하기전 Appender 를 먼저 알아본 이유는 logback-spring.xml 을 작성할 때 우리가 하는 설정이 Appender 에 대한 설정이기 때문입니다.

개념을 먼저 배웠기때문에 사용할때는 조금더 이해하고 사용할 수 있다고 생각합니다.




Logback 사용하기

Gradle 과 XML 설정하기

implementation 'org.slf4j:slf4j-api:2.0.13'
implementation 'ch.qos.logback:logback-classic:1.5.6'
implementation 'ch.qos.logback:logback-core:1.5.6'

먼저 Logback 을 사용하기 위해서는 gradle 에 추가 해야합니다.

logback-classic: Slf4j API 의 구현체로 주요 모듈입니다. 메세지를 출력하는 역할입니다.

logback-core: Logback 의 핵심 모듈로 모든 Logback 구현의 기반이 되는 모듈입니다. 로그 메세지를 파일 또는 콘솔에 출력할 수 있도록 지원하는 핵심 기능입니다.

Appender (로그 출력 대상), Encoder (로그 메세지 형식화), RollingPolicy (로그 파일 정책) 등이 제공됩니다.


<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <property name="LOG_PATH" value="./logs"/>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${LOG_PATH}/logback.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/application-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP:-1GB}</totalSizeCap> <!-- 모든 롤링된 로그 파일의 총 크기 -->
            <cleanHistoryOnStart>${LOG_FILE_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
            <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize>
        </rollingPolicy>
    </appender>

    <springProfile name="local">
        <include resource="org/springframework/boot/logging/logback/base.xml"/>
    </springProfile>

    <springProfile name="prod">
        <property name="LOG_PATH" value="/home/ec2-user/project/logs"/>

        <root level="ERROR">
            <appender-ref ref="FILE"/>
        </root>
    </springProfile>
</configuration>

src/main/resourcelogback-spring.xml 을 생성해서 위와 같이 붙혀넣습니다.

여기서 태그들이 보이는데 다음과 같은 기능을 합니다.

  • include:
    • logback 라이브러리나 미리 만들어놓은 xml 을 해당 코드에 가져와 사용하는 것입니다.
  • property:
    • 변수명을 설정하는 태그입니다. 예를 들어 name 과 value 로 설정하고, 사용시 ${변수명}로 사용합니다.
    • 변수명:-10MB 이렇게 사용되는 부분이 있는데 이거는 외부에 변수명이 저장되어 있을 경우 외부 설정을 사용하고 아닐 경우 :- 뒤에 있는 10MB 를 사용하겠다는 의미입니다.
  • appender:
    • 위에서 설명한 appender 에 대한 설정입니다. 어떤 appender 를 사용할 것이고, 그 appender 의 디테일한 설정까지 하는 부분입니다.
  • springProfile:
    • Profile 별로 전략을 다르게 가져갈 수 있도록 하는 태그입니다.
    • 내부에도 property 가 있는데 외부에 선언된 변수를 변경합니다.
  • root:
    • 설정한 level ( INFO, WARN, ERROR, FATAL ) 의 이상만 받도록 하는 설정입니다. 예를 들어 WARN 이면 INFO 를 제외한 WARN, ERROR, FATAL 부분의 로깅만 받습니다.
  • appender-ref:
    • appender 라는 객체를 사용하겠다는 의미이고 ref 속성으로 어떤 이름을 불러와 사용하겠다는 의미입니다.
    • 예를 들어 appender 태그의 name 을 AAAA 로 했으면 ref 속성으로 AAAA 를 사용하면 해당 appender 를 사용하겠다는 의미입니다.


위에서는 태그에 대한 설정이었다면 다음은 RollingFileAppender 의 디테일한 설정을 알아보겠습니다.

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <encoder>
        <pattern>${FILE_LOG_PATTERN}</pattern>
    </encoder>
    <file>${LOG_PATH}/logback.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/application-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxHistory>30</maxHistory>
        <totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP:-1GB}</totalSizeCap> <!-- 모든 롤링된 로그 파일의 총 크기 -->
        <cleanHistoryOnStart>${LOG_FILE_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
        <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize>
    </rollingPolicy>
</appender>
  • encoder:
    • 로그 이벤트를 원하는 형식으로 바꿔주는 역할
    • 위에서 OutputStreamAppender 에 대해서 얘기할때 Encoder 가 있었는데 그 부분입니다.
  • file:
    • 어떤 이름으로 파일에 저장할지 적는 태그입니다. 여기에 모든 로깅이 됩니다.
  • rollingPolicy:
    • 어떤 정책으로 롤링 할 것인지 설정하는 태그입니다.
  • fileNamePattern:
    • 시간 정책을 사용하는 경우 fileNamePattern 에 의해 정해집니다.
      • yyyy-MM-dd 일 경우 하루동안 로그가 쌓입니다.
      • yyyy-MM-dd_HH 일 경우 시간 단위로 로그가 쌓입니다.
    • 특정 조건에 의해 rolling 이 될때 기존의 파일이 지금 설정하는 fileNamePattern 에 의해 파일 이름이 변경되고, file 태그의 이름으로 새롭게 로그가 쌓입니다.
  • maxHistory:
    • 최대로 보관할 수 있는 파일의 갯수를 정합니다.
    • 갯수를 초과하는 오래된 순서대로 로그파일이 삭제됩니다.
    • totalSizeCap 보다 우선순위가 높습니다.
  • totalSizeCap:
    • 모든 롤링된 로그 파일의 총 크기입니다.
    • 해당 크기가 넘어가면 오래된 순서대로 로그파일이 삭제됩니다.
  • cleanHistoryOnStart:
    • true 이면 appender 가 시작할 때 기존 로그 파일들을 삭제합니다. 기본값은 false 입니다.
  • maxFileSize:
    • 로그 파일의 총 사이즈를 제한합니다. 설정한 파일 사이즈가 다 되면 롤링됩니다.

FILE_LOG_PATTERN 은 Default.xml 에 다음과 선언되어 있습니다.

<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}}"/>



스프링 코드

스프링 코드에서는 Lombok 의 @Slf4jlog.error 와 같이 사용합니다.

@RestControllerAdvice
@Slf4j
public class ExceptionControllerAdvice {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IllegalArgumentException.class)
    public ApiResponse<String> illegalExHandler(IllegalArgumentException e) {
        log.error("error message: {}", e.getMessage(), e);
        return ApiResponse.res(HttpStatus.BAD_REQUEST.value(), e.getMessage(), "error");
    }
  
  	//...생략
}

저는 에러에 대해서만 로깅을 할 예정이라 다음과 같이 사용하였습니다.




테스트하기

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
    
    <property name="LOG_PATH" value="./logs"/>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${LOG_PATH}/logback.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/application-%d{yyyy-MM-dd-HH-mm}.%i.log</fileNamePattern>
            <maxHistory>3</maxHistory>
            <totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP:-1GB}</totalSizeCap> <!-- 모든 롤링된 로그 파일의 총 크기 -->
            <cleanHistoryOnStart>${LOG_FILE_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
            <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize>
        </rollingPolicy>
    </appender>

    <springProfile name="local">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="FILE"/>
        </root>
    </springProfile>
</configuration>

여기서 봐야 할 부분은 springProfile 태그에서 name 속성을 local 로 지정한 부분입니다.

먼저 보시면 INFO 부터 CONSOLE 과 FILE 로 받겠다고 했으니 console 과 file 을 확인할 수 있습니다.

그리고 fileNamePattern 을 yyyy-MM-dd-HH-mm 로 설정했으니 1분마다 빠르게 롤링되는 것을 확인할 수 있습니다.


@Controller
@RequiredArgsConstructor
@RequestMapping("/admin")
@Slf4j
public class AdminHomeController {
    @GetMapping("")
    public String home() {
        log.info("hi");
        return "admin/home";
    }
}

그다음을 lombok의 @Slf4j 어노테이션log.info 를 사용해서 logging 을 하는 코드를 작성해줍니다.


그다음은 위 /admin 으로 요청하면 로깅이 쌓이게 되는데 다음과 같습니다.

image

콘솔창에는 성공적으로 나오는게 보입니다.


image

우리가 설정한 logs 에도 다음과 같이 정상적으로 나오는게 보입니다.

위에서 우리가 설정한 maxHistory 는 3이므로 4개가 되면 맨 마지막에 하나는 지우게 됩니다.

image

정상적으로 파일도 작성되고 롤링도 되는 것을 확인하였습니다.




프로젝트 세팅하기 시리즈

프로젝트 세팅하기




참고

https://wbluke.tistory.com/51

Leave a comment