유지보수 로깅

sion Jeong·2024년 8월 11일
1

로깅이란??

애플리케이션이 동작하는 동안 발생하는 이벤트(예: 오류, 정보, 경고 등)를 기록하는것을 로깅이라 합니다.
이러한 로그는 나중에 버그를 식별하고 해결하는 데 매우 유용하죠!!

로깅 레벨과전략

모든 메서드에 로그를 기록하는 대신, 다음과 같은 로깅 전략을 통해 효율적인 로깅을 구현할 수 있습니다.

  1. 중요한 지점에서만 로그 기록
    시작과 종료: 중요한 비즈니스 로직이나 주요 프로세스의 시작과 종료 시점을 기록
    조건 분기: 중요한 조건 분기나 결정이 이루어지는 지점에서 로그를 기록
    오류 및 예외: 모든 예외 처리 블록에서 예외 정보를 기록
  2. 적절한 로그 레벨 사용
    ERROR: 오류 메시지. 예외가 발생했거나 중요한 문제가 발생했을 때 기록
    WARN: 경고 메시지. 큰 문제는 아니지만 주의가 필요한 사항을 기록
    INFO: 일반적인 운영 정보를 기록. 애플리케이션의 주요 이벤트를 기록
    DEBUG: 개발 중 디버깅 목적으로 사용하는 로그. 상세한 정보가 필요할 때 사용

우리는 기본적으로 로깅을 사용하고 있다.

우리는 사실 기본적으로 로깅을 사용하고 있다는 것을 아시나요??
스프링 부트 스타터를 사용하면 기본적으로 Logback을 사용하여 로깅을 실행합니다.
스프링을 실행시키면 아래의 그림처럼 기본적으로 로그가 찍혀있을을 볼 수 있습니다

로깅을 커스터마이징 해보자

기본 로깅이 유용하더라도 우리의 요구 사항을 충족시키기에는
충분하지 않을 수 있기때문에 커스텀해서 사용할 수 있습니다!!!!
그리고 로깅설정만을 따로 할 파일이 필요합니다. → 그래서 XML파일을 사용합니다.


로깅 전략을 짜보자

보통 서버는 디벨롭서버, 스테이징 서버, 프로덕션 3개의 서버로 운영됩니다 (3개의 각각 도메인을 가진 서버)

프로덕션서버: 실제 서비스되는 서버
스테이징 서버: 프로덕션 서버와 동일한 내용의 서버 → 스테이징 서버에서 개발이 잘 되었는지 실제 데이터와 정상작동하는지 테스트함
디벨롭 서버: 개발자들이 실시간 개발하는 서버

그렇다면 각각 서버에서의 로깅 전략은 어떻게 가져가야 할까요??
실제 프로덕션 서버: 로그는 → INFO 로그가 보이면 보안 취약점입니다. → 실제 운영되는 서버에서는 보이는 로그는 없어야 합니다. (아예 로그가 보이면 안됩니다. 해커가 보고 뚫습니다.)
스테이징 서버: 실 유저 데이터를 그대로 담기 때문에 INFO, Debug가 보이면 안됩니다. ERROR는 노출되어도 됩니다. (개발자가 확인하고 잡아야 하기 때문에, 아직 테스트용 서버이기 떄문입니다)
디벨롭 서버: 로그레벨 INFO 가 보여도 됩니다. (개발과, 디버깅을 할때 사용하는 서버이기 때문에 INFO레벨 이상이 보여도 상관없습니다.)


로깅 인터페이스와 구현체

🛠 SLF4J
simple logging facade for java
다양한 로깅 프레임워크에 대한 추상화(인터페이스) 역할을 하는 프레임워크
단독으로 사용 불가
최종 사용자가 배포시 원하는 구현체를 선택

🛠 Logback
Slf4j의 구현체이며 Log4j를 토대로 만든 프레임워크입니다.
스프링 프레임워크에서도 slf4j와 Logback을 채택해서 사용하고 있습니다.

Logback을 활용해 효과적으로 로깅하기

스프링 부트에서 콘솔 로그의 수준을 변경하는 방법은 → application.properties, logback-spring.xml 에서 설정할 수 있습니다.

application.properties에서 로그 수준을 변경할 수 있지만 세세한 설정에 제한이 있어 → logback-spring.xml로 관리하여 세부적인 설정을 할 수 있습니다.

