Spring Boot에서 개발, 운영 환경에 따른 Logback 설정

Frog Lemon·2025년 9월 9일
0

Spring

목록 보기
5/6
post-thumbnail

Spring Boot에서 개발, 운영 환경에 따른 Logback 설정

모잇지 프로젝트를 진행하며 개발 환경과 운영 환경에서 서로 다른 로깅 전략이 필요했다. 그 이유는 개발 환경에서 직접 보는 것이 효과적인 로그들이 있었고, 운영 환경에서는 모니터링을 목적으로 남기는 로그들이 필요했다. 이번에 로그를 담당하며 이를 효과적으로 관리하는 방법을 알아보았다.


🔧 1. 전역 변수 관리 - logback-variable.properties

로깅 설정에서 사용할 변수들을 중앙에서 관리한다다.

# 로그 파일 위치
# 로그 파일 경로 backend/logs/info 또는 backend/logs/error
LOG_PATH=./logs
DEBUG_PATH=debug
WARN_PATH=warn
INFO_PATH=info
ERROR_PATH=error

# 로그 출력 포맷 
# CONSOLE_PATTERN : 개발 환경(콘솔에 출력)
# ROLLING_PATTERN : 운영 환경(파일로 저장) 
CONSOLE_PATTERN=%d{yyyy-MM-dd HH:mm:ss.SSS} %magenta([%thread]) %highlight([%-3level]) %logger{36} - %replace(%msg){'\n', ' '} %n 
ROLLING_PATTERN=%d{yyyy-MM-dd HH:mm:ss.SSS}  %logger{5} - %msg %n

# 로그 파일 정책
MAX_FILE_SIZE=10MB
TOTAL_SIZE=100MB
MAX_HISTORY=10

변수 설명

  • LOG_PATH: 로그 파일이 저장될 기본 경로
  • DEBUG_PATH, INFO_PATH, ERROR_PATH, WARN_PATH: 각각 일반 로그와 에러 로그의 하위 디렉토리
  • CONSOLE_PATTERN: 개발 환경에서 콘솔에 출력될 로그 포맷 (컬러 포함)
  • ROLLING_PATTERN: 운영 환경에서 파일에 저장될 로그 포맷 (심플)
  • MAX_FILE_SIZE: 개별 로그 파일의 최대 크기
  • TOTAL_SIZE: 전체 로그 파일들의 최대 크기
  • MAX_HISTORY: 보관할 로그 파일의 최대 개수

⚙️ 2. application.properties 환경별 설정

개발 환경 설정

# application-dev.properties
# 개발 환경에서는 주로 콘솔 로깅 사용
spring.profiles.active=dev

운영 환경 설정

# application-prod.properties  
# 운영 환경에서는 파일 로깅 사용
spring.profiles.active=prod
logging.file.path=./logs

📊 3. 로그 출력 패턴 분석

항목의미예시 출력
%d{yyyy-MM-dd HH:mm:ss.SSS}로그 발생 시각 (형식 지정 가능)2025-08-04 10:15:31.420
%thread로그를 발생시킨 스레드명main, http-nio-8080-exec-1
%magenta(...)해당 항목을 콘솔에서 자홍색으로 출력 (색상 강조)[main]
%highlight(...)로그 레벨별 색상 지정 (INFO, ERROR 등)[INF], [ERR]
%-3level로그 레벨 (좌측 정렬, 최소 너비 3칸)INF, WRN, DBG
%logger{36}Logger 이름 (FQCN 기준 최대 36자)com.f12.moitz.LoggingTest
%logger{5}Logger 이름 (마지막 5단어 또는 문자 기준으로 축약)moitz.LoggingTest
%replace(%msg){'\n', ' '}메시지에서 줄바꿈(\n)을 공백으로 치환 (단일라인 유지용)"line1\nline2""line1 line2"
%msg로그 메시지 본문"사용자 정보 조회 완료"

개발 환경 로그 예시

2025-08-04 10:15:31.420 [main] [DEBUG] com.f12.moitz.service.UserService - 사용자 정보 조회 시작
2025-08-04 10:15:31.425 [http-nio-8080-exec-1] [INFO] com.f12.moitz.controller.UserController - GET /api/users 요청 수신

📡 모니터링 로그 쿼리를 위한 Logstash

운영 환경에서는 단순히 로그를 파일에 저장하는 것만으로는 부족하다.
ElasticSearch + Logstash 또는 CloudWatch, OpenSearch 같은 로그 수집 시스템으로 로그를 전달하고, 이를 분석할 수 있어야 한다.

이를 위해 logback-spring.xml에서는 LogstashEncoder를 사용해 로그를 JSON 포맷으로 변환했다.
이를 위한 설정은 아래와 같다.

<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>

