Logging And RollingFileAppender

풀어갈 나의 이야기·2022년 6월 18일
1

Spring Framework

목록 보기
3/4

개요

로깅을 하는 이유?

  • 로깅이란건.. 시스템이 동작할때 시스템의 상태나 동작정보들을 시간 경과에 따라 기록하는 것을 의미함
    • 로깅의 베네핏
      • 개발자는 개발과정 혹은 개발 후에 발생할 수 있는 예상치 못한 애플리케이션의 문제를 진단
      • 다양한 정보 수집
      • 사용자 로그의 경우는 분석 데이터로 활용할 수 있음

로깅방식

  • 초기 스프링은 JCL (Jakarta Commons Logging) 을 이용해서 로깅을 구현
  • 요즘에는 대표적으로 Log4j 와 Logback 를 사용
    • Log4j 는 가장 오래된 프레임워크이며, Apache 의 JAVA 기반 Logging Framework 다.
      • 로그레벨의 경우 6단계로 구성되어 있음
    • Logback 은 log4j 이후 출시된 Java 기반 Logging Framework 이다.
      • SLF4j 의 구현체이며, Spring Boot 환경이라면 별도의 Dependency 추가없이 기본적으로 사용할수 있음
        • SLF4J 는 JCL 의 가진 문제를 해결하기 위해 클래스 로더 대신에 컴파일 시점에서 구현체를 선택하도록 변경시키기 위해 도입된 것

LogBack

  • Logback 은 5단계의 로그 레벨을 가진다
    • 심각도 수준은 Error > Warn > Info > Debug > Trace 이다.
      • 📝 Trace : 모든 레벨에 대한 로깅이 추적되므로 개발 단계에서 사용함
      • ⚙️ Debug : 개발 단계에서 사용하며, SQL 로깅을 할 수 있음
      • ✅ Info : 운영에 참고할만한 사항, 중요한 비즈니스 프로세스가 완료됨
      • ⚠ ️Warn : 로직 상 유효성 확인, 예상 가능한 문제로 인한 예외 처리, 당장 서비스 운영에는 영향이 없지만 주의해야 할 부분
      • ⛔️ Error : 예상하지 못한 심각한 문제가 발생하는 경우, 즉시 조취를 취해야 할 수준의 레벨
    • Debug 와 Trace 레벨은 많은 양의 로그가 쌓이므로 자칫 운영 단계에서 해당 레벨의 로깅을 할 경우 용량 감당이 안 될 수 있다.

로그파일 작성

  • 콘솔 로그의 수준을 변경하는 방법은 application.yml 과 logback-spring.xml 에서 설정하는 방법이 있다.
    • application.yml 은 설정하는 난이도가 비교적 쉽지만, 실제 제품에 사용하기엔 한계가 있고 세부적인 설정이 불편
    • 우리는 logback.xml 을 사용하고 있다.

configuration

<configuration scan="true" scanPeriod="30 seconds">
 -- 설정 변경시 설정 자동 reload
  • configuration 파일을 읽어 Logback의 설정을 재구성할 수 있다.
    • 설정파일의 \ 에서 scan속성을 true로 설정하게 되면 설정이 변경시 자동으로 변경된 설정을 다시 로드함
    • scanPeriod의 값은 시간과 시간의 단위("milliseconds", "seconds", "minutes", "hours")를 조합하여 기술할수 있음.
      • 만약 단위를 사용하지 않고 값만 적게 되는 경우에는 밀리초 단위가 설정

appender (어디에 출력할까?)

  • 로그 메세지가 출력할 대상 결정

    • ConsoleAppender : 콘솔에다 출력하는 Appender
    • FileAppender : 파일로 출력하는 Appender
    • RollingFileAppender : 파일을 일정 조건에 맞게 따로 저장하는 Appender
      • DailyRollingFileAppender
  • Encoder

    • 로그 이벤트를 바이트 배열로 변환하고 해당 바이트 배열을 OutputSteam에 쓰는 작업을 담당함
      • Appender 에 포함되어 사용자가 지정한 메시지를 변환하는 역할
<appender name="meter_log" class="ch.qos.logback.core.FileAppender">
    <File>${LOG_PATH}/meter.log</File>
    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/meter_log.%d.gz</fileNamePattern>
        <maxHistory>150</maxHistory>
    </rollingPolicy>
</appender>

pattern

  • pattern 에서 지정한 방식대로 시간과 레벨 등의 설정이 되고난 후 콘솔에 메세지를 출력한다.

    • 패턴에 사용되는 요소

      • %Logger{length} - Logger name을 축약할 수 있다. {length}는 최대 자리 수
      • %thread - 현재 Thread 이름
      • %-5level - 로그 레벨, -5는 출력의 고정폭 값
      • %msg - 로그 메시지 (=%message)
      • %n - new line
      • ${PID:-} - 프로세스 아이디
    • 기타

      • %d : 로그 기록시간
      • %p : 로깅 레벨
      • %F : 로깅이 발생한 프로그램 파일명
      • %M : 로깅일 발생한 메소드의 이름
      • %l : 로깅이 발생한 호출지의 정보
      • %L : 로깅이 발생한 호출지의 라인 수
      • %t : 쓰레드 명
      • %c : 로깅이 발생한 카테고리
      • %C : 로깅이 발생한 클래스 명
      • %m : 로그 메시지
      • %r : 애플리케이션 시작 이후부터 로깅이 발생한 시점까지의 시간
  • 상세설명

<appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
   <file>./logs/info.log</file> 파일을 저장할 경로를 정한다
   <filter class="ch.qos.logback.classic.filter.LevelFilter">
     <level>INFO</level>
     <onMatch>ACCEPT</onMatch> 해당 레벨만 기록한다.
     <onMismatch>DENY</onMismatch> 다른 수준의 레벨은 기록하지 않는다.(상위 레벨도 기록 안함), 상위 수준의 레벨에 대한 기록을 원하면 ACCEPT 로 하면 기록된다.
   </filter> 레벨별 필터링이 필요없을 경우 filter class 관련된 부분을 삭제하면 됨
   <encoder>
     <pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern> 해당 패턴 네이밍으로 현재 로그가 기록됨
   </encoder>
   <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
     <fileNamePattern>./was-logs/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern> 해당 패턴 네이밍으로 이전 파일이 기록됨
     <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
       <maxFileSize>100MB</maxFileSize> 한 파일의 최대 용량
     </timeBasedFileNamingAndTriggeringPolicy>
     <maxHistory>180</maxHistory> 한 파일의 최대 저장 기한
   </rollingPolicy>
 </appender>
[2021-08-07 20:03:12:3093][main] INFO org.hibernate.SQL - drop table if exists user CASCADE 
  • 해당 레벨의 로그를 콘솔에 출력한다.
<if condition='p("spring.profiles.active").equals("test") || p("spring.profiles.active").equals("local") || p("spring.profiles.active").equals("dev")'>
    <then>
        <root>
            <appender-ref ref="console"/>
        </root>
    </then>
    <else>
        <root>
            <appender-ref ref="app_log"/>
            <appender-ref ref="app_error_log"/>
            <appender-ref ref="lnc-error-log"/>
        </root>
    </else>
</if>

RollingFileAppender

  • RollingFileAppender는 FileAppender를 상속하여 로그 파일을 rollover 한다.

    • RollingFileAppender가 타깃 파일로 log.txt에 로그 메시지를 append 하다가 어느 지정한 조건에 다다르면, 타깃 파일을 다른 파일로 바꿀 수 있다.
  • RollingFileAppender와 함께 동작하는 두 가지 component가 존재

    • RollingPolicy : rollover에 필요한 action
    • TriggeringPolicy : 어느 시점에 rollover가 발생할지 정의
Property NameTypeDescription
appendboolean기본값은 true, true일 경우 기존파일에 이어서 쓰기, false일경우 새로운 파일을 생성하여 쓰기.
encoderEncoder이벤트가 어떤식으로 작성되는지 설정. pattern속성을 통해 패턴 지정이 가능.
fileStringSee FileAppender properties. 타깃 파일이름
rollingPolicyRollingPolicy만들어질 파일 이름설정, minIndex, maxIndex설정 가능, 크기별, 시간별, 크기/시간별 로그파일을 만들 수 있다.
triggeringPolicyTriggeringPolicy새로운 파일을 만들 트리거를 지정, maxFileSize 내부속성을 사용해서 크기가 다 차면 새로운 파일을 생성하도록 설정가능하다.
prudentboolean여러개의 JVM이 하나의 로그파일에 동시작성을 막기위한 lock설정, 성능저하 있을 수 있음. 일반적으로 2개잇아의 JVM이 하나의 로그파일을 같이사용하지 않는다.

Rolling Policies

  • TimeBasedRollingPolicy
    • 가장 잘 알려진 RollingPolicy 종류
      • 시간에 기반하여 rollover 정책을 정의할 수 있으며, 주로 일 또는 월 단위로 rollover 한다.
    • TimeBasedPolicy는 필수적으로 fileNamePattern 속성을 가지게 됨
PropoertyTypeDescription
fileNamePatternString (default %d: yyyy-MM-dd)아카이브 될 로그 파일의 패턴을 정의 %d 문자를 이용해 파일의 적절한 부분을 dateTime 패턴으로 치환
FileAppender의 file 프로퍼티를 통해 활성 로그 파일의 위치와 보관 될 로그 파일의 위치를 분리할 수 있다. file 프로퍼티로 등록된 곳이 활성 로그 파일의 위치가 됨
%d{} 내부의 dateTime 패턴 안에 '/' 또는 '\' 기호는 디렉터리 분리자로 인식함 이를 통해 시간으로 원하는 디렉터리 구조를 구성할 수 있다.
ex) /var/log/%d{yyyy/MM}/myapplication.%d{yyyy-MM-dd}.log
maxHistoryint아카이브에 저장 유지할 로그 파일의 개수을 지정. 예를들어 rollover를 1개월 마다 하며 값을 6으로 지정했다면, 6개월의 히스토리가 남게된다..
다음 월의 파일이 아카이브 될 경우 오래된 파일이 삭제된다.
totalSizeCapint로그 파일 아카이브 저장소의 최대크기를 지정. totalSizeCap을 초과한다면 가장 오래된 파일이 삭제.
cleanHistoryOnStartboolean(default: false) Application이 시작될 때 아카이브된 로그 파일을 모두 삭제함. false로 지정되면 삭제하지 않고 시작된다.
  • fileNamePattern에 명시된 dateTime 패턴의 최소 단위에 따라 rollover 단위가 달라진다.