스프링 부트는 로깅 설정 파일의 네이밍에 관한 규칙을 가지고 있습니다.
스프링 부트가 로딩되는 시점에 로그 설정 파일이 프로젝트 내에 존재하는지 스캔합니다
1. logback-spring.xml
2. logback-spring.groovy
3. logback.xml
4. logback.grooby
등의 파일이 있는지 스캔하고 해당 파일에 정의된 로그 설정을 적용하게 됩니다.
특히 logback-spring.xml은 Spring Boot에 특화된 설정 파일입니다. 이 파일을 사용하면 Spring Boot의 로깅 확장 기능을 활용할 수 있습니다.

Logback 구조

1. Logger (어떻게 기록할까?)

로깅을 수행하는 주요 객체
로거는 이름을 가지며 주로 패키지 이름 또는 클래스 이름과 일치하게 설정
로거는 계층 구조를 가지는데 ROOT 로거는 모든 로거의 상위 로거
출력레벨: TRACE < DEBUG < INFO < WARN < ERROR
기본레벌은 디버그이다. + 지정된 레벨 이하의 메서드들은 기록되지 않는다
ex) INFO 레벨로 지정한 로거는 INFO < WARN < ERROR만 기록한다

2. Appender (어디에다 기록할까?)

로그 메시지가 출력될 대상을 결정

  • 콘솔에다 출력하는 ConsoleAppender
  • 파일로 출력하는 FileAppender
  • 여러개의 파일을 순회하며 로그를 저장 RollingFileAppender
    • 로그 파일을 일정 크기 또는 시간 간격으로 분할(롤링)
  • rollingPolicy: 롤링 정책을 정의
  • 각 로거는 하나 이상의 Appender에 연결될 수 있습니다.

3. Layout (encoder)(어떻게 출력할까?)

Appender에 포함되어 사용자가 지정한 형식으로 → pattern을 사용해 표현될 로그 메시지를 변환하는 역할

예시

<encoder>
    <!-- 로그 메시지의 형식을 정의하는 패턴 설정 -->
    <pattern>
        <!-- %d{yyyy-MM-dd HH:mm:ss.SSS}: 로그 기록 시각을 연도-월-일 시:분:초.밀리초 형식으로 표시 -->
        [%d{yyyy-MM-dd HH:mm:ss.SSS}] 
        
        <!-- %-5level: 로그 레벨을 5자리 고정 폭으로 표시 -->
        [%-5level] 
        
        <!-- %thread: 로그를 기록한 스레드 이름을 표시 -->
        [%thread] 
        
        <!-- %logger: 로그를 기록한 로거의 이름을 표시 -->
        %logger 
        
        <!-- %msg: 로그 메시지를 표시 -->
        %msg
        
        <!-- %n: 새로운 줄로 이동 -->
        %n
    </pattern>
</encoder>

환경 별로 로그 남기는 방법

현재 로그는

  1. 콘솔환경에서 INFO레벨 이상 로그가 찍힘
  2. 파일로 저장되며 INFO레벨 이상 로그가 찍힘
  3. 파일로 저장되며 ERROR레벨 이상 로그가 찍힘 으로 구현되어 있습니다.

현재 환경은 local, develop, blue, green 4개의 서버로 나누어져 있고 → 각각의 환경마다 로그를 남기는 방법을 다르게 지정할 수 있습니다.

console-appender.xml (콘솔로그 설정)

<included> <!-- 어떤 appender를 사용할지 추가 (자바에서 import와 동일) -->

    <!-- 어디에 기록할 것인가 (콘솔Appender) -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!-- 콘솔에 로그를 기록하는 Appender 설정 -->

        <encoder> <!-- 어떻게 출력할 것인가 Encoder -->
            <pattern>${CONSOLE_LOG_PATTERN}</pattern> <!-- 인코딩할 패턴설정 -->
        </encoder>

    </appender>

</included>

file-info-appender.xml (파일 INFO 저장 로그 설정)

