Сохранение пользовательских атрибутов MDC во время обработки исключений в Spring Boot - PullRequest
1 голос
/ 13 марта 2019

Короткая версия (с достаточным количеством деталей)

Как сохранить атрибут MDC, добавленный в doFilter() метод javax.servlet.Filter реализации ...

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    try {
        MDC.put("token", MyToken.random()); // add the MDC attribute related to the current request processing
        chain.doFilter(request, response); // send the request to other filters and the Controller
    } finally {
        MDC.clear(); // MDC attribute must be removed so future tasks executed on the same thread would not log invalid information
    }
}

... во время обработки исключения, если исключение возникает в другом фильтре или в контроллере (вызов chain.doFilter(...)).

В настоящее время, если возникает исключение: будет выполнен блок finally, очищающий MDC, а затем исключение будет выброшено из фильтра. Все журналы во время обработки исключений не будут содержать атрибут MDC.

Длинная версия (слишком подробная)

У меня есть простая реализация Filter для перехвата всех запросов. Он создает только случайную строку символов (токен) для включения во все журналы, связанные с обработкой запроса.

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            MDC.put("token", MyToken.random());
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }

    @Override
    public void destroy() {
    }
}

Последовательность событий будет:

  1. Запрос получен.
  2. Мой doFilter() вызывается, добавляя случайный токен в MDC.
  3. Запрос обрабатывается по телефону chain.doFilter().
  4. Что бы ни случилось (обработка завершена, произошла ошибка), MDC очищается от случайного токена в блоке finally.

Проблема заключается в том, что если ошибка возникает и обрабатывается (например, пользовательской реализацией ErrorController), соответствующие журналы не включают токен:

[2019.03.13 15:00:14.535] token:308...8bf [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet                  : GET "/resource", parameters={}
[2019.03.13 15:00:14.551] token:308...8bf [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet                  : Completed 400 BAD_REQUEST
[2019.03.13 15:00:14.551] token:          [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet                  : "ERROR" dispatch for GET "/error", parameters={}
[2019.03.13 15:00:14.551] token:          [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity myproj.CustomErrorController.handleError(javax.servlet.http.HttpServletRequest)
[2019.03.13 15:00:14.551] token:          [ERROR] 8124 [https-jsse-nio-8443-exec-7] m.CustomErrorController                    : HTTP Error: Bad Request (400)
[2019.03.13 15:00:14.551] token:          [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet                  : Exiting from "ERROR" dispatch, status 400

Блок finally выполняется, когда выдается исключение из Controller, который его обрабатывает, что приводит к очистке MDC.

После этого выполняется обработка ошибок (включая пользовательский ErrorController), что означает, что в соответствующих журналах больше нет токена.

Как добавить пользовательский токен в все журналы, относящиеся к всей обработке запроса от его получения до отправки ответа, включая обработку ошибок? Я хочу, чтобы MDC очищался после того, как поток отправил ответ, как последнее действие. MDC следует очищать независимо от того, что происходит (успешный ответ, исключение, выданное во время обработки ошибок и т. Д.).

Если несколько клиентов одновременно используют службу Rest, журналы могут сильно испортиться. Присвоение уникального токена каждому журналу, созданному в течение всего процесса обработки определенного запроса, значительно упростит отладку.

1 Ответ

0 голосов
/ 22 марта 2019

A стандартный фильтр сервлета выполняется вокруг любого сервлета, включая DispatcherServlet Spring (см., Например, здесь ), но ваш фильтр - Spring составная часть. Поскольку он не использует никаких bean-компонентов Spring, вы можете легко преобразовать его в простой фильтр, то есть фильтр, настроенный в web.xml, как описано на странице, на которую я ссылался.

...