Что такое MDC в контексте логирования в Java и для чего он используется?

«Что такое MDC в контексте логирования в Java и для чего он используется?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

MDC (Mapped Diagnostic Context) — это механизм, предоставляемый фреймворками логирования (SLF4J/Logback, Log4j 2), для прикрепления контекстной информации ко всем log-сообщениям, создаваемым в рамках текущего потока выполнения (ThreadLocal).

Основное назначение: Сквозная идентификация запросов или операций в логах, что критически важно для отладки распределенных систем.

Типичные сценарии использования:

  • ID запроса (requestId): Для группировки всех логов, относящихся к одному HTTP-запросу.
  • ID пользователя (userId): Для отслеживания действий конкретного пользователя.
  • ID сессии (sessionId): Для анализа поведения в рамках сессии.
  • ID транзакции (transactionId): В batch-процессах или финансовых операциях.

Пример использования в веб-приложении (с использованием фильтра):

import org.slf4j.MDC;
import javax.servlet.*;
import java.io.IOException;
import java.util.UUID;

public class MdcLoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            // Помещаем уникальный ID запроса в MDC
            MDC.put("requestId", UUID.randomUUID().toString());
            MDC.put("userIp", request.getRemoteAddr());
            // Все логи внутри цепочки фильтров и обработки запроса будут содержать эти поля
            chain.doFilter(request, response);
        } finally {
            // Очищаем контекст после обработки запроса (важно!)
            MDC.clear();
        }
    }
}

Конфигурация в logback.xml для вывода значений MDC:

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss} [%thread] [%X{requestId}] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

Вывод в лог: 14:25:03 [http-nio-8080-exec-1] [abc-123-def] INFO c.example.MyController - Processing request

Важные особенности:

  • ThreadLocal: Контекст привязан к потоку. При ручном создании потоков или использовании пулов (например, ExecutorService) MDC не копируется автоматически. Для передачи контекста нужно использовать MDC.getCopyOfContextMap() и MDC.setContextMap().
  • Очистка: Крайне важно очищать MDC (например, в блоке finally) после завершения операции, чтобы избежать утечки памяти и некорректных данных в последующих операциях, обработанных тем же потоком из пула.