Log/Logback Manual

Chapter 4. logback appenders

우라릭 2022. 10. 2. 20:53

본 문서는 https://logback.qos.ch/manual/introduction.html을 번역/요약/정리/사견 추가한 글입니다.

 

1. Appender가 뭘까?

Logback에선 로깅 이벤트를 쓰는 작업을 담당하는 것을 appender라고 합니다. Appender는 반드시 ch.qos.logback.core.Appender 인터페이스를 구현해야 합니다.

package ch.qos.logback.core;
  
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.FilterAttachable;
import ch.qos.logback.core.spi.LifeCycle;
  

public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {

  public String getName();
  public void setName(String name);
  void doAppend(E event);
  
}

이 때, 타입 E는 logback 모듈에 따라 달라질 수 있다고 합니다. logback-classic 모듈의 경우는 ILoggingEvent이고 logback-access의 경우는 AccessEvent입니다. 

2. AppenderBase

ch.qos.logback.core.AppenderBase는 Appender 인터페이스를 구현하는 추상 클래스입니다. 이 클래스는 모든 appender에서 공유되는 기본 기능을 제공합니다. 이 클래스는 doAppend()를 구현하는데 synchronized 키워드를 붙였습니다.  synchronized를 붙이지 않는 버전인 UnsynchronizedAppenderBase도 같은 패키지에 있습니다. 

구현된 메소드의 코드를 보면 started가 true일 때만 로깅을 하고 그렇지 않으면 경고 메시지를 남기도 메소드를 중단시키는 것응ㄹ 볼 수 있습니다. started가 false일 때는 appender가 중단(stop)됐거나 시작에 실패한 경우입니다. 예를 들어, appender에서 필요로 하는 속성을 가져올 수 없다던가..

3. Logback-core

logback-core가 제공하는 즉시 사용 가능한 Appender를 살펴 보자.

3.1. OutputStreamAppender

이벤트를 OutputStream에 보냅니다. 사용자는 일반적으로 OutputStream 타입이 문자열로 쉽게 매핑되지 않기 때문에 이 클래스의 객체를 직접적으로 이용하진 않습니다. 설정 파일에서 OutputStream 오브젝트를 명시할 방법이 없기 때문입니다. 대신 OutputStreamAppender는 ConsoleAppender, FileAppender, RollingFilAppender의 슈퍼 클래스입니다. 

3.2. ConsoleAppender

이름에서도 알 수 있듯이 ConsoleAppender는 로그를 System.out이나 System.err로 보냅니다. 이 클래스는 사용자가 명시하는 encoder를 이용해서 로깅 이벤트를 포맷팅합니다. 

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
  </configuration>

3.3. FileAppender

이 클래스는 로그 이벤트를 파일에 씁니다. file 속성을 이용해 파일 이름을 명시합니다. 이미 파일이 있다면 이어쓸 것인지 처음부터 쓸 것인지 정할수도 있습니다.

<configuration>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>testFile.log</file>
    <append>true</append>
    <!-- set immediateFlush to false for much higher logging throughput -->
    <immediateFlush>true</immediateFlush>
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
  </appender>
        
  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

(참고) 시간 표현 timestamp

<timestamp> 요소를 이용해 시간 패턴을 정해줄 수도 있습니다.

<configuration>

  <!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
       the key "bySecond" into the logger context. This value will be
       available to all subsequent configuration elements. -->
  <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>log-${bySecond}.txt</file>
    ...
    ...

datePattern의 형식은 SimpleDateFormat을 따릅니다.

3.4. RollingFileAppender

RollingFileAppender는 로그 파일을 주기적으로 변경하는 방식으로 FileAppender의 기능을  확장합니다. 예를 들어, log.txt에 로그를 적다가 어떤 조건을 만족하면 다른 곳에 적는 식으로...

두 가지 중요한 sub-components는 RollingPolicy와 TriggeringPolicy입니다. 

3.4.1. TimeBasedRollingPolicy

아마 가장 인기 있는 rolling policy일 거에용. 이 클래스는 rolling policy를 시간에 기반해 정의합니다. 시간이 트리거 역할을 하기 때문에 TimeBasedRollingPolicy는 triggering policy도 포함합니다. 이 클래스를 사용하기 위해선 fileNamePattern이라는 필수 속성이 필요합니다. fileNamePattern이 .gz이나 .zip으로 끝난다면 자동 압축도 지원합니다. FilenamePattern은 생각보다 문서에서 설명이 긴데 자세한 설명은 생략하고 예제만 첨부한 후 넘어가겠습니다.

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logFile.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- daily rollover -->
      <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>

      <!-- keep 30 days' worth of history capped at 3GB total size -->
      <maxHistory>30</maxHistory>
      <totalSizeCap>3GB</totalSizeCap>

    </rollingPolicy>

    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
  </appender> 

  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

30일 동안의 로그 파일을 최대 3기가까지만 저장하는 설정 코드입니다. fileNamePattern에서 일 단위 rollover를 했기 때문에 maxHistory가 일 단위가 됩니다. 만약 월단위라면 30이 30개월을 뜻할 것입니다.

