Spring

Spring Boot 로깅의 표준 Logback과 SLF4J 알아보기

Luti 2025. 12. 30. 19:14


 
안정적이고 신뢰성 있는 시스템을 만들기 위한 수단은 여러 가지가 있습니다.
테스트, 아키텍처, 예외 핸들링 등...
그중에서도 시스템의 현재 상태를 관찰하고, 문제가 발생했을 때 빠르게 원인을 파악할 수 있게 해주는 로깅이 가장 기초적이면서도 중요하지 않을까 합니다.
그런 의미에서 스프링부트 환경에서 Logback과 SLF4J를 활용한 로깅의 개념 그리고 기본적인 구성 방식을 정리해보고자 합니다.
 
 

 

# 00. Spring Boot 로깅의 기본 구조

 
스프링 부트를 사용하고 있다면 이미 로깅은 진행되고 있습니다.

 
별도의 설정을 하지 않았음에도 INFO 레벨의 로그를 통해 프로젝트가 시작되었음을 콘솔에 출력시켜주고 있습니다.
즉, 우리가 `log.info()`를 직접 호출하기 전부터, 스프링 부트는 내부적으로 로깅을 사용해 애플리케이션의 시작, 설정 로딩, 웹 서버 초기화 과정 등을 기록하고 있었습니다.
 

By default, if you use the starters, Logback is used for logging. Appropriate Logback routing is also included to ensure that dependent libraries that use Java Util Logging, Commons Logging, Log4J, or SLF4J all work correctly. ( docs.spring.io - Spring Boot / Reference / Core Features / Logging )

 
별도의 설정 없이 로그가 찍히는 이유는 스프링 부트가 기본 로깅 스타터 ( `srping-boot-starter-logging` )를 통해 SLF4J와 Logback을 자동으로 구성하기 때문입니다.
`spring-boot-starter-logging`은 스프링부트 프로젝트 생성 시 자동으로 포함되는 `spring-boot-starter`를 이루고 있는 하나의 기능이며, `spring-boot-starter-logging`의 구성은 아래와 같습니다.

spring-boot-starter-logging
 ├─ logback-classic
 ├─ log4j-to-slf4j
 └─ jul-to-slf4j

 
정리하자면, 스프링 부트에서 발생한 로그는 내부적으로 SLF4J를 로깅 API로 사용하며, 실제 로그의 출력과 관리는 Logback이 담당하는 구조를 기본으로 채택하고 있습니다.
 
 

# 01.SLF4J와 Logback

 
기본 스프링부트 환경에서 실제 로그를 기록하고 콘솔에 출력해 주거나 파일로 저장해 주는 등의 역할을 하는 로깅 프레임워크는 Logback입니다.
Logback 뿐만 아니라 Log4j나 Java Util Logging 등 여러 로깅 프레임워크들이 있습니다.
하지만, 저희가 스프링부트 프로젝트에서 `log.info()`를 통해 로그를 찍기 위해서는 Logback이 아닌 아래와 같은 import문들을 필요로 합니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

 
그럼 이 SLF4J는 무엇일까요?
 
SLF4J는 Simple Logging Facade for Java를 의미하며 Facade라는 이름 그대로 추상화 계층입니다.
중요한 점은, SLF4J는 로그를 출력하는 엔진이 아니라, 로그를 남기기 위한 공통 인터페이스 역할을 한다는 것입니다.
때문에 SLF4J만 단독으로 있어서는 로그를 출력할 수 없으며, 반드시 이를 구현한 Logback 등의 로깅 프레임워크가 함께 존재해야 합니다.
 
덕분에, 실제 로깅 프레임워크를 어떤 것을 사용하든 간에 통일된 사용법으로 로깅 기능을 사용할 수 있으며, 중간에 로깅 프레임워크를 교체하게 되더라도 모든 로그 코드를 다 뜯어고칠 필요가 없어지게 됩니다.
 

 
 

