Ответ
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) после завершения операции, чтобы избежать утечки памяти и некорректных данных в последующих операциях, обработанных тем же потоком из пула.