Spring Boot에 Log4j2와 MDC 적용해서 쓰레드별 식별 가능한 로그 남기기
Spring Boot: 3.1.0
스프링 부트에서는 Logback
이 기본 로거로 설정되어 있다.
spring-boot-starter
모듈은 spring-boot-starter-logging
모듈을 포함하고 있고 spring-boot-starter-logging
에는 SLF4J
와 구현체인 Logback
를 포함하고 있다.
Logback
보다 퍼포먼스가 더 좋은 Log4j2
를 적용해보자.
dependencies {
// ...
// Log4j2
implementation("org.springframework.boot:spring-boot-starter-log4j2")
modules {
module("org.springframework.boot:spring-boot-starter-logging") {
replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
}
}
}
spring-boot-starter-logging
모듈을 spring-boot-starter-log4j2
로 교체env:
root-path: .
Log4j2
설정에서 스프링 설정 파일의 값을 사용할 수 있어서 굳이 추가해 봤다.<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Properties>
<!-- 로그 파일 디렉토리 경로 -->
<Property name="LOG_FILE_PATH">${spring:env.root-path}/logs</Property>
<!-- 로그 파일 이름 -->
<property name="LOG_FILE_NAME" value="${LOG_FILE_PATH}/app.log"/>
<!-- 로그 파일 패턴 -->
<property name="LOG_FILE_PATTERN" value="${LOG_FILE_PATH}/app_%d{yyyy-MM-dd}_%i.log.gz"/>
<!-- 로그 패턴 -->
<property name="LOG_PATTERN"
value="[%d{yyyy.MM.dd HH:mm:ss.SSS}] %5level - [%10.10t] %-40.40logger{39} : %msg %n"/>
</Properties>
<Appenders>
<Console name="STDOUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<RollingFile
name="RollingFile"
fileName="${LOG_FILE_NAME}"
filePattern="${LOG_FILE_PATTERN}">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="256 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<SpringProfile name="!prod">
<AppenderRef ref="STDOUT"/>
</SpringProfile>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
${spring:env.root-path}
처럼 사용하면 스프링 설정 파일에서 properties를 읽어 올 수 있다.멀티 쓰레드 환경에서 MDC를 적용하여 쓰레드별로 식별 가능한 로그 남기기
서블릿에 필터를 추가하여 MDC를 적용해보자.
package com.example.demo;
import jakarta.servlet.*;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.UUID;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE) // 최고 우선 순위
public class MDCFilter implements Filter {
private static final String MDC_KEY = "trx_id";
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
final UUID uuid = UUID.randomUUID();
MDC.put(MDC_KEY, uuid.toString());
chain.doFilter(request, response);
// MDC.remove(MDC_KEY);
MDC.clear();
}
}
slf4j.MDC
은 쓰레드 로컬을 사용하기 때문에 요청 종료 전에 쓰레드 로컬을 비워줘야 한다.<Configuration>
<Properties>
<!-- ... -->
<!-- 로그 패턴 -->
<property name="LOG_PATTERN"
value="[%d{yyyy.MM.dd HH:mm:ss.SSS}] %5level - [%10.10t] [%.8equals{%X{trx_id}}{}{ TRX_ID }] %-40.40logger{39} : %msg %n"/>
<!-- ... -->
</Configuration>
LOG_PATTERN
의 value를 위와 같이 변경package com.example.demo;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Log4j2
@RestController
@RequestMapping("hello")
public class HelloController {
@GetMapping()
public void hello() throws InterruptedException {
log.info("hello - start");
Thread.sleep(1000);
log.info("hello - end");
}
}