Spring Boot

Logging

헬로우월드 2023. 4. 17. 23:43

🔍 Java Logging System 완전 정복: JUL, SLF4J, Logback, Log4j까지

📌 들어가며

소프트웨어에서 로깅(logging)은 필수입니다. 디버깅, 모니터링, 감사, 보안, 성능 분석 등 모든 영역에서 로그는 시스템의 눈과 귀 역할을 합니다.

Java에는 기본 로깅 API인 java.util.logging부터, 다양한 서드파티 라이브러리인 Log4j, Logback, SLF4J까지 수많은 로깅 프레임워크가 존재합니다. 이 글에서는 다음과 같은 주제로 Java 로깅 시스템을 깊이 있게 다뤄보겠습니다:

🗂️ 목차

  1. 자바 로깅 시스템의 개요
  2. java.util.logging (JUL)
  3. SLF4J (Simple Logging Facade for Java)
  4. Log4j & Log4j2
  5. Logback
  6. 로깅 프레임워크 비교
  7. 로깅 구성 방법 및 Best Practice
  8. 실전 예제: Spring Boot에서 로깅 설정
  9. 로깅과 성능, 보안, 운영에서의 팁

1️⃣ 자바 로깅 시스템의 개요

🔸 로깅이란?

  • 정의: 애플리케이션의 실행 중 발생하는 이벤트(정보, 경고, 에러 등)를 기록하는 행위
  • 목적: 디버깅, 감사, 모니터링, 성능 분석 등
  • 로깅 수준(Level):
    • TRACEDEBUGINFOWARNERRORFATAL
    • Level이 높을수록 더 중요한 이벤트(FATAL이 가장 높은 레벨)

2️⃣ java.util.logging (JUL)

🔹 소개

JDK 1.4부터 포함된 Java 기본 로깅 프레임워크입니다.

import java.util.logging.*;

public class JulLoggerExample {
    private static final Logger logger = Logger.getLogger(JulLoggerExample.class.getName());

    public static void main(String[] args) {
        logger.info("정보 로그입니다.");
        logger.warning("경고 로그입니다.");
        logger.severe("심각한 에러 로그입니다.");
    }
}

 

🔹 구성 요소

구성 요소 설명
Logger 로깅의 핵심 클래스
Handler 로그를 어디로 출력할지 지정 (콘솔, 파일 등)
Formatter 로그 출력 형식 지정 (SimpleFormatter, XMLFormatter)
Level 로그 수준 지정 (INFO, WARNING, ...)

 

🔹 설정 파일 (logging.properties)

handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

JVM 실행 시 -Djava.util.logging.config.file=logging.properties 로 설정 가능

 

3️⃣ SLF4J: Logging Facade

🔹 SLF4J란?

Simple Logging Facade for Java

  • 직접 로깅을 하지 않고, 다른 로깅 프레임워크를 추상화한 라이브러리
  • Logback, Log4j, java.util.logging 등 어떤 구현체든 붙일 수 있음

🔹 장점

  • 로깅 API를 표준화해 다양한 환경에서 유연하게 적용 가능
  • Logger API는 매우 단순하고 직관적
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jExample {
    private static final Logger log = LoggerFactory.getLogger(Slf4jExample.class);

    public static void main(String[] args) {
        log.info("SLF4J Info");
        log.debug("SLF4J Debug");
        log.error("SLF4J Error");
    }
}

 

🔹 구현체 선택 예시

구현체 의존성 라이브러리
Logback logback-classic
Log4j log4j-slf4j-impl
JUL jul-to-slf4j

 

4️⃣ Log4j & Log4j2

🔹 Log4j

  • Apache에서 만든 가장 널리 알려진 로깅 라이브러리
  • log4j.propertieslog4j.xml로 설정
log4j.rootLogger=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

 

🔹 Log4j2의 개선점

  • 비동기 로깅 지원으로 성능 향상
  • 플러그인 기반 설정
  • Property Substitution, JSON/YAML 설정 지원
<!-- log4j2.xml 예시 -->
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" />
    </Console>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

 

5️⃣ Logback

