slf4j의 MDC 활용을 통한 호율적 로깅 및 디버깅
SLF4J (Simple Logging Facade for Java)에서 제공하는 MDC (Mapped Diagnostic Context) 클래스는 로깅 정보를 관리하는데 사용되는 유틸리티 클래스 주로 멀티스레드 환경에서 각각의 로그 이벤트가 어떤 상황에서 발생했는지를 추적하는 데 유용
MDC를 사용하면 로그 메시지에 추가적인 컨텍스트 정보를 쉽게 추가 가능
MDC의 주요 기능 !
1. 로그 컨텍스트 설정 : MDC를 사용하여 스레드 로컬(Thread Local)에 로그 컨텍스트를 설정할 수 있습니다. 예를 들어, 현재 사용자 ID, 요청 ID 등의 정보를 MDC에 저장
2. 멀티스레드 환경에서의 사용 : MDC는 각 스레드마다 별도의 컨텍스트 맵을 유지하므로, 멀티스레드 환경에서 여러 스레드가 동시에 로깅을 수행할 때도 각각의 스레드가 정확한 컨텍스트 정보를 로그 메시지에 포함할 수 있고 이것이 가장 큰 장점이라고 생각
3. 로그 메시지 흐름 추적 : 특정 요청이나 트랜잭션의 로그 메시지를 모두 동일한 요청 ID로 묶어 추적할 수 있어, 로그 메시지 흐름을 이해하고 디버깅하기 용이합니다.
MDC는 SLF4J에서 제공하는 API 중 하나이며, 이를 구현하는 실제 백엔드 로깅 프레임워크에서는 다양한 방식으로 MDC를 지원합니다. logback에서는 MDC를 기본적으로 지원하며, 설정 파일을 통해 MDC의 동작 방식을 조정할 수 있습니다.
MDC는 로깅 시스템에서 로그 메시지에 추가적인 컨텍스트 정보를 포함시키고, 멀티스레드 환경에서도 정확한 로그 추적을 지원하는 유용한 도구
Filter와 함께 활용하는 방법
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
public class RequestIdFilter implements Filter {
private static final String REQUEST_ID_HEADER = "X-Request-ID";
private static final String MDC_KEY_REQUEST_ID = "requestId";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 필요한 경우 초기화 코드 작성
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
String requestId = httpRequest.getHeader(REQUEST_ID_HEADER);
// 요청 헤더에서 requestId가 없는 경우 UUID로 새로 생성
if (requestId == null || requestId.isEmpty()) {
requestId = UUID.randomUUID().toString();
}
// MDC에 요청 ID 저장
MDC.put(MDC_KEY_REQUEST_ID, requestId);
try {
// 다음 필터 또는 서블릿으로 요청 전달
filterChain.doFilter(servletRequest, servletResponse);
} finally {
// 요청 처리 후 MDC 초기화
MDC.remove(MDC_KEY_REQUEST_ID);
}
}
@Override
public void destroy() {
// 필요한 경우 리소스 정리 코드 작성
}
}
MDC(Mapped Diagnostic Context)와 Filter를 함께 활용하는 것은 웹 애플리케이션의 로깅 관리에서 매우 유용한 전략입니다. 주로 웹 애플리케이션에서는 각각의 요청이 별도의 스레드에서 처리되며, 이 때 MDC와 Filter를 결합하여 로깅 컨텍스트를 관리하면 몇 가지 중요한 장점이 있습니다:
1. Request ID 또는 세션 ID 추적: Filter를 사용하여 각 요청에 고유한 Request ID나 세션 ID를 생성하고 MDC에 저장할 수 있습니다. 이를 통해 각 로그 메시지에 어떤 요청에서 로그가 발생했는지를 명확하게 추적할 수 있습니다. 예를 들어, HTTP 요청의 Header에서 요청 ID를 추출하거나, 필요에 따라 UUID를 생성하여 MDC에 저장합니다.
2. 멀티스레드 환경에서의 정확한 로깅: 웹 애플리케이션에서는 여러 스레드가 동시에 다양한 요청을 처리할 수 있습니다. 각각의 스레드는 고유한 MDC를 가지므로, Filter를 사용하여 각 요청의 시작과 끝에서 MDC를 초기화하고 해제할 수 있습니다. 이는 로깅 메시지를 올바른 컨텍스트와 함께 기록할 수 있게 합니다.
3. 트랜잭션 흐름의 추적 : Filter를 통해 MDC에 저장된 정보를 사용하면, 특정 요청의 처리 과정에서 발생하는 모든 로그 메시지를 동일한 요청 ID 또는 세션 ID로 묶어서 추적할 수 있습니다. 이는 디버깅과 성능 최적화에 매우 유용
4. 로그 레벨 설정 및 필터링: MDC에 저장된 정보를 기반으로 로그 메시지의 레벨을 동적으로 설정하거나 필터링할 수 있습니다. 예를 들어, 특정 조건에 따라 DEBUG 레벨의 로그를 활성화하거나 비활성화할 수 있습니다.
5. 보안 및 감사 추적: 보안 관련 로그인 경우, 인증된 사용자 ID를 MDC에 저장하여 각 로그 메시지에 포함시킬 수 있습니다. 이는 보안 사고 발생 시 발생 원인을 신속히 파악하고 조치를 취하는 데 도움이 됩니다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class ExampleService {
private static final Logger logger = LoggerFactory.getLogger(ExampleService.class);
private static final String MDC_KEY_REQUEST_ID = "requestId";
public void doSomething() {
// MDC에 저장된 요청 ID 가져오기
String requestId = MDC.get(MDC_KEY_REQUEST_ID);
// 로그 메시지에 요청 ID 추가
logger.info("Handling request with ID: {}", requestId);
// 여기에 실제 비즈니스 로직을 추가할 수 있음
}
}