6-keem
Gallery
About
© Powered by 6-keem

Logging with SLF4J and Logback

Backend
2025년 04월 15일
11분

Spring Framework

Series bookmark
  1. Spring 개요
  2. Spring 의존성 주입
  3. Spring MVC 패턴
  4. Spring MVC Web Form
  5. Spring JPA (Java Persistence API)
  6. Spring Entity Mapping
  7. Logging with SLF4J and Logback
On this page
  • 들어가며
  • Logging Framework
  • SLF4J (Simple Logging Facade for Java)
  • Logback
  • 1. Logger
  • 2. Appenders
  • 3. Logback Configuration

이전 포스트
Spring Entity Mapping
다음 글이 없습니다
thumbnail.png
교과목 내용을 정리하기 위한 글입니다. 틀린 내용이 있을 수 있으니 참고하세요

들어가며

왜 간단하게 System.out.print()으로 로그를 출력하지 않을까?

유연성을 위해서

  • 선택 가능한 우선순위(DEBUG, INFO...) 수준 이상의 출력 메시지를 표시할 수 있음
  • 모든 모듈이나 특정 모듈/클래스에 대해서만 메시지를 출력할 수 있음
  • 이러한 메시지들의 형식을 어떻게 지정할지 제어할 수 있음
  • 어디로 보낼지 결정할 수 있음

Logging Framework

  • 네이티브인 java.util.logging은 잘 사용하지 않음
  • Log4J - 몇 년간 사실상 표준(de facto)이었음
  • Logback - Log4J의 개발자가 만든 후속작으로 현재 많은 프로젝트에서 사용됨
  • SLF4J (Simple Logging Facade for Java) - Facade 패턴을 사용하는 인터페이스로 Log4J, Logback 등 실제 로깅 프레임워크의 공통된 인터페이스 역할

Facade Pattern (퍼사드 패턴)

Facade PatternFacade Pattern

  • 클라이언트는 퍼사드(Facade)에 요청을 보내는 방식으로 서브시스템과 소통하며, 퍼사드는 해당 요청을 적절한 서브시스템 객체로 전달함
  • 실제 작업은 서브시스템 객체들이 수행하지만, 퍼사드는 자신의 인터페이스를 서브시스템 인터페이스로 변환하기 위한 작업을 수행해야 할 수 있음
  • 클라이언트가 서브 시스템에 직접 접근하지 않아도 됨

※ 캡슐화(정보 은닉)와 목적이 다름

SLF4J (Simple Logging Facade for Java)

SLF4JSLF4J

  • java.util.logging, Log4j, Logback 등 다양한 로깅 프레임워크에 대해 간단한 퍼사드(혹은 추상화 레이어) 역할
  • slf4j-api-2.0.12.jar 하나를 의존성에 추가해야 함
  • 클래스패스에 로깅 프레임워크와 연결(binding)되는 구현체가 없다면, SLF4J는 아무 동작도 하지 않는(no-operation) 상태로 동작

Logback

log4j의 후속작으로 더 빠르고 가벼움

LogbackLogback

logback-classic 모듈을 사용하려면, 클래스패스에 다음 JAR 파일들이 반드시 포함되어야 한다.

  1. slf4j-api.jar
  2. logback-core.jar
  3. logback-classic.jar

※ 스프링부트에서 spring-boot-starter-logging은 로킹 프레임 워크를 포함함

package kr.ac.hansung.cse;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class HelloWorld { 
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger("kr.ac.hansung.cse.HelloWorld");
    logger.debug("Hello world.");
  }
}
20:49:07.962 [main] DEBUG kr.ac.hansung.cse.HelloWorld - Hello world.
  • 위 예제는 logback 관련 클래스는 전혀 사용하지 않았다는 점에 주목
  • 오직 SLF4J의 클래스만 import 하면 됨
  • 따라서, 코드는 SLF4J API만 사용하고, Logback이 존재한다는 사실을 몰라도 됨

1. Logger

Logger는 이름을 가진 엔티티(객체)로 이름을 대소문자를 구분하며 계층적인 이름 규칙을 따름

  • kr.ac.hansung 이름의 Logger는 kr.ac.hansung.cse Logger의 부모
  • root logger는 계층 구조의 최상위

Logging Level

Logger에 레벨을 지정할 수 있다

  • 로그 레벨은 TRACE → DEBUG → INFO → WARN → ERROR 순으로 **중요도(심각도)**가 점점 높아짐
  • 명시적으로 레벨이 지정되어 있지 않다면 가장 가까운 상위 로거의 레벨을 상속 받음
  • root logger는 항상 레벨이 지정되어 있으며, 기본값은 INFO임
예시
Logger nameAssigned levelEffective level
rootDEBUGDEBUG
XINFOINFO
X.YnoneINFO
X.Y.ZERRORERROR

Printing method

  • 어떤 출력 메서드를 사용하는지에 따라 로그 요청의 레벨이 결정됨
    → logger.warn("hello");는 WARN 레벨의 로그 요청

LogbackLogback

  • 로그 요청의 레벨이 해당 로거의 유효 레벨(effective level)보다 크거나 같으면, 그 로그 요청은 **활성화(enabled)**되었다고 한다.
  • 그렇지 않으면, 즉 로깅 요청의 레벨이 더 낮으면, 그 로그 요청은 **비활성화(disabled)**되었다고 한다.