/foo.%d - default %d는 yyyy-MM-dd임. 매일 자정에 새로운 로그 파일로 rollover 
/foo/%d{yyyy/MM}/bar.txt - 매월 새로운 디렉터리를 만들며 하위에 bar.txt 파일로 rollover
/foo/bar.%d{yyyy-MM-dd_HH-mm} - 매 분 새로운 로그 파일로 rollover
/foo/bar.%d.gz - 매일 새로운 로그 파일로 rollover 하고, 이전 로그파일은 GZIP으로 압축
  • 다시 우리 코드를 보자.
<appender name="meter_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <File>${LOG_PATH}/meter.log</File>
    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/meter_log.%d.gz</fileNamePattern>
        <maxHistory>150</maxHistory>
    </rollingPolicy>
</appender>
  • TimeBasedRollingPolicy를 적용하였으며 file 태그와 fileNamePattern 태그를 함께 사용하고 있다.

    • Application 이 동작중일땐 활성화된 meter.log 로그파일에 쌓지만, 매일 자정이 지나면 meter_log.2022-05-12.gz 와 같은 이름으로 아카이브 됨
  • SizeAndTimeBasedRollingPolicy

    • SizeAndTimeBasedRollingPolicy는 파일의 크기까지 고려한 TimeBasedPolicy를 상속한 RollingPolicy이다.
    • 위의TimeBasedPolicy 속성 중 totalSizeCap을 이용하면 전체 아카이브 된 로그 파일의 크기를 제한할 수 있다.
<configuration>
  <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>mylog.txt</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
       <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
       <maxFileSize>100MB</maxFileSize>    
       <maxHistory>60</maxHistory>
       <totalSizeCap>20GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>


  <root level="DEBUG">
    <appender-ref ref="ROLLING" />
  </root>

</configuration>
  • TimeBasedRollingPolicy와 다른점은 fileNamePattern에서 %i와 %d가 필수적인 토큰이라는 것
    • 또한 maxFileSize라는 태그가 추가되었다.
    • maxFileSize 태그는 각각의 로그파일이 가질 수 있는 최대 크기를 제한

FixedWindowRollingPolicy

  • FixedWindowRollingPolicy는 Fixed Window 알고리즘에 따라 로그 파일의 이름을 지정
    • fileNamePattern은 파일의 이름 패턴을 지정하며 반드시 %i 토큰이 필요
PropertyTypeDescription
minIndexintwindow index의 최소값
maxIndexintwindow index의 최대값
fileNamePatternStringFixedWindowRollingPolicy를 통해 지어지게된 로그 파일 이름입니다. 반드시 window index를 위해 %i 토큰을 반드시 포함하고 있어야 한다.
예를들어 minIndex=1, maxIndex=3일 경우, MyLogFile%i.log의 파일은 MyLogFile1.log MyLogFile2.log, MyLogFile3.log로 지어지게됨
아카이브할 로그 파일을 압축 하고싶다면 .zip이나 .gz을 끝에 붙여 처리할 수 있다.
<configuration>
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>test.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>tests.%i.log.zip</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>5MB</maxFileSize>
    </triggeringPolicy>
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
  </appender>
        
  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
  • rollover가 한번도 일어나지 않았을 때 file 태그의 이름으로 활성화된 로그 파일(test.log)만이 존재한다.
  • rollover가 1번 일어나면, 아카이브된 로그 파일의 이름은 minIndex=1로부터 시작되어 test.1.log.zip으로 만들어지고,
    여전히 활성 로그 파일의 이름은 test.log 이다.
  • rollover가 2번 일어나면, 이전의 test.1.log.zip 파일의 이름이 test.2.log.zip으로 수정되고, 그리고 새로 아카이브 될 로그파일의 이름이 test.1.log.zip으로 지어진다.
  • rollover가 4번 일어나면, 기존에 아카이브 된 로그 파일 test.1.log.zip, test.2.log.zip, test.3.log.zip 중 maxIndex에 해당하는 test.3.log.zip 파일이 삭제된다.
    그리고 각 파일들이 다음 index 파일명으로 renaming 된다.
profile
깨끗한 스케치북 일수록 우아한 그림이 그려지법, 읽기 쉽고, 짧은 코드가 더 아름다운 법.. 또한 프로그래머의 개발은 구현할 프로그래밍이 아닌, 풀어갈 이야기로 써내려가는것.

0개의 댓글