📄 4. logback-spring.xml 전체 구조

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

    <!-- 외부 프로퍼티 로딩 -->
    <property resource="logback-variable.properties" />

    <!-- Spring 환경 변수 (application.properties 에서 주입됨) -->
    <springProperty scope="context" name="DEBUG_LOG_PATH" source="logging.file.debug.path"/>
    <springProperty scope="context" name="INFO_LOG_PATH" source="logging.file.info.path"/>
    <springProperty scope="context" name="WARN_LOG_PATH" source="logging.file.warn.path"/>
    <springProperty scope="context" name="ERROR_LOG_PATH" source="logging.file.error.path"/>

    <!-- 개발 환경용 설정 -->
    <springProfile name="dev">
        <!-- 콘솔 전용 Appender -->
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>${CONSOLE_PATTERN}</pattern>
            </encoder>
        </appender>
        <!-- DEBUG 전용 Appender -->
        <appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
            <file>${LOG_PATH}/${DEBUG_PATH}/application.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>${DEBUG_LOG_PATH}/application-info-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
                <maxHistory>${MAX_HISTORY}</maxHistory>
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
                <totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
            </rollingPolicy>
        </appender>

        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="FILE_DEBUG"/>
        </root>
        <!-- 프레임워크 기본 로거 수준 설정 -->
        <logger name="org.springframework" level="INFO"/>
        <logger name="org.springframework.boot" level="INFO"/>
        <logger name="org.hibernate" level="WARN"/>
        <logger name="org.apache.catalina" level="INFO"/>
        <logger name="com.zaxxer.hikari" level="WARN"/>

        <!--mongoDB-->
        <logger name="data.mongodb" level="WARN"/>
        <logger name="org.mongodb.driver" level="WARN"/>

        <!--Web Flux-->
        <logger name="io.netty" level="WARN"/>
        <logger name="reactor.netty" level="WARN"/>

        <!-- CloudWatch 메트릭 관련 로거 비활성화 -->
        <logger name="io.micrometer.cloudwatch2" level="OFF"/>
        <logger name="software.amazon.awssdk" level="OFF"/>
        <logger name="io.micrometer.core.instrument.push.PushMeterRegistry" level="OFF"/>

        <!-- AWS SDK 관련 로거 비활성화 -->
        <logger name="software.amazon.awssdk.auth.credentials" level="OFF"/>
        <logger name="software.amazon.awssdk.core.interceptor" level="OFF"/>
        <logger name="software.amazon.awssdk.services.cloudwatch" level="OFF"/>
        <logger name="software.amazon.awssdk.utils.cache" level="OFF"/>
        <logger name="software.amazon.awssdk.core.internal" level="OFF"/>

    </springProfile>

    <!-- ========================================================= -->

    <!-- 운영 환경용 설정 -->
    <springProfile name="prod">

        <!-- INFO 전용 Appender -->
        <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>

            <file>${LOG_PATH}/${INFO_PATH}/application-info.log</file>

            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>${INFO_LOG_PATH}/application-info-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
                <maxHistory>${MAX_HISTORY}</maxHistory>
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
                <totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
            </rollingPolicy>

            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>

        <!-- WARN 전용 Appender -->
        <appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>

            <file>${LOG_PATH}/${WARN_PATH}/application-warn.log</file>

            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>${WARN_LOG_PATH}/application-warn-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
                <maxHistory>${MAX_HISTORY}</maxHistory>
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
                <totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
            </rollingPolicy>

            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>WARN</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>

        <!-- ERROR 전용 Appender -->
        <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>

            <file>${LOG_PATH}/${ERROR_PATH}/application-error.log</file>

            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>${ERROR_LOG_PATH}/application-error-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
                <maxHistory>${MAX_HISTORY}</maxHistory>
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
                <totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
            </rollingPolicy>

            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>

        <root level="INFO">
            <appender-ref ref="FILE_INFO"/>
            <appender-ref ref="FILE_WARN"/>
            <appender-ref ref="FILE_ERROR"/>
        </root>
    </springProfile>

</configuration>

🔍 5. 로깅 설정 파일 상세 분석

외부 프로퍼티 로딩

<property resource="logback-variable.properties" />

logback-variable.properties에 정의된 전역 변수들을 logback 설정에서 사용할 수 있게 한다.

Spring 환경 변수 연동

<springProperty scope="context" name="DEBUG_LOG_PATH" source="logging.file.debug.path" defaultValue="${LOG_PATH}/${DEBUG_PATH}" />
<springProperty scope="context" name="INFO_LOG_PATH"  source="logging.file.info.path"  defaultValue="${LOG_PATH}/${INFO_PATH}" />
<springProperty scope="context" name="WARN_LOG_PATH"  source="logging.file.warn.path"  defaultValue="${LOG_PATH}/${WARN_PATH}" />
<springProperty scope="context" name="ERROR_LOG_PATH" source="logging.file.error.path" defaultValue="${LOG_PATH}/${ERROR_PATH}" />
  • scope="context": Spring ApplicationContext가 초기화될 때 값을 가져옴
  • name: logback 내에서 참조할 이름 (ex. ${INFO_LOG_PATH})
  • source: Spring 설정 파일(application.properties)에서 가져올 key 값
  • defaultValue: source에서 값을 찾지 못할 경우 사용할 기본값

