본문 바로가기
배포

Logback을 이용한 EC2 환경 로그 관리

by LDY3838 2024. 7. 17.
반응형

이번 게시글에서는 Logback을 활용하여 EC2 환경에서 로그 관리하는 방법에 대해서 다루도록 하겠습니다.

프로젝트를 진행하면서 로컬에서 개발할 때는 바로 로그를 확인할 수 있지만 EC2에 프로젝트를 배포해 놓았을 때는 로그를 바로 확인하지 못하면 사라지는 문제가 있었습니다.

오류가 발생해도 로그 관리를 하지 못하여 인지하지 못하거나 다시 확인하지 못하는 것은 심각한 문제라 생각하여 스프링에서 제공하는 기능은 Logback을 활용하여 로그 관리를 하기로 결정하였습니다.


로그백 설정

Logback은 build.gradle에서 아래와 같은 라이브러리를 등록하면 사용할 수 있습니다. 해당 라이브러리는 스프링 개발을 위해서 필수적이기 때문에 사실상 추가로 해주어야 하는 작업은 없다고 생각하시면 됩니다.

implementation 'org.springframework.boot:spring-boot-starter-web'

Logback 설정을 해주기 위한 logback-spring.xml 파일의 경로는 아래와 같이 resources 아래에 두면 됩니다.


EC2 환경에서의 로그 설정을 하기에 앞서 logback-spring.xml의 내용과 local에서의 로그 설정에 대해서 다루도록 하겠습니다.

우선 logback-spring.xml 파일의 내용은 아래와 같습니다.

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

<configuration>
    <property name="FILE_LOG_PATTERN"
              value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] [%thread] %-5level [%C.%M:%L] - %msg %ex{5}%n"/>
    <property name="CONSOLE_LOG_PATTERN"
              value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg %ex{5}%n"/>

    <springProfile name="local">
        <include resource="console-appender.xml"/>
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>

    <springProfile name="dev">
        <include resource="logs/common/file-info-appender.xml"/>
        <include resource="logs/common/file-warn-appender.xml"/>
        <include resource="logs/common/file-error-appender.xml"/>
        <include resource="logs/error/file-info-appender.xml"/>
        <include resource="logs/error/file-warn-appender.xml"/>
        <include resource="logs/error/file-error-appender.xml"/>

        <logger additivity="false" level="INFO" name="ErrorLogger">
            <appender-ref ref="ERROR_ERROR"/>
            <appender-ref ref="ERROR_WARN"/>
            <appender-ref ref="ERROR_INFO"/>
        </logger>

        <root level="INFO">
            <appender-ref ref="FILE-INFO"/>
            <appender-ref ref="FILE-WARN"/>
            <appender-ref ref="FILE-ERROR"/>
        </root>
    </springProfile>

    <springProfile name="prod">
        <include resource="logs/common/file-info-appender.xml"/>
        <include resource="logs/common/file-warn-appender.xml"/>
        <include resource="logs/common/file-error-appender.xml"/>
        <include resource="logs/error/file-info-appender.xml"/>
        <include resource="logs/error/file-warn-appender.xml"/>
        <include resource="logs/error/file-error-appender.xml"/>

        <logger additivity="false" level="INFO" name="ErrorLogger">
            <appender-ref ref="ERROR_ERROR"/>
            <appender-ref ref="ERROR_WARN"/>
            <appender-ref ref="ERROR_INFO"/>
        </logger>

        <root level="INFO">
            <appender-ref ref="FILE-INFO"/>
            <appender-ref ref="FILE-WARN"/>
            <appender-ref ref="FILE-ERROR"/>
        </root>
    </springProfile>

</configuration>

위 파일의 내용을 하나하나 살펴보도록 하겠습니다.

우선 configuration 태그로 설정 파일임을 명시하였습니다.