🔹 개요

  • SLF4J의 공식 구현체
  • 성능 좋고, Spring Boot 기본 로깅 구현체
  • 설정 파일: logback.xml 또는 logback-spring.xml

🔹 Logback 구성요소

구성 요소 설명
<appender> 출력 대상 (Console, File, RollingFile 등)
<encoder> 로그 포맷
<logger> 개별 클래스 또는 패키지의 로깅 설정
<root> 전체 로거에 대한 설정
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

 

 

6️⃣ 로깅 프레임워크 비교

항목 JUL Log4j SLF4J Logback Log4j2
JDK 포함 여부 ✅ 기본 포함 ❌ 외부 추가 필요 ❌ (Facade)
구성 방식 properties properties/xml facade only xml/groovy xml/yaml/json
성능 낮음 중간 - 높음 매우 높음
비동기 지원 ✅ (Async Appender)
Spring Boot 기본 ❌ (옵션으로 교체 가능)

 

7️⃣ 로깅 구성 방법 및 Best Practices

SLF4J + Logback 조합 추천 (Spring Boot에서 디폴트)

✨ Best Practices

  1. Logger는 클래스당 하나
  2. private static final Logger log = LoggerFactory.getLogger(YourClass.class);
  3. 문자열 결합은 지양
  4. log.debug("Result: {}", result); // O log.debug("Result: " + result); // X (성능 낭비)
  5. 적절한 로그 레벨 설정
    • 개발환경에서는 DEBUG, 운영환경에서는 INFO 이상
  6. 민감한 정보는 절대 기록하지 말 것
    • 주민번호, 비밀번호, 토큰 등

 

8️⃣ 실전 예제: Spring Boot에서 로그 설정

기본 설정 (application.properties)

logging.level.root=INFO
logging.level.com.example=DEBUG
logging.file.name=app.log

 

고급 설정 (logback-spring.xml)

<configuration scan="true">
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>logs/myapp.log</file>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

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

 

 

9️⃣ 로깅 관련 실전 팁 (운영 중심)

📌 성능

  • DEBUG 레벨은 운영환경에서 비활성화
  • 비동기 로깅 적용 시 대량 로그 처리 가능 (Logback AsyncAppender, Log4j2 AsyncLogger)

📌 보안

  • 로그에 민감한 정보 마스킹 (password=****)
  • IP, 사용자 정보 등의 감사 추적용 로그는 별도 파일로 분리

📌 운영

  • 로그 롤링(Rolling) 필수
  • ELK(Stack), Prometheus + Grafana 연동 시 JSON 포맷 추천

 

📌 그런데 log4j-to-slf4j 란?

당신의 프로젝트는 SLF4J + Logback을 기반으로 로그를 통합하고 있는데,
서드파티 라이브러리가 위처럼 Log4j 2 API를 직접 사용하고 있다면?

👉 그 로그는 SLF4J나 Logback 설정을 따르지 않고, Log4j 자체 설정이나 기본 설정으로 출력됩니다.
이러면 로그가 혼란스럽고, 출력 형식이나 레벨 제어도 통일되지 않게 됩니다.

 

🎯 해결책: log4j-to-slf4j

이럴 때 log4j-to-slf4j 브릿지를 사용하면:

Log4j 2 API를 사용해 작성된 코드에서 출력한 로그가 SLF4J로 전달되고,
SLF4J의 구현체(예: Logback)가 최종적으로 로그를 출력합니다.

 

🔧 브릿지 예시 흐름

 
Log4j API (Logger, LogManager)
   ↓
log4j-to-slf4j
   ↓
SLF4J API
   ↓
Logback (최종 출력)

 

🧾 마무리

Java의 로깅 시스템은 단순히 콘솔에 메시지를 출력하는 도구가 아닙니다. 운영 환경에서의 생존과 직결되는 필수 요소입니다. 로깅 프레임워크의 선택과 구성은 애플리케이션의 디버깅, 보안, 성능 분석까지도 좌우할 수 있으므로, 이번 포스팅을 통해 로깅 시스템에 대한 탄탄한 기초와 실전 지식을 쌓아가시길 바랍니다.