로그 요청 허용 여부 (YES = 출력됨, NO = 무시됨)

Logging Request p ↓ \ Logger Effective Level q →TRACEDEBUGINFOWARNERROROFF
TRACEYESNONONONONO
DEBUGYESYESNONONONO
INFOYESYESYESNONONO
WARNYESYESYESYESNONO
ERRORYESYESYESYESYESNO
package kr.ac.hansung.example;
 
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
 
public class LoggerLevelExample {
    public static void main(String[] args) {
        // "kr.ac.hansung.example" 이름의 로거 생성
        Logger logger = (Logger) LoggerFactory.getLogger("kr.ac.hansung.example");
 
        // 로거 레벨을 INFO로 설정
        logger.setLevel(Level.INFO);
 
        // 출력됨: WARN >= INFO
        logger.warn("⚠️ Low fuel level.");
 
        // 출력 안 됨: DEBUG < INFO
        logger.debug("🔍 Starting search for nearest gas station.");
 
        // 출력됨: ERROR >= INFO
        logger.error("❗ Out of fuel! Engine stopped.");
 
        // 출력 안 됨: TRACE < INFO
        logger.trace("Trace log for diagnostic purposes.");
    }
}
[main] WARN  kr.ac.hansung.example - ⚠️ Low fuel level.
[main] ERROR kr.ac.hansung.example - ❗ Out of fuel! Engine stopped.

2. Appenders

  1. 로그 요청은 하나 이상의 출력 대상(destination)으로 출력될 수 있음
  2. 각 출력 대상은 appender(출력기)로 표현되며, 다음과 같은 종류 가능
    → console, files (plain text, HTML...), remote socket servers, databases

AppendersAppenders

특정 로거의 활성화된 로그 요청은, 해당 로거에 설정된 모든 appender뿐 아니라 상위 계층의 appender들에게도 전달된다.

  • 즉, appender는 로거 계층에서 누적적으로 상속된다.이를 Appender Additivity이라고 부른다.
  • 예를 들어, root 로거에 console appender가 추가되어 있다면 모든 활성 로그는 최소한 콘솔에 출력된다.
  • 여기에 추가로 어떤 로거 L에 file appender를 설정하면, 및 그 하위 로거의 로그는 파일과 콘솔에 모두 출력된다.
  • 하지만 이러한 기본 동작은 설정을 통해 변경할 수 있다. 즉, additivity 플래그를 false로 설정하면 상위 appender 상속을 막을 수 있다.
Logger NameAttached AppendersAdditivity FlagOutput TargetsComment
rootA1not applicableA1Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it.
xA-x1, A-x2trueA1, A-x1, A-x2Appenders of "x" and of root.
x.ynonetrueA1, A-x1, A-x2Appenders of "x" and of root.
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1Appenders of "x.y.z", "x" and of root.
securityA-secfalseA-secNo appender accumulation since the additivity flag is set to false. Only appender A-sec will be used.
security.accessnonetrueA-secOnly appenders of "security" because the additivity flag in "security" is set to false.

1. ConsoleAppender

<configuration> 
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
       <encoder> 
            <pattern> %d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n </pattern> 
       </encoder> 
   </appender> 
 
   <root level="info"> 
       <appender-ref ref="STDOUT" /> 
   </root> 
 
   <logger name="myPackage.Heater" level="warn"/> 
</configuration>
패턴의미
%d{pattern}로그 발생 시각
%thread스레드 이름
%-5level로그 레벨 (5자 너비로 좌측 정렬)
%logger{length}로거 이름 (최대 길이 지정 가능)
%msg로그 메시지

2. FileAppender

<appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
    <append>true</append> <!-- default --> 
    <encoder> 
       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n</pattern> 
    </encoder> 
    <file>test.dat</file> 
</appender>
 
<root level="info"> 
   <appender-ref ref="STDOUT" /> 
   <appender-ref ref="FILE" /> 
</root>

로그는 콘솔과 파일(test.dat)에 동시에 출력

3. RollingFileAppender

  • 롤오버 파일(Rollover files): 로그를 하나의 파일에 기록하다가, 특정 조건이 되면 새로운 출력 파일로 전환하는 방식
  • 이러한 조건은 롤링 정책(rolling policies)으로 지정하며, 가장 많이 사용되는 것은 TimeBasedRollingPolicy로,
    → 매달, 매주, 매일, 매시간마다 새 로그 파일로 전환됩니다.
<appender name="log-file" class="ch.qos.logback.core.rolling.RollingFileAppender"> 
   <file>my-application.log</file>
 
   <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 
       <!-- rotate every day for log collection and archiving --> 
       <fileNamePattern>my-application.%d{yyyyMMdd}.log</fileNamePattern> 
   </rollingPolicy> 
</appender>

3. Logback Configuration

classpath에 위치한 logback.xml 파일

Logback ConfigurationLogback Configuration

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>TRACE</level>
        </filter>
    </appender>
 
    <appender name="DAILY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/rest-demo.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/rest-demo.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
 
    <logger name="kr.ac.hansung" level="DEBUG" additivity="false">
        <appender-ref ref="DAILY_FILE"/>
        <appender-ref ref="CONSOLE"/>
    </logger>
 
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>