이후 property로 콘솔에 찍을 때의 로그 패턴과 파일에 로그를 찍을 때의 패턴을 나누어 주었습니다. 이와 같이 해준 이유는 콘솔에 로그를 찍을 때 시각적으로 좀 더 보기 편하게 하기 위한 목적이 있습니다.

로그 패턴에 대해서 자세히 살펴보도록 하겠습니다.

[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg %ex{5}%n

yyyy-MM-dd HH:mm:ss시간을 의미하고 %-4relative는 로그 메시지가 시작된 이후 경과 시간을 보여줍니다.

%green([%thread]) 부분은 로그 메시지가 생성된 스레드의 이름을 녹색으로 보여줍니다.

%highlight(%-5level)로그 레벨(INFO, WARN, ERROR 등)을 보여주는 역할을 하고 하이라이트 한 후 5자리에 맞춰 출력합니다.

%boldWhite([%C.%M:%yellow(%L)])에는 굵은 흰 글자로 보여준다는 의미입니다.
%C - 로거 이름 또는 클래스 이름, %M - 메서드 이름, %L - 라인 번호를 출력(%yellow 때문에 노란색으로 출력)

%msg실제 로그 메시지를 출력합니다.

%ex[5]는 예외가 발생한 경우 스택 트레이스를 5줄까지만 보여줍니다.

위 내용에 따른 실제 로그는 아래와 같습니다.

이 아래에 있는 부분에서 springProfile은 profile을 설정할 수 있게 해 줍니다. include에서 console-appender.xml을 설정하였는데 이 내용은 아래와 같습니다.

<included>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>
</included>

Logback은 Appender를 사용하여 로그에 대한 설정을 진행하는데 ConsoleAppender, RollingFileAppender 등이 존재합니다.

저는 profile이 local인 경우 콘솔에 로그를 찍기 위해서 ConsoleAppender를, EC2 환경에서는 파일에 로그를 찍기 위해서 RollingFileAppender를 적용했습니다. FileAppender가 아닌 RollingFileAppender를 사용한 이유는 특정 조건이 됐을 때 자연스럽게 다음 파일로 로그를 진행하기 위해서입니다.

그리고 위 파일 내용을 보면 logback-spring.xml에서 정의한 CONSOLE_LOG_PATTERN을 사용하고 있습니다.

아래와 같이 appender 이름을 지정해 주고appender를 logback-spring.xml에서 append-ref를 통해 가져옵니다.

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

logback-spring.xml에서 해당 appender를 가져오는 코드는 아래와 같습니다.

<springProfile name="local">
    <include resource="console-appender.xml"/>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</springProfile>

이다음에는 profile이 dev, prod인 상황입니다. 두 profile은 각각 개발, 배포 서버로 둘 다 파일에 로그를 저장하고 내용은 동일합니다.

<springProfile name="dev">
    <include resource="logs/common/file-info-appender.xml"/>
    <include resource="logs/common/file-warn-appender.xml"/>
    <include resource="logs/common/file-error-appender.xml"/>
    <include resource="logs/error/file-info-appender.xml"/>
    <include resource="logs/error/file-warn-appender.xml"/>
    <include resource="logs/error/file-error-appender.xml"/>

    <logger additivity="false" level="INFO" name="ErrorLogger">
        <appender-ref ref="ERROR_ERROR"/>
        <appender-ref ref="ERROR_WARN"/>
        <appender-ref ref="ERROR_INFO"/>
    </logger>

    <root level="INFO">
        <appender-ref ref="FILE-INFO"/>
        <appender-ref ref="FILE-WARN"/>
        <appender-ref ref="FILE-ERROR"/>
    </root>
</springProfile>

위와 같이 설정을 해주었는데 Exception이 발생하는 경우는 ErrorLogger를 이용하여 따로 처리해 주어 Exception과 관련된 로그들은 별도의 파일에 저장해 주었습니다.

위와 같이 일반 로그와 에러 로그를 분리해서 운영이 편리하도록 만들었습니다.

각각의 RollingFileAppender는 파일명 등을 제외하면 모두 비슷하니 예시로 하나의 Appender만 보여드리도록 하겠습니다.

참고로 로그 레벨 DEBUG, TRACE은 로그 처리를 하지 않은 이유너무 많은 로그가 생겨 로그 파일 용량이 너무 커질 수 있기 때문입니다.

<included>
    <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE-ERROR">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/log/common/error/error-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
    </appender>
</included>

이때 ${LOG_PATH}를 설정해주어야 하기 때문에 application.yml 파일에 아래와 같은 변수를 지정해주어야 합니다.

logging:
  file:
    path: /logs/goat

이제 스프링에서 해주어야 할 일은 끝났습니다.

EC2 환경에서 로그가 잘 찍히는지 확인해 보도록 하겠습니다.


EC2 로그 확인

EC2 환경에서 로그 파일이 제대로 만들어지지 않는 문제가 발생하였습니다.

이와 관련되어 문제를 확인하던 중 제가 서버를 docker로 배포해 놓았다는 것이 생각이 났고 volume을 container에 mount 해주어야 한다는 것을 깜빡했다는 것을 깨달았습니다.

따라서 제가 blue green deploy를 하기 위해서 설정해 놓은 docker-compose 파일의 내용을 아래와 같이 수정해 주었습니다.

version: '3.8'

services:
  blue:
    image: 이미지 명
    container_name: blue
    ports:
      - "8080:8080"
    environment:
      - ENV=blue
    volumes:
      - /home/ubuntu/logs/goat:/logs/goat
  green:
    image: 이미지 명
    container_name: green
    ports:
      - "8081:8080"
    environment:
      - ENV=green
    volumes:
      - /home/ubuntu/logs/goat:/logs/goat

이제 정상적으로 로그 파일이 생성되었습니다.

이 로그 파일 중 하나의 내용을 살펴보도록 하겠습니다.

아래와 같은 로그가 생성이 되었습니다.

[2024-07-14 14:08:49:4894549] [SpringApplicationShutdownHook] INFO  [org.quartz.core.QuartzScheduler.standby:585] - Scheduler quartzScheduler_$_NON_CLUSTERED paused.
[2024-07-14 14:08:51:4896363] [SpringApplicationShutdownHook] INFO  [org.springframework.scheduling.quartz.SchedulerFactoryBean.destroy:844] - Shutting down Quartz Scheduler
[2024-07-14 14:08:51:4896377] [SpringApplicationShutdownHook] INFO  [org.quartz.core.QuartzScheduler.shutdown:666] - Scheduler quartzScheduler_$_NON_CLUSTERED shutting down.
[2024-07-14 14:08:51:4896382] [SpringApplicationShutdownHook] INFO  [org.quartz.core.QuartzScheduler.standby:585] - Scheduler quartzScheduler_$_NON_CLUSTERED paused.
[2024-07-14 14:08:51:4896392] [SpringApplicationShutdownHook] INFO  [org.quartz.core.QuartzScheduler.shutdown:740] - Scheduler quartzScheduler_$_NON_CLUSTERED shutdown complete.
[2024-07-14 14:08:51:4896679] [SpringApplicationShutdownHook] INFO  [org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.destroy:650] - Closing JPA EntityManagerFactory for persistence unit 'default'

이 로그를 logback-spring.xml에서 아래와 같이 선언했던 내용과 비교하면 같은 형식을 가진 것을 확인하실 수 있습니다.

property name="FILE_LOG_PATTERN"
          value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] [%thread] %-5level [%C.%M:%L] - %msg %ex{5}%n"/>

local에서 작업할 때의 로그EC2 환경에서 실제로 배포할 때의 로그 관리를 해보았습니다.

실제 배포를 할 때 로그를 관리하는 것은 필수라 생각하여 해당 기능을 구현하였고 이후 문제가 발생하면 로그를 확인하여 문제의 원인을 파악하고 해결할 수 있게 되었습니다.

반응형

댓글