본문 바로가기
Java & Spring

Logback 설정 정리

by superuser 2023. 1. 11.

로그 레벨

Error > Warn > Info > Debug > Trace
  • Warn  : 당장 서비스 운영에는 영향이 없지만 주의해야 할 부분
  • Trace : 모든 레벨에 대한 로깅이 추적되므로 개발 단계에서 사용함
  • 별다른 설정을 하지 않을 경우, 로그 레벨의 기본 값은 Info이며, 로그 레벨의 상위 로그만 출력된다.

        ex) Info로 설정할 경우, Errror, Warn, Info 레벨의 로그만 출력됨

로그 설정파일

  • src/main/resource/logback-spring.xml에 작성한다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
	<springProperty scope="context" name="kafka_url" source="server.kafka.bootstrap.servers" defaultValue="127.0.0.1"/>
	<springProperty scope="context" name="kafka_topic" source="server.kafka.topic" defaultValue=""/>
	<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
		<resetJUL>true</resetJUL>
	</contextListener>
	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<charset>utf-8</charset>
			<pattern>[%d{yyyy-MM-DD HH:mm:ss.SSS}][%-5level] \(%F:%L\) - %msg%n</pattern>
		</encoder>
	</appender>
	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${server.log.basedir}/gateway/gateway.log</file>
		<!-- encoder는 인터페이스이며, PatternLayoutEncoder이 default 구현체이다. (생략 가능) -->
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<charset>utf-8</charset>
			<pattern>%d{yyyy-MM-DD HH:mm:ss.SSS, Asia/Seoul} %5p [%X{X-B3-TraceId:-}] - %msg%n</pattern>
		</encoder>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>${server.log.basedir}/gateway/backup/gateway.%d{yyyy-MM-dd, Asia/Seoul}.%i.log</fileNamePattern>
			<maxFileSize>10MB</maxFileSize>
			<maxHistory>30</maxHistory>
			<totalSizeCap>30GB</totalSizeCap>
		</rollingPolicy>
	</appender>
	<appender name="KafkaStaticAppender" class="com.github.danielwegener.logback.kafka.KafkaAppender">
		<encoder>
			<charset>utf-8</charset>
			<pattern>%msg%n</pattern>
		</encoder>
		<topic>${kafka_topic}</topic>
		<keyingStrategy class="com.github.danielwegener.logback.kafka.keying.NoKeyKeyingStrategy" />
		<deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
		<producerConfig>acks=0</producerConfig>
		<producerConfig>bootstrap.servers=${kafka_url}</producerConfig>
	</appender>
	<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
		<queueSize>512</queueSize>
		<appender-ref ref="FILE" />
		<!--<neverBlock>false</neverBlock>-->
	</appender>
	<appender name="KAFKA_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
		<queueSize>512</queueSize>
		<appender-ref ref="KafkaStaticAppender" />
		<!--<neverBlock>false</neverBlock>-->
	</appender>
	<logger name = "com.example.MonitoringLogFilter" additivity = "false">
		<level value = "INFO" />
		<appender-ref ref="FILE" />
		<appender-ref ref="KAFKA_ASYNC" />
	</logger>
	<logger name = "org.springframework" level = "ERROR" />
	<springProfile name = "local">
		<root level = "INFO">
			<appender-ref ref = "CONSOLE" />
		</root>
	</springProfile>
	<springProfile name = "!local">
		<root level = "INFO">
			<appender-ref ref = "ASYNC" />
		</root>
	</springProfile>
</configuration>

 

<configuration>

  • <configuration> ~ </configuration> 요소 내에 logback 설정을 작성한다.
  • 주요 옵션
scan : true로 설정할 경우,  Application이 동작 중일 때 재시작 없이 설정 파일을 수정할 수 있다.
scanPeriod : scan 주기를 지정한다. (생략할 경우 scanPeriod default 값은 1분)

<springProperty>

  • logback 안에서 사용을 위해 스프링 Environment 으로부터 속성을 드러나도록(expose) 한다.
  • logback-spring.xml의 source(server.kafka.bootstrap.servers)가 application.yml의 설정 값과 대응된다.