3.4.2. SizeAndTimeBasedRollingPolicy

시간에 더해 일정 크기 이상이 되면 다시 파일을 나누는 방식입니다.

3.4.3. FixedWindowRollingPolicy

window 크기만큼 파일을 생성하고 계속 그걸 돌려씁니다. 예를 들어, minIndex = 1, maxIndex = 3이고 fileNamePattern이 foo%i.log라면 triggeringPolicy에 따라 오래된 순으로 foo1.log, foo2.log, foo3.log를 계속 교체하면서 로그를 씁니다.

3.5. triggering policy 소개

TriggeringPolicy 인터페이스는 isTriggeringEvent라는 한 개의 메소드만 가집니다. 이를 통해 rollover가 일어날지 말지 정합니다.

TimeBasedRollingPolicy의 triggeringPolicy말고 다른 예는 SizeBasedTriggeringPolicy입니다. 말그대로 현재 파일 크기를 기반으로 rolling을 합니다.

4. Logback-classic

Logback-classic에서 로깅 이벤트는 모두 ILoggingEvent입니다. 이를 처리하는 appender는 어떤게 있을까요???

4.1. SocketAppender와 SSLSocketAppender

지금까지 소개한 appender는 모두 로컬 자원에 로그를 쓰는 것이었다면 이것은 remote entity에게 로그를 보냅니다. 직렬화된 실제 이벤트 타입은 ILoggingEvent를 구현한 LoggingEventV0입니다. 

SockerAppender는 layout을 가지지 않습니다. 이벤트를 직렬화해 그대로 원격 서버로 보내기 때문입니다. 그리고 원격 서버가 다운되거나 메시지를 받지 못하는 상태라면 로그가 그대로 드랍됩니다. 

4.2. ServerSocketAppender와 SSLServerSocketAppender

이전의 SocketAppender, SSLSocketAppender는 특정 상황에서는 원격 서버와 연결을 수립할 수 없는 상황에서 적절하지 않습니다. ServerSocketAppender는 로깅 이벤트를 여러 TCP 클라이언트로 분배합니다. 연결된 클라이언트가 없다면 로깅 이벤트는 버려집니다. 

4.3. SMTPAppender

SMTPAppender는 고정 크기의 버퍼에 로깅 이벤트를 차곡차곡 쌓아놨다가 유저가 특정 이벤트를 날리면 이메일로 버퍼의 컨텐츠들을 보내줍니다. 이메일 전송은 비동기적으로 일어납니다. 기본 설정으로 이메일 전송은 ERROR 레벨의 로깅 이벤트로 트리거할 수 있습니다. 

4.4. DBAppender

DBAppender는 3개의 DB 테이블(logging_event, logging_event_property, logging_event_exception)에 로깅 이벤트를 넣습니다. 

4.5. SyslogAppender

syslog 프로토콜을 이용해 메시지를 보낸다고 합니다.

4.6. SiftingAppender

어떤 특정 런타임 속성에 따라 로그를 분리할 수 있게 해줍니다. 예를 들어, 유저 세션에 따라 로그를 분리해 유저마다 다른 로그 파일에 로그를 쓸 수 있습니다.

로그를 어디다 쓸건지는 중첩 appender로 정할 수 있습니다.

<configuration>

  <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    <!-- in the absence of the class attribute, it is assumed that the
         desired discriminator type is
         ch.qos.logback.classic.sift.MDCBasedDiscriminator -->
    <discriminator>
      <key>userid</key>
      <defaultValue>unknown</defaultValue>
    </discriminator>
    <sift>
      <appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender">
        <file>${userid}.log</file>
        <append>false</append>
        <layout class="ch.qos.logback.classic.PatternLayout">
          <pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</pattern>
        </layout>
      </appender>
    </sift>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="SIFT" />
  </root>
</configuration>

4.7. AsyncAppender

AsyncAppender는 ILoggingEvent 로그를 비동기적으로 처리할 수 있게 해줍니다.

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myapp.log</file>
    <encoder>
      <pattern>%logger{35} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
  </appender>

  <root level="DEBUG">
    <appender-ref ref="ASYNC" />
  </root>
    </configuration>

4.8. 자신만의 appender 작성하기

AppenderBase를 상속해서 쉽게쉽게 appender를 작성할 수 있습니다. 오직 하나의 메소두 append(Object eventObject)만 구현하면 되요. AppenderBase.doAppend()가 내부적으로 append()를 호출합니다.

5. Logback Access

logback-classic의 appender들과 비슷한 appender들이 상당히 많습니다. 따라서 패스~

 

'Log > Logback Manual' 카테고리의 다른 글

Chapter 6. logback layouts  (0) 2022.10.03
Chapter 5. logback encoders  (0) 2022.10.03
Chapter 3. logback 설정  (0) 2022.10.02
Chapter 2: logback 구조  (0) 2022.10.01
Chapter 1. logback 소개  (0) 2022.09.29