<included> <!-- 어떤 appender를 사용할지 추가 (자바에서 import와 동일) -->

    <!-- 롤링 파일Appender: INFO 레벨 이상의 로그를 파일에 기록 -->
    <appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 파일에 로그를 기록하는 Appender 설정 -->

        <!-- 어떻게 출력할 것인가 Encoder -->
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern> <!-- 인코딩할 패턴설정 -->
        </encoder>

        <!-- 롤링 정책: 로그 파일을 일정 크기 또는 시간 단위로 롤링(분할) -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>./log/pium-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 롤링된 파일 이름 패턴 설정 -->
            <maxFileSize>50MB</maxFileSize> <!-- 최대 파일 크기 설정 -->
            <maxHistory>30</maxHistory> <!-- 보관할 최대 파일 수 설정 -->
            <totalSizeCap>1GB</totalSizeCap> <!-- 전체 로그 파일의 최대 크기 설정 -->
        </rollingPolicy>

    </appender>

</included>

file-error-appender.xml (파일 ERROR 저장 로그 설정)


<included> <!-- 어떤 appender를 사용할지 추가 (자바에서 import와 동일) -->

    <!-- 에러 로그를 기록할 롤링 파일Appender: ERROR 레벨 로그만 기록 -->
    <appender name="FILE-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>

        <!-- 어떻게 출력할 것인가 Encoder -->
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern> <!-- 파일에 기록할 패턴 설정 -->
        </encoder>

        <!-- 롤링 정책: 로그 파일을 일정 크기 또는 시간 단위로 롤링(분할) -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>./log/pium-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 롤링된 파일 이름 패턴 설정 -->
            <maxFileSize>50MB</maxFileSize> <!-- 최대 파일 크기 설정 -->
            <maxHistory>30</maxHistory> <!-- 보관할 최대 파일 수 설정 -->
            <totalSizeCap>3GB</totalSizeCap> <!-- 전체 로그 파일의 최대 크기 설정 -->
        </rollingPolicy>

    </appender>

</included>

환경별로 로그 전략을 분리 (로컬환경, develop환경, blue환경, green환경)

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

<!-- 어떻게 기록할 것인가 (Logger)--> <!-- 로깅 설정 파일 (logback-spring.xml) -->
<configuration>

    <!-- 로그 컬러 설정 컨버터 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>

    <!-- 변수명과, 패턴 저장 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %clr(%5level) %cyan(%logger) - %msg%n"/>
    <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %5level %logger - %msg%n"/>

    <!--local 환경에서의 로그설정-->
    <springProfile name="local">

        <!-- include = 어떤 appender를 사용할지 추가 (자바 import와 동일)  -->
        <include resource="console-appender.xml"/>

        <!-- 루트 로거 설정: INFO 레벨 이상의 로그를 콘솔에 기록 -->
        <root level="INFO">
            <appender-ref ref="CONSOLE"/> <!-- 필요한 appender 이름을 appender-ref 태그에 추가 -->
        </root>

    </springProfile>

    <!--develop 환경에서의 로그설정-->
    <springProfile name="develop">

        <!-- include = 어떤 appender를 사용할지 추가 (자바 import와 동일)  -->
        <include resource="file-error-appender.xml"/>

        <!-- 루트 로거 설정: ERROR 레벨의 로그를 파일에 기록 -->
        <root level="ERROR">
            <appender-ref ref="FILE-ERROR"/> <!-- 필요한 appender 이름을 appender-ref 태그에 추가 -->
        </root>

    </springProfile>

    <!--blue 환경에서의 로그설정-->
    <springProfile name="blue">

        <!-- include = 어떤 appender를 사용할지 추가 (자바 import와 동일)  -->
        <include resource="file-error-appender.xml"/>

        <!-- 루트 로거 설정: ERROR 레벨의 로그를 파일에 기록 -->
        <root level="ERROR">
            <appender-ref ref="FILE-ERROR"/> <!-- 필요한 appender 이름을 appender-ref 태그에 추가 -->
        </root>

    </springProfile>

    <!--green 환경에서의 로그설정-->
    <springProfile name="green">

        <!-- include = 어떤 appender를 사용할지 추가 (자바 import와 동일)  -->
        <include resource="file-error-appender.xml"/>

        <!-- 루트 로거 설정: ERROR 레벨의 로그를 파일에 기록 -->
        <root level="ERROR">
            <appender-ref ref="FILE-ERROR"/> <!-- 필요한 appender 이름을 appender-ref 태그에 추가 -->
        </root>

    </springProfile>

</configuration>
profile
개발응애입니다.

0개의 댓글