/* logback-spring.xml  */

<springProperty scope="context" name="kafka_url" source="server.kafka.bootstrap.servers" defaultValue="127.0.0.1"/>

 

/* application.yml */

server:
   kafka:
      bootstrap:
         servers: 127.0.0.1:9092

<contextListener>

<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
	<resetJUL>true</resetJUL>
</contextListener>
  • SLF4J(Simple Logging Facade)에서 logback과 함께 사용하는 경우, JUL(Java Util Logger)을 재설정해야 한다.

<Appender>

  • 로그를 출력 할 위치, 출력 형식 등을 설정할 수 있다.

ConsoleAppender

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <charset>utf-8</charset>
        <pattern>[%d{yyyy-MM-DD HH:mm:ss.SSS}][%-5level] \(%F:%L\) - %msg%n</pattern>
    </encoder>
</appender>
  • 로그를 콘솔에 출력되도록 한다.
  • 주요 요소
<encoder> : Appender에 포함되어 사용자가 지정한 형식으로 표현 될 로그메시지를 변환하는 역할을 담당
※ encoder는 인터페이스이며, PatternLayoutEncoder이 default 구현체이며, class 옵션은 생략가능하다.
<pattern> :  시간과 레벨 등의 방식을 지정

log pattern

%logger: 패키지 포함 클래스 정보
%logger{0}: 패키지를 제외한 클래스 이름만 출력
%logger{length}: Logger name을 축약할 수 있음. {length}는 최대 자리 수, ex)logger{35}
%-5level: 로그 레벨, -5는 출력의 고정폭 값(5글자), 로깅레벨이i nfo일 경우 빈칸 하나 추가
%d: 로그 기록시간 출력
%d{yyyy-MM-dd-HH:mm:ss:sss}: %d는 date를 의미하며 중괄호에 들어간 문자열은 dateformat을 의미. 따라서 [2021-07-12 12:42:78]과 같은 날짜가 로그에 출력됨.
%p: 로깅 레벨 출력
%F: 로깅이 발생한 프로그램 파일명 출력
%M: 로깅일 발생한 메소드의 명 출력
%line: 로깅이 발생한 호출지의 라인
%L: 로깅이 발생한 호출지의 라인
%msg: - 로그 메시지 (=%message)
%n: 줄바꿈(new line)
%r : 애플리케이션 시작 이후부터 로깅이 발생한 시점까지의 시간(ms)
%x : 로깅이 발생한 Thread와 관련된 NDC(nested diagnostic context)를 출력한다.
%X : 로깅이 발생한 Thread와 관련된 MDC(mapped diagnostic context)를 출력한다.

- 기호 : 왼쪽 정렬을 의미한다. (default : 오른쪽 정렬)

 FileAppender (RollingFileAppender)

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${server.log.basedir}/gateway/gateway.log</file>
    <!-- encoder는 인터페이스이며, PatternLayoutEncoder이 default 구현체이다. (생략 가능) -->
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <charset>utf-8</charset>
        <pattern>%d{yyyy-MM-DD HH:mm:ss.SSS, Asia/Seoul} %5p [%X{X-B3-TraceId:-}] - %msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${server.log.basedir}/gateway/backup/gateway.%d{yyyy-MM-dd, Asia/Seoul}.%i.log</fileNamePattern>
        <maxFileSize>10MB</maxFileSize>
        <maxHistory>30</maxHistory>
        <totalSizeCap>30GB</totalSizeCap>
    </rollingPolicy>
</appender>
  • FileAppender는 로그를 지정된 File에 기록한다.
  • RollingFileAppender는 FileAppender로 부터 상속받은 Appender로 날짜, 최대 용량 등을 설정하여 지정한 파일명 패턴에 따라 로그가 다른 파일에 기록되도록 한다.

RollingPolicy (SizeAndTimeBasedRollingPolicy)

  • rollover 발생 시 RollingFileAppender의 행동
  • rollover : 로그 메시지를 append 하다가 어느 지정한 조건에 다다르면, 타겟 파일을 다른 파일로 바꿈
  • 주요 요소