# 02. SLF4J 사용법

 
위에서 말씀드린 이유로 저희는 코드 레벨에서 구현체가 아닌 SLF4J의 사용법만 익히면 로그를 다룰 수 있게 됩니다.
SLF4J의 사용법에 대해 알아보겠습니다.
 
기본 사용
 
SLF4J 공식 문서에서 가장 기본으로 소개하는 사용 방식이 아래와 같습니다.
 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {

  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

 
`LoggerFactory`를 통해 클래스마다 `Logger` 객체를 생성합니다.
이때 `.getLogger()`는 로거의 이름으로 String 혹은, 클래스 타입을 받습니다.

 
대부분의 경우가 클래스 방식으로 `Logger`의 이름을 지정합니다.
클래스명과 `Logger` 이름을 자동으로 일치시킴으로써 이후 로그백 설정을 통해 패키지 단위로 로그 레벨 제어가 가능해집니다.
이를 테면, A 패키지는 INFO 레벨 이상만 출력하고, B 패키지는 WARN 레벨 이상만 출력하는 등의 조작이 가능한 것이지요.
 
로그 출력 메서드 및 로그 레벨
 
SLF4J는 아래와 같은 공통 로그 레벨메서드를 제공합니다.

log.trace("trace message");
log.debug("debug message");
log.info("info message");
log.warn("warn message");
log.error("error message");

 
반복해서 설명드리지만,
구현체로 Logback을 사용하든, Log4j를 사용하든 이 코드 자체는 변경될 일이 없습니다.
 
로그 레벨의 경우 SLF4J나 Logback 등에서 공식적으로 기준을 정해놓지는 않았지만 실무에서는 보통 아래와 같은 기준으로 사용합니다.
 

  • TRACE : 메서드 진입/탈출, 루프 내부, 조건 분기 확인용
  • DEBUG : 개발 중 상태 확인, 조건 및 계산 결과나 분기 판단 근거 등
  • INFO : 시스템이 의도한 대로 동작했음을 알리는 로그, 회원가입, 결제 완료 등
  • WARN : 실패는 아니지만 방치하면 장애로 이어질 가능성 알림
  • ERROR : 요청 단위 실패

 
 

# 03. Logback 핵심 구성 요소와 설정 방법

 
SLF4J가 남긴 로그를 Logback이 실제로 어떻게 처리하고 출력하는지, 그리고 스프링 부트 환경에서 최소한으로 알아야 할 설정 요소들을 정리해 보겠습니다.
 
Logback은 `Logger`, `Appender`, `Encoder`의 핵심 구성 요소를 통해 "어디서", "어디로", "어떻게" 기록할지 결정합니다.
 

Logger

 
로그의 주체입니다. 개발자가 코드 내에서 로그 메시지를 전달하면, 설정된 로그 레벨에 따라 메시지를 `Appender`에게 전달할지를 결정합니다.
예를 들어서 특정 클래스의 로그 레벨을 INFO로 설정하면, 그보다 낮은 DEBUG나 TRACE 로그는 무시됩니다.
 

Appender

 
`Logger`로부터 로그를 위임받고 어디에 기록할지 결정하는 요소입니다.
저희는 로그를 단순히 콘솔 출력을 통해 눈으로 확인하는 것에만 그치지 않고, 파일이나 DB에도 남겨 사후에 문제를 분석하고자 하는 경우도 있습니다.
때문에 이 `Appender`를 통해 로그를 기록할 위치를 지정할 수 있습니다.
 
자주 사용되는 `Appender`의 종류는 아래와 같습니다.
 
ConsoleAppender

  • 로그를 콘솔에 출력
  • class = `"ch.qos.logback.core.ConsoleAppender"`
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

 
 
FileAppender

  • 로그를 파일에 출력
  • class = `"ch.qos.logback.core.FileAppender"`
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myapp.log</file>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

 
RollingFileAppender

  • 로그 파일을 시간 혹은 크기 기반 등으로 롤링
  • class = `"ch.qos.logback.ore.rolling.RollingFileAppender"`
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>myapp.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>myapp.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

 
AsyncAppender

  • 로깅을 비동기적으로 처리하여 성능을 향상
  • class = `"ch.qos.logback.classic.AsyncAppender"`
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
    <queueSize>512</queueSize>
    <discardingThreshold>0</discardingThreshold>
    <includeCallerData>true</includeCallerData>
</appender>

 
등 여러 가지 `Appender`들이 있고, 각 `Appender`들의 구체적인 사용 방법과 속성들은 Logback 공식 문서 - Ch4 Appenders에서 확인하실 수 있습니다.
 
 

Encoder

`Encoder`는 `Appender`가 로그를 쓰기 전, 로그를 바이트 배열로 변환하고 포맷을 입히는 역할을 합니다.
날짜, 시간, 스레드명, 메시지 등을 어떤 패턴으로 보여줄지 커스텀할 수 있습니다.
 
자주 사용되는 인코더는 아래와 같습니다.
 
PatternLayoutEncoder

  • 사람이 읽기 쉬운 텍스트 로그
  • 콘솔 로그나 단순 파일 로그에 적합
  • %d, %level, %logger, %msg 같은 패턴 조합
  • class = `"ch.qos.logback.classic.encoder.PatternLayoutEncoder"`
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>
        %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
    </pattern>
</encoder>

 
 
JSON Encoder (logstash-logack-encoder 등)

  • JSON 형태의 구조화 로그
  • 운영 환경에서 CloudWatch, ELK, Datadog 등과 함께 사용하기에 적합
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>

 
 

logback-spring.xml

위와 같은 구성 요소를 설정하는 파일로 `logback.xml`과 `logback-spring.xml`이 있습니다.
결론부터 말하자면, Spring Boot 환경에서는 `logback-spring.xml` 사용이 표준입니다.
Logback 자체는 스프링과 무관한 독립적인 로깅 프레임워크입니다.
따라서 Logback의 기본 설정 파일인 `logabck.xml`은 스프링의 기능을 사용할 수 없는데요,
반면 `logabck-spring.xml`은 스프링 부트가 Logback 설정 로딩 과정에 직접 개입하면서 제공하는 확장 기능이기 때문에 부가적인 기능을 사용할 수 있게 됩니다.
 
이를테면 아래와 같이 스프링 프로필 기능을 이용해 개발 환경과 운영 환경의 로깅 전략을 다르게 설정할 수 있습니다.

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

    <springProfile name="dev">
        <include resource="logback-dev.xml"/>
    </springProfile>

    <springProfile name="deploy">
        <include resource="logback-deploy.xml"/>
    </springProfile>

    <springProfile name="default">
        <include resource="logback-dev.xml"/>
    </springProfile>

</configuration>

 
 
실제로 제가 사용하고 있는 설정 파일을 예시로 보겠습니다.

 
deploy 환경의 설정 파일입니다.
 
기본적으로 Logback으로 수집한 로그를 CloudWatch를 통해 확인을 하고 있고, CloudWatch만으로 로그가 영구적으로 보관이 가능하며, 레벨이나 날짜 필터링이 가능하기 때문에 따로 파일로 저장하지 않고 ConsoleAppender를 사용합니다.
초기에는 json 포맷의 인코더를 사용하였으나, CloudWatch 스트림 미리 보기에서 json 형태가 가시성이 좋지 않아 `PatternLayoutEncoder`로 수정을 했고, 스크린샷과 같이 인코더의 class를 따로 지정하지 않으면 디폴트로 `PatternLayoutEncoder`가 선택됩니다.
 
또한 초기에 불필요한 로그가 지나치게 수집되는 것이 확인이 되어 기본 level을 INFO로 설정하고, hibernate 등 노이즈 로그를 발생시키는 패키지들에 대해 level을 조정했습니다.
 
 


 
끄읕