개발 환경 설정 (dev profile)

<springProfile name="dev">
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_PATTERN}</pattern>
        </encoder>
    </appender>
    
    <!-- 프레임워크 로거 레벨 설정 -->
    <logger name="org.springframework" level="INFO"/>
    <logger name="org.springframework.boot" level="INFO"/>
    <logger name="org.hibernate" level="WARN"/>
    <logger name="org.apache.catalina" level="INFO"/>
    <logger name="com.zaxxer.hikari" level="WARN"/>
	<!-- 그외에도 DB, CloudWatch, AWS 등의 로거 레벨 설정 가능-->
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
    </root>
</springProfile>

주요 구성 요소:

  • springProfile: Spring 프로파일이 dev일 때만 활성화
  • appender: 로그 출력 방식을 정의 (콘솔 출력용)
  • encoder: 로그 메시지의 출력 포맷 정의
  • logger: 특정 패키지/클래스의 로그 레벨 설정
  • root level: 전체 애플리케이션의 기본 로그 레벨

운영 환경 설정 (prod profile)

INFO 전용 Appender

<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_PATH}/${INFO_PATH}/application-info.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${INFO_LOG_PATH}/application-info-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
        <maxHistory>${MAX_HISTORY}</maxHistory>
        <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
        <totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
    </rollingPolicy>

    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>

    <encoder>
      <pattern>${ROLLING_PATTERN}</pattern>
    </encoder>
</appender>

ERROR 전용 Appender

<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_PATH}/${ERROR_PATH}/application-error.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${ERROR_LOG_PATH}/application-error-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
        <maxHistory>${MAX_HISTORY}</maxHistory>
        <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
        <totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
    </rollingPolicy>

    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>ERROR</level>
    </filter>

    <encoder>
        <pattern>${ROLLING_PATTERN}</pattern>
    </encoder>
</appender>

WARN,DEBUG 전용 Appender는 이하 생략하겠다...

주요 구성 요소:

  • RollingFileAppender: 파일 크기/시간 기준으로 로그 파일을 분할
  • rollingPolicy: 파일 롤링 정책 설정
    • fileNamePattern: 롤링된 파일의 이름 패턴 및 압축 설정
    • maxFileSize: 단일 로그 파일의 최대 크기 (예: 10MB)
    • maxHistory: 최대 보관 일수 또는 개수 (예: 10일)
    • totalSizeCap: 전체 로그 디렉토리의 최대 용량 제한 (예: 50MB)
  • filter: 특정 로그 레벨 필터링
    • ThresholdFilter: 설정된 레벨 이상만 출력 (INFO 설정 시 DEBUG 무시)

🚀 6. 실행 방법

개발 환경 실행

# IDE에서 실행 시
-Dspring.profiles.active=dev

# JAR 실행 시
java -jar -Dspring.profiles.active=dev your-application.jar

운영 환경 실행

# JAR 실행 시
java -jar -Dspring.profiles.active=prod your-application.jar

📂 7. 생성되는 폴더 구조

운영 환경에서는 다음과 같은 폴더 구조가 생성된다.

logs/
├── info/
│   ├── application-info.log
│   ├── application-info-2025-08-04.0.log.zip
│   └── application-info-2025-08-04.1.log.zip
└── error/
    ├── application-error.log
    ├── application-error-2025-08-04.0.log.zip
    └── application-error-2025-08-04.1.log.zip
└── warn/
    ├── application-warn.log
    ├── application-warn-2025-08-04.0.log.zip
    └── application-warn-2025-08-04.1.log.zip
   

💡 8. 주요 장점

환경별 최적화

  • 개발 환경: 콘솔 출력, 컬러 하이라이트, 상세한 디버그 정보
  • 운영 환경: 파일 저장, 레벨별 분리, 압축 및 롤링

자동 로그 관리

  • 파일 크기 기반 자동 롤링
  • 날짜별 파일 분할
  • 오래된 로그 자동 삭제
  • ZIP 압축으로 저장 공간 절약

성능 최적화

  • 운영 환경에서 불필요한 로그 레벨 제한
  • 프레임워크 로거 레벨 조정으로 노이즈 감소
  • 효율적인 파일 I/O

이 설정을 통해 개발과 운영 환경에서 각각 최적화된 로깅 전략을 구현할 수 있었다. 개발 시에는 상세한 디버그 정보를, 운영 시에는 효율적이고 관리하기 쉬운 로그를 얻을 수 있었다.
profile
도전하며 굴러가는 돌멩이, 인생 마라톤 중 😎

0개의 댓글