fileNamePattern : 로그 파일 이름으로, 반드시 window index를 위해 %i 토큰을 반드시 포함하고 있어야 한다.
ex) MyLogFile%i.log의 파일은 MyLogFile1.log MyLogFile2.log, MyLogFile3.log... 이런 식으로 지어진다.
timeBasedFileNamingAndTriggeringPolicy
maxFileSize : 각각의 로그파일이 가질 수 있는 최대 크기를 제한
maxHistory : 설정한 일 이상이 지난 로그를 오래된 순서대로 지워준다.
totalSizeCap : 로그 파일 아카이브 저장소의 최대크기를 지정한다.
※ totalSizeCap은 maxHistory를 설정한 경우에만 동작한다. maxHistory 조건이 1순위로 처리되고 totalSizeCap 조건이 처리된다.

TraceId를 출력하기 위한 방안

OpenTracing
여러개의 마이크로 서비스 간에 통신이 발생하는 과정에서 로그를 확인하기 어려운 문제를 해결하기 위한 방법으로 OpenTracing이 있다.
OpenTracing은 간단히 말해 애플리케이션 간 분산 추적을 위한 표준으로, Zipkin은 OpenTracing의 구현체이다.

Zipkin
B3-Propagation을 통해서 OpenTracing을 구현하고 있다. B3-Propagation은 간단히 말해 'X-B3-'으로 시작하는 X-B3-TraceId와 X-B3-ParentSpanId, X-B3-SpanId, X-B3-Sampled, 이 4개 값을 전달하는 것을 통해서 트레이스 정보를 관리한다. 서버에서는 이 값들을 TraceContext에서 관리하는데, Spring 프레임워크와 SLF4J(Simple Logging Facade for Java)를 사용하고 있었기에 MDC(Mapped Diagnostic Context)에서 해당 값을 꺼내서 사용할 수 있다.

Spring Cloud Sleuth
Zipkin의 Brave는 B3-propagation의 Java 구현체이고, Spring Cloud Sleuth는 BraveTracer를 Spring 프레임워크에서 쉽게 사용하기 위한 라이브러리이다. Sleuth는 Slf4j를 이용해 로그를 출력할 시 TraceId와 SpanId 등을 자동으로 출력되도록 할 수 있다. Sleuth은 단순히 의존성만 설정한 상태로도 로그 출력 시 TraceId가 출력된다.

결론
TraceId를 출력하기 위해서 logback-spring.xml 설정파일의 <pattern> 요소 내 %X{X-B3-TraceId:-}를 명시한다.
<pattern>%d{yyyy-MM-DD HH:mm:ss.SSS, Asia/Seoul} %5p [%X{X-B3-TraceId:-}] - %msg%n</pattern>

KafkaAppender

<appender name="KafkaStaticAppender" class="com.github.danielwegener.logback.kafka.KafkaAppender">
	<encoder>
		<charset>utf-8</charset>
		<pattern>%msg%n</pattern>
	</encoder>
	<topic>${kafka_topic}</topic>
	<keyingStrategy class="com.github.danielwegener.logback.kafka.keying.NoKeyKeyingStrategy" />
	<deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
	<producerConfig>acks=0</producerConfig>
	<producerConfig>bootstrap.servers=${kafka_url}</producerConfig>
</appender>
  • 애플리케이션에서 발생한 로그 이벤트를 아파치 카프카를 통하여 Publish한다.
  • 주요 요소
topic : 발행할 토픽에 대한 값
producerConfig : 카프카 프로듀서의 properties를 설정을 위한 'key=value'값
※ 카프카 브로커의 bootstrap-servers은 필수적이므로 이를 추가해줘야된다.
acks=0 : Producer는 자신이 보낸 메시지에 대한 ack(응답문자)를 기다리지 않는다. 메시지가 손실될 수 있지만, 높은 처리량을 얻을 수 있다.

AsyncAppender

