MDC 와 ThreadLocal

Bonjugi·2021년 9월 19일
2
post-thumbnail

MDC 는 로그에 컨텍스트를 남기는 용도로 사용된다.
MDC.put(k,v) , MDC.get(k) 를 이용하여 저장하고 읽을수가 있다.
맵과 같은데 특징은 이 맵이 쓰레드 단위로 생성된다.

MDC 의 사용 예

MDC(Mapped Diagnostic Context) 는 로그에 메타데이터를 맵으로 관리할수 있게 해준다.
log.info("내용"); 라고 적으면 해당 로그는 파일에 "내용" 이라고 단순하게 적어주는것 외에 누구에 의한 요청인지, 어떤 트랜잭션ID 인지 등을 메타데이터로 저장하는 것이다.

필터에서 put 하면

웹프로그램의 가장 앞단인 필터에서 MDC 에 저장하고자 하는 값을 put 하면 된다.
키는 tran_id 이고 값은 그냥 랜덤 문자열 이다.

public class MDCFilter implements Filter {
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
		MDC.set("tran_id", random());
		chain.dofilter(req, res);
		MDC.clear();
	}
}

이후 모든 레이어에서 get 할수 있다.

위에서 MDC.put() 을 이용해 남긴것은 Controller, Service 등 어떤 레이어 에서든 추적할수가 있다.

log.info(MDC.get("tran_id")) 

쓰레드바운드 이기 때문이다.

로그스태시 에서 중요한 정보로 쓸수 있다.

라인별로 로그가 남게되는데 이것을 엮어서 볼때 MDC 의 컨텍스트는 매우 유용하다.
따라서 로그스태시에 다음과 같이 MDC 정보를 함께 전달하면 검색 활용도가 높아진다.

여기까지 MDC 의 용법을 알아봤다.
그런데 한번 put 하면 이후 어디서든 get 할수 있는 메커니즘은ThreadLocal 을 모르고서는 이해할수 없다.

ThreadLocal 이란?

Java 1.2 부터 제공 되었지만 모르는 사람이 많다.

쓰레드별로 Map 을 할당하고 값을 저장하고 읽을수 있다.
쓰레드마다 할당 된다는게 큰 특징이 있다.

개념 설명은 아래 링크에 너무 잘 되어 있어서 패스..

https://javacan.tistory.com/entry/ThreadLocalUsage

특성상 쓰레드 단위로 뭔가를 해야 할때 쓰여진다.
웹프로그램 특성상 요청 1번에 쓰레드 1개 이므로, 쓰레드 내에서만 유용한 값들을 관리하는 ThreadLocal 을 이용하면 요청 내에서만 유효한 값을 만들수 있다.
실제로 MDC 가 ThreadLocal 을 이용하고 있다.

MDC 를 추적하면서 ThreadLocal 과 어떤 관계가 있는지를 추적 해보고자 한다.
코드를 알면 좀 더 잘 쓸수 있지 않을까?

MDC 에서의 ThreadLocal

MDC 어딘가에서 ThreadLocal 을 쓰고 있으니까 이러한 동작이 가능한 것이다.
추적 해 보도록 하자.

put, get 을 추적 해보면 mdcAdapter 가 나온다.

MDC.put() 또는 MDC.get() 코드를 보면 내부에서는 그저 mdcAdapter 에게 위임하고 있다.
mdcAdapter 의 구현체가 중요한 것이다.

org.slf4j.MDC 의 javadoc 에 힌트가 있다.
mdcAdapter 구현체는 3개 이긴 하지만, 일반적으로 LogbackMDCAdapter 가 적용 된다고 보면 된다.
코드를 추적하여 설명할수도 있지만 참고를 위해 javadoc 내용을 첨부 하는걸로 대신한다.

  • 기본 로깅 시스템이 MDC 기능을 제공하는 경우 SLF4J의 MDC, 즉 이 클래스는 기본 시스템의 MDC에 위임합니다.
  • 현재 log4j와 logback이라는 두 가지 로깅 시스템만 MDC 기능을 제공합니다.
  • MDC를 지원하지 않는 java.util.logging의 경우 BasicMDCAdapter가 사용됩니다.
  • 다른 시스템(예: slf4j-simple 및 slf4j-nop)의 경우 NOPMDCAdapter가 사용됩니다.
    따라서 SLF4J 사용자는 log4j, logback 또는 java.util.logging이 있는 상태에서 MDC를 활용할 수 있지만 이러한 시스템을 사용자에 대한 종속성으로 강제하지 않아도 됩니다.
  • MDC에 대한 자세한 내용은 로그백 매뉴얼의 MDC에 대한 장을 참조하십시오.
  • 이 클래스의 모든 메서드는 정적입니다.

LogbackMDCAdapter 구현체에서 ThreadLocal 을 쓰고 있다.

LogbackMDCAdapter 의 멤버변수에서 ThreadLocal 을 쓰고 있는것을 발견했다.

또한 위 javadoc을 읽어보면 다음과 같다.

  • 매핑된 진단 컨텍스트 또는 간단히 MDC는 다른 소스에서 인터리브된 로그 출력을 구별하기 위한 도구입니다.
  • 로그 출력은 일반적으로 서버가 거의 동시에 여러 클라이언트를 처리할 때 인터리브됩니다.
  • MDC는 스레드 단위로 관리됩니다.
  • 자식 스레드는 부모의 매핑된 진단 컨텍스트 복사본을 자동으로 상속합니다.

인터리브 라는 단어가 어렵긴 한데, 대략 메모리를 나눠서 관리하는 기법이라는 용어 정도로 이해하면 될듯 하다.
여기서는 컨텍스트를 쓰레드마다 나눠서 관리 한다고 여기면 된다.

정리

  • ThreadLocal 이 쓰레드 바운드로 동작 하는것임을 알았다.
  • 그러므로 쓰레드를 풀로 돌려서 쓴다면 잘못 사용되지 않게 clear 를 명시적으로 해주는게 좋다.
  • MDC 는 멤버변수로 mdcAdapter 를 두고 put, get 을 위임하며, 이것의 구현체는 LogbackMDCAdapter 이다.
  • LogbackMDCAdapterThreadLocal 을 사용 하고 있음을 코드로 확인했다.
  • 추후 기회가 되면 Transactional 처리도 ThreadLocal 을 쓰고있음을 코드로 확인해 보자.

0개의 댓글