멀티스레드 환경에서 하나의 스레드의 컨텍스트를 추적할 때 사용하는 기능이다.
로깅 프레임워크에서 제공되는 기능이며, 주로 로깅의 목적으로 사용된다.
사실 위 질문은 low-level 코드로 뭐든 구현할 수 있는데, 왜 Java나 Python등 고수준 언어를 사용하냐는 질문과 같다(물론 그 정도 차이는 아니지만).
MDC가 없으면, 개발자가 ThreadLocal에서 직접 데이터를 꺼내와서 로그 메시지에 포함을 시켜줘야 하는 번거로운 작업을 매번 해야한다. MDC는 내부적으로 ThreadLocalMap을 이용해서 이를 로깅과 결합해 자동으로 로그 메시지에 메타정보를 포함시켜준다.
우리가 주로 사용하는 로깅 라이브러리인 SLF4J에서도 해당 기능을 제공한다.
MDC 코드에서 주요한 내용을 살펴보도록 하자.
가장 먼저 MDC 코드를 살펴보면 다음과 같은 내용이 있는데, 이는 SLF4J의 여러 구현체(LogBack, Log4J등)에 따라 설정되는 어댑터이다.
private static MDCAdapter getMDCAdapterGivenByProvider() {
SLF4JServiceProvider provider = LoggerFactory.getProvider();
if(provider != null) {
// If you wish to change the mdc adapter, setting the MDC.MDCAdapter variable might be insufficient.
// Keep in mind that the provider *might* perform additional internal mdcAdapter assignments that
// you would also need to replicate/adapt.
// obtain and attach the MDCAdapter from the provider
final MDCAdapter anAdapter = provider.getMDCAdapter();
emitTemporaryMDCAdapterWarningIfNeeded(provider);
return anAdapter;
} else {
Reporter.error("Failed to find provider.");
Reporter.error("Defaulting to no-operation MDCAdapter implementation.");
return new NOPMDCAdapter();
}
}
다음으로는 주요 메서드를 간략하게 요약한 내용이다.
put(String key, String value): 현재 스레드의 MDC 맵에 key-value 저장get(String key): MDC 맵에서 키에 해당하는 value 조회사실 Map을 기반으로 동작하기 때문에 Map의 메소드와 크게 다를 내용이 없다.
먼저, MDC의 로깅 범위를 설정해주어야 한다. 로깅 범위는 logback 설정 파일에서 설정할 수 있다.
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %X{userId} - %msg%n</pattern>
이렇게 패턴을 등록하여, 원하는 정보들을 로그에 포함시킬 수 있다.
%X{keyName}: 특정 키 값을 포함%X, %mdc: MDC 내 모든 키-값을 포함다음은 이제 MDC에 값을 넣는 부분인데, MDC는 보통 스레드의 요청 별 메타정보를 등록해주기 위해 서버의 가장 앞 부분인 Servlet Filter나 Spring Interceptor 같은 컴포넌트에서 설정해 준다.
@Component
public class MdcFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
try {
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
위의 코드에서 주의할 점은 MDC.clear()이다. MDC는 ThreadLocal을 기반으로 동작하기 때문에, 스레드풀 기반 환경에서는 반환된 스레드의 ThreadLocal에 데이터가 남아있으면 문제가 될 수 있기 때문이다. 따라서, MDC도 스레드 반환 전에 초기화 시켜주는 작업이 필요하다.