<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
	<queueSize>512</queueSize>
	<appender-ref ref="FILE" />
	<!--<neverBlock>false</neverBlock>-->
</appender>
  • AsyncAppeder가 생성한 worker thread가 큐에서 메시지 하나를 꺼내서 AsyncAppender에 연결된 appender에 넘겨주면(dispatch) appender가 로그를 write 하는 작업을 알아서(비동기)로 처리한다.
  • AsyncAppender는 큐에 있는 쓰기 대기상태의 로그 메시지를 꺼내오는 event dispatcher 역할이라서 실제 로그를 남기는 건 appender를 참조시켜줘야 한다. (appender-ref)
  • AsyncAppender는 발생한 로그를 BlockingQueue에 보관한다.
  • 큐의 용량이 80%가 차면 TRACE, DEBUG, INFO 수준의 로그는 큐에서 제거한다. (WARN, ERROR만 남음)
  • 서버가 멈추거나 재배포될 때는 maxFlushTime 만큼 큐에 남아있던 걸 처리하고 끝난다. (maxFlushTime을 0으로 하면 queue를 모두 처리할 때까지 기다리게 된다.)
  • 주요 요소
queueSize : BlockingQueue 사이즈 (default = 256)
includeCallerData : 로그를 남길때 현재 클래스에 어느 라인 수에서 로그가 남기고 있는지 알 수 있다. 해당 옵션을 끄게 되면 성능 향상을 가져올 수 있다. 대신에 로그가 어디서 찍히는지 추적하기가 어려워지는 문제가 있다.
neverBlock : 큐가 다 찼을 때, 메시지를 넣을지 말지 결정한다. 로그의 버퍼가 꽉차서 application이 blocking되지 않게 하기 위해서 사용한다. (default : false)

<logger>

<logger name = "com.example.MonitoringLogFilter" additivity = "false">
    <level value = "INFO" />
    <appender-ref ref="FILE" />
    <appender-ref ref="KAFKA_ASYNC" />
</logger>
<logger name = "org.springframework" level = "ERROR" />
  • 실제 로깅을 수행하는 구성요소
  • 각 소스로부터 입력받은 로깅 메시지는 로그 레벨에 따라 Appender로 전달된다.
  • 기본적으로 최상위 로거인 Root Logger를 설정해 주어야한다.
  • name에 정의하는 패키지 범위를 설정 할 수 있다. 예를들어 name을 "org.springframework", level을 "ERROR"로 지정하면, "org.springframework" 패키지는 error가 발생할 경우에만 로그가 찍힌다.
  • 주요 옵션
additivity : false로 설정된 경우, 로깅이 발생할 때 상위 로거의 appender로 전달되지 않는다.
ex) com.example.MonitoringLogFilter의 additivity는 false이기 때문에, Root Logger에는 전달되지 않는다.
  • 주요 요소
appender-ref : 위에 정의한 Appender 의 참조를 지정 한다. Root, Logger 태그 안에서만 사용가능

<root>

<root level = "INFO"> <appender-ref ref = "ASYNC" /> </root>
  • 일반적인 로그 정책에 대해 정의를 할 수 있으며, 반드시 한개를 정의해야한다.
  • Root Logger의 이름은 이미 "ROOT"로 정해져 있기에 name 속성을 포함하지 않는다.
  • Root Logger의 level이 "OFF" 혹은 "ERROR"이더라도, 하위 로거의 level이 INFO라면, 하위 로거는 INFO level로 지정된다.

Profile별 환경 설정

<springProfile name = "local">
    <root level = "INFO">
        <appender-ref ref = "CONSOLE" />
    </root>
</springProfile>
<springProfile name = "!local">
    <root level = "INFO">
        <appender-ref ref = "ASYNC" />
    </root>
</springProfile>
  • spring.profiles.active에서 지정한 profile에 따라 조건들을 추가할 수 있다.

 

Reference

https://ckddn9496.tistory.com/category/Spring/Logback

https://livenow14.tistory.com/64

https://mr-spock.tistory.com/60

댓글