Link
Today
Total
10-17 07:26
Archives
관리 메뉴

초보개발자 긍.응.성

Logback - 6. Layouts (1). Custom Layout 본문

Spring/Logback

Logback - 6. Layouts (1). Custom Layout

긍.응.성 2020. 10. 5. 22:58
반응형

logback

logback 홈페이지의 매뉴얼을 읽으며 내용들을 정리한 글입니다.

Layout이란?

Layout은 들어오는 이벤트에 대해 문자열로 변환해주는 역할을 하는 logback의 component입니다. Layout 인터페이스의 format() 메서드는 이벤트를 나타내는 object를 가져가 String 형으로 반환합니다. 하지만, 막상 Layout 인터페이스에는 format 메서드가 없는데 현재 doLayout() 메서드가 이를 대체합니다. Layout 인터페이스는 아래와 같습니다.

public interface Layout<E> extends ContextAware, LifeCycle {

  String doLayout(E event);
  String getFileHeader();
  String getPresentationHeader();
  String getFileFooter();
  String getPresentationFooter();
  String getContentType();
}

Logback-classic의 Layout

Logback-classic 모듈은 ILoggingEvent 타입의 이벤트만 처리합니다. 

Custom Layout 만들기

아래와 같은 로그 메시지를 위해 커스텀 레이아웃을 만든다고 생각해봅시다. 로그 메시지의 내용은 차례로 애플리케이션 시작 후 현재 시간, 로그 level, 호출 쓰레드, 로거 이름, '-', 이벤트 메시지입니다.

10489 DEBUG [main] com.marsupial.Pouch - Hello world.

LayoutBase를 상속함으로써 위의 로그 포맷을 만족하는 커스텀 레이아웃을 만들 수 있습니다. 

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout extends LayoutBase<ILoggingEvent> {

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    sbuf.append(" [");
    sbuf.append(event.getThreadName());
    sbuf.append("] ");
    sbuf.append(event.getLoggerName();
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(CoreConstants.LINE_SEP);
    return sbuf.toString();
  }
}

LayoutBase 클래스는 layout의 시작 또는 중지 여부, 머리글, 바닥글, 콘텐츠 유형과 같은 공통된 상태에 대한 데이터를 관리 가능합니다. 이는 개발자에게 레이아웃의 포맷에만 집중할 수 있도록 합니다. LayoutBase 클래스는 제네릭하여 LayoutBase<ILoggingEvent>를 상속했음을 확인할 수 있습니다.

doLayout 메서드는 StringBuffer를 초기화 하고 이벤트로부터 다양한 필드 데이터를 가져와 append 합니다. 포맷에 맞추어 append 한 StringBuffer는 마지막에 toString 메서드를 통해 String 형으로 반환되게 됩니다.

위의 예시는 이벤트에서 일어날 수 있는 예외처리를 하나도 해주지 않았는데 실제 사용하기 위해 만들 커스텀 Layout에 대해서는 많은 예외처리가 들어가게 됩니다.

Configuration File에 Custom Layout 적용하기

 앞서 제작한 Custom Layout은 하나의 Logback의 component로써 사용 가능합니다.

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout" />
    </encoder>
  </appender>

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

 

Appender에 LayoutWrappingEncoder를 추가하고 내부에 갖고 있을 layout으로 앞서 제작한 layout 클래스를 전달합니다.  

옵션을 가진 Custom Layout 만들기

커스텀 레이아웃을 만들고 레이아웃에 추가적인 옵션 property를 넣어주고 싶다면 필드와 해당 필드에 대한 setter 메서드를 함께 추가합니다.

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout2 extends LayoutBase<ILoggingEvent> {

  String prefix = null;
  boolean printThreadName = true;

  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }

  public void setPrintThreadName(boolean printThreadName) {
    this.printThreadName = printThreadName;
  }

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    if (prefix != null) {
      sbuf.append(prefix + ": ");
    }
    sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    if (printThreadName) {
      sbuf.append(" [");
      sbuf.append(event.getThreadName());
      sbuf.append("] ");
    } else {
      sbuf.append(" ");
    }
    sbuf.append(event.getLoggerName());
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(LINE_SEP);
    return sbuf.toString();
  }
}

MySampleLayout2은 앞서 본 커스텀 레이아웃과 같이 LayoutBase<ILoggingEvent>를 상속합니다. 달라진 점은 추가된 `prefix`, `printThreadName`과 해당 필드에 대한 setter 메서드입니다. doLayout에서는 해당 필드의 유무에 따라 다른 로그 메시지 포맷을 출력하도록 하였습니다.

Configuration 파일에도 추가된 점이 있다면 바로 필드 이름으로 만들어진 태그와 값을 전달하는 것입니다. 필드와 setter를 추가하였다면 configuration 파일에서 해당 태그를 선언하고 값을 넘겨줌으로써 setter 메서드의 동작과 연결시켜 값을 주입할 수 있습니다.

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout2"> 
        <prefix>MyPrefix</prefix>
        <printThreadName>false</printThreadName>
      </layout>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
반응형
Comments