Logging 로컬 환경 구축하기

김태훈·2023년 8월 1일
0

실제 서버 구동중 발생하는 여러 문제점을 포착하기 위해, 로깅을 본격적으로 시작해보려고 한다.

도입하기 전에 가장 고민되는 부분은, 로컬에서의 로그와, 실제 배포 환경에서의 로그를 분명히 구별해야만 했다.

현재는 logback-spring.xml파일로 실제 서버에서 문제가 발생하면 슬랙으로 띠링띠링 알림이 울리고있지만, 로컬에서는 해당파일을 배제하고 개발하고 있다.

따라서 이번에, local 과 production 환경을 분명히 나누어서 logback을 설정해보자.

1. application.yml

spring:
  profiles:
    active: local
logging:
  config: classpath:logback-${spring.profiles.active}.xml

다음처럼 yml을 설정한다.
나는 어차피 github action을 이용하여 해당 설정 변수들을 production으로만 바꾸어서 application.yml 파일이 필요없이 설정값들만 직접 입력하면 된다.. 그래서 application.yml파일은 로컬에서만 쓸모가 있으므로, local로 고정시켰다.

그리고, logging.config를 logback-[xxx].xml 파일로 적용시킨다.
이 이유는 아래 참고사항을 살펴보면 되겠다.

참고) 스프링부트 resources 폴더내에 설정파일들 적용 순서
1. logback-spring.xml
2. .yml
3. 둘다 존재시, .yml먼저 적용후, .xml 적용

2. logback-local.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}[%-5level] : %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

로컬에서 로깅기록을 파일에 저장해도 되겠지만.. 굳이? 그냥 콘솔로 찍기로 했다.

3. 만약 로그파일로 저장하고싶다면..?

1. logback-local.properties

# 로그파일 경로
log.config.path=logs/local
#로그파일 이름
log.config.filename=local_log
#로그 레벨
logging.level.root=info

다음과 같이 작성한다.
이때, logging.level.root는 xml에서 source로 받아오는 property 정보이다.

주의할 점 !
여기서 주의할점은 log.config.path의 경로인데,

만약 다음과 같은 오류가 뜬다면 해당 경로의 문제일 가능성이 있으니 '/'를 제외시켜보자.

<!-- 개발 환경별 log profile 설정 -->
    <springProfile name="local">
        <property resource="logback-local.properties"/>
    </springProfile>
    <!--Environment 내의 프로퍼티들을 개별적으로 설정할 수도 있다.-->
    <springProperty scope="context" name="LOG_LEVEL" source="logging.level.root"/>
    <!-- log file path -->
    <property name="LOG_PATH" value="${log.config.path}"/>
    <!-- log file name -->
    <property name="LOG_FILE_NAME" value="${log.config.filename}"/>
    <!-- err log file name -->
    <property name="ERR_LOG_FILE_NAME" value="err_log"/>
    <!-- pattern -->
    <property name="LOG_PATTERN" value="%-5level %d{yy-MM-dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n"/>

    <!-- File Appender -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 파일경로 설정 -->
        <file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>

        <!-- 출력패턴 설정-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>

        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 kb, mb, gb -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
            <maxHistory>30</maxHistory>
            <!--<MinIndex>1</MinIndex>
            <MaxIndex>10</MaxIndex>-->
        </rollingPolicy>
    </appender>

    <!-- 에러의 경우 파일에 로그 처리 -->
    <appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${ERR_LOG_FILE_NAME}.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOG_PATH}/${ERR_LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 kb, mb, gb -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
    </appender>
	<root level="${LOG_LEVEL}">
        <appender-ref ref="File"/>
        <appender-ref ref="Error"/>
    </root>
</configuration>

이런식으로 적용을 해보자.
여기서 springProperty 값으로 logback-local.properties 파일에서 logging.level.root 값을 불러와 "LOG_LEVEL"로 저장시키고, 아래 마지막에서 root level의 로그 레벨을 그대로 대입한다.

그리고, logback-spring.xml도 local환경에서는 필요없는 slack알림을 끄기위해 다음과 springProperty를 추가한다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <springProperty name="SLACK_WEBHOOK_URI" source="logging.slack.webhook-uri"/>
    <springProfile name="prod"> # ---------추가-----------
        <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>
        <appender name="SLACK" class="com.github.maricn.logback.SlackAppender">
            <webhookUri>${SLACK_WEBHOOK_URI}</webhookUri>
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %msg %n</pattern>
            </layout>
            <username>spring-error-bot</username>
            <iconEmoji>:stuck_out_tongue_winking_eye:</iconEmoji>
            <colorCoding>true</colorCoding>
        </appender>
        <appender name="ASYNC_SLACK" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="SLACK"/>
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>WARN</level>
            </filter>
        </appender>
        <appender name="Sentry" class="io.sentry.logback.SentryAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>WARN</level>
            </filter>
        </appender>
    </springProfile>
    <root level="INFO">
        <appender-ref ref="Console"/>
        <appender-ref ref="Sentry"/>
        <appender-ref ref="ASYNC_SLACK"/>
    </root>
</configuration>

많이 수정된 부분이 있습니다.

이어지는 포스트 여기를 참조해주세용

profile
기록하고, 공유합시다

0개의 댓글