[Spring Boot] Logback, MDC 적용

AIR·2024년 12월 7일

로깅 라이브러리


스프링 부트 로깅 라이브러리는 기본으로 다음 로깅 라이브러리를 사용한다

  • SLF4J
  • Logback

로그 라이브러리는 Logback, Log4J 등등 수 많은 라이브러리가 있는데, 그것을 통합해서 인터페이스로 제공하는 것이 SLF4J 라이브러리다. SLF4J는 인터페이스이고, 그 구현체로 Logback 같은 로그 라이브러리를 선택하면 된다. 실무에서는 스프링 부트가 기본으로 제공하는 Logback을 대부분 사용한다.


로그 사용 시 장점


  • 쓰레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.
  • 로그 레벨에 따라 로그를 상황에 맞게 조절할 수 있다.
  • 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있다. 특히 파일로 남길 때는 일별, 특정 용량에 따라 로그를 분할하는 것도 가능하다.
  • 성능도 일반 System.out보다 좋다.

Logback


로그 레벨

Logback은 메시지의 중요도에 따라 로그 레벨을 설정한다.

  • TRACE: 가장 낮은 상세 로그, 주로 디버깅 용도.
  • DEBUG: 디버깅에 필요한 정보.
  • INFO: 일반적인 정보 메시지.
  • WARN: 잠재적인 문제를 나타내는 경고 메시지.
  • ERROR: 치명적인 문제를 나타내는 에러 메시지.

Appender

Logback은 로그를 다양한 출력 대상으로 보낼 수 있는 Appender를 제공한다.

  • ConsoleAppender: 로그를 콘솔에 출력.
  • FileAppender: 로그를 파일에 기록.
  • RollingFileAppender: 일정 조건에 따라 로그 파일을 순환하여 기록.
  • SocketAppender: 로그를 네트워크 소켓으로 전송.

Layout

로그 메시지의 형식을 정의하는 기능으로, 일반적으로 PatternLayout이 사용된다.

  • %d: 날짜와 시간.
  • %thread: 실행 중인 스레드 이름.
  • %level: 로그 레벨 (INFO, WARN 등).
  • %logger: 로거 이름.
  • %msg: 로그 메시지.
  • %n: 개행 문자.

설정 파일

Logback은 설정 파일로 애플리케이션의 로깅 동작을 정의하며, 설정 파일은 logback.xml 또는 logback-spring.xml 형식으로 작성한다.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <!-- %d{}: 로그 발생 시각, %thread: 로그가 실행된 스레드 이름, %level: 로그 레벨, %logger{}: 로그를 출력한 클래스 이름 -->
    <property name="CONSOLE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] %magenta([%thread]) %highlight([%-3level]) %logger{5} - %msg %n" />

    <!-- Console appender(로그를 콘솔에 출력) 설정 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>${CONSOLE_PATTERN}</Pattern>
        </encoder>
    </appender>

    <!-- name: 로거 이름, level: 로깅 레벨, additivity: 부모 로거에 로그를 전달할 지 여부 -->
    <logger name="jdbc" level="OFF" additivity="false">
        <!-- <appender-ref>: 이 로거에서 사용할 appender를 참조. -->
        <appender-ref ref="STDOUT"/>
    </logger>
    <logger name="jdbc.sqlonly" level="OFF" additivity="false" >
        <appender-ref ref="STDOUT"/>
    </logger>
    <logger name="jdbc.sqltiming" level="OFF" additivity="false" >
        <appender-ref ref="STDOUT"/>
    </logger>
    <logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>
    <logger name="com.study.controller" level="DEBUG" additivity="false" >
        <appender-ref ref="STDOUT"/>
    </logger>
    <logger name="com.study.service" level="DEBUG" additivity="false" >
        <appender-ref ref="STDOUT"/>
    </logger>
    <logger name="com.study.domain" level="DEBUG" additivity="false" >
        <appender-ref ref="STDOUT"/>
    </logger>
  
    <!-- 모든 로거의 최상위 부모 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

MDC


MDC는 ThreadLocal 기반으로 작동하며, 각 스레드별로 고유한 컨텍스트 정보를 저장한다. MDC에 저장된 값은 로그 패턴에 자동으로 포함된다. (여기서 ThreadLocal은 해당 스레드만 접근할 수 있는 특별한 저장소)

Filter를 사용하여 HTTP 요청마다 MDC에 데이터를 추가해본다.

@Component
public class MDCFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
		//임의의 id 생성
        String requestId = UUID.randomUUID().toString();
        MDC.put("requestId", requestId);  //MDC에 저장

        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();  //요청 처리 후 MDC 정리
        }
    }
}

logback-spring.xml에 MDC에서 requestId 키에 해당하는 값을 로그에 포함시킨다.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <!-- %d{}: 로그 발생 시각, %thread: 로그가 실행된 스레드 이름, %level: 로그 레벨, %logger{}: 로그를 출력한 클래스 이름 -->
    <property name="CONSOLE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] %magenta([%thread]) %highlight([%-3level]) %logger{5} - %msg %n" />

    ...

</configuration>

로그 테스트를 진행하기 위해 테스트 컨트롤러를 생성한다.

@RestController
public class TestController {

    private static final Logger log = LoggerFactory.getLogger(TestController.class);

    @GetMapping("/mdc-test")
    public String mdcTest() {
        log.info("MDC test");
        return "MDC test";
    }
}

이제 /mdc-test 경로로 GET 요청을 보내보면 다음과 같이 requestId 값이 포함된 로그가 출력된다.

2024-12-08 03:40:56.794 [fee410db-ed01-4de4-b290-3ce1e9dc712f] [http-nio-8080-exec-3] [INFO] c.s.c.TestController - MDC test 
profile
백엔드

0개의 댓글