spring / logback - регистрирует тело http-запроса только для ошибок - PullRequest
0 голосов
/ 25 февраля 2020

Я использую MDC внутри перехватчика для добавления полей, связанных с запросом http, в мою запись в журнал. Теперь я хочу зарегистрировать тело запроса в любом журнале уровня error или fatal и / или для исключений. Я совершенно новичок в MDC, spring или java в целом.

Раздел провайдеров моего logback-spring.xml выглядит в настоящее время так:

<providers>
    <timestamp>
        <!-- <fieldName>timestamp</fieldName> -->
        <timeZone>UTC</timeZone>
    </timestamp>
    <version/>
    <nestedField>
        <fieldName>log</fieldName>
        <providers>
            <logLevel />
        </providers>
    </nestedField>
    <message/>
    <loggerName/>
    <threadName/>
    <context/>
    <pattern>
        <omitEmptyFields>true</omitEmptyFields>
        <pattern>
            {
                "trace": {
                    "id": "%mdc{X-B3-TraceId}",
                    "span_id": "%mdc{X-B3-SpanId}",
                    "parent_span_id": "%mdc{X-B3-ParentSpanId}",
                    "exportable": "%mdc{X-Span-Export}"
                }
            }
        </pattern>
    </pattern>
    <mdc>
        <excludeMdcKeyName>traceId</excludeMdcKeyName>
        <excludeMdcKeyName>spanId</excludeMdcKeyName>
        <excludeMdcKeyName>parentId</excludeMdcKeyName>
        <excludeMdcKeyName>spanExportable</excludeMdcKeyName>
        <excludeMdcKeyName>X-RequestId</excludeMdcKeyName>
        <excludeMdcKeyName>X-B3-TraceId</excludeMdcKeyName>
        <excludeMdcKeyName>X-B3-SpanId</excludeMdcKeyName>
        <excludeMdcKeyName>X-B3-ParentSpanId</excludeMdcKeyName>
        <excludeMdcKeyName>X-B3-Sampled</excludeMdcKeyName>
        <excludeMdcKeyName>X-B3-Flags</excludeMdcKeyName>
        <excludeMdcKeyName>B3</excludeMdcKeyName>
        <excludeMdcKeyName>X-Span-Export</excludeMdcKeyName>
    </mdc>
    <stackTrace/>
</providers>
```

1 Ответ

1 голос
/ 28 февраля 2020

Это будет небольшой совет, чем готовое решение, но я думаю, что решение может быть не столь очевидным.

В общем случае, если вы читаете полезную нагрузку один раз из запроса, из InputStream из HttpRequest , это может больше не быть доступным для остальной части весеннего заявления. (Spring, вероятно, делает (или может делать) прокси-объект для хранения полезной нагрузки запроса чтения из InputStream для доступа к нему несколько раз).

Я думаю, вы можете создать HTTP-фильтр и установить MDC context после обработка HTTP-запроса - но в этом случае вы можете регистрировать сообщение об ошибке только в теле после обработки этого запроса, но не во время (когда вы регистрируете сообщения, как правило, вы не знаете, закончится ли этот запрос http 500 или нет).

Если мы говорим о Spring, у вас здесь есть класс абстрактного фильтра, который может вам помочь: org.springframework.web.filter.AbstractRequestLoggingFilter

Вот простой фильтр, созданный мной (но не проверенный) на основе источника AbstractRequestLoggingFilter

import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

@Component
public class TestFilter extends OncePerRequestFilter {

    private static final int MAX_PAYLOAD_LENGTH = 50_000;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest requestToUse = request;

        if (!(request instanceof ContentCachingRequestWrapper)) {
            requestToUse = new ContentCachingRequestWrapper(request, MAX_PAYLOAD_LENGTH);
        }

        try {
            filterChain.doFilter(requestToUse, response);
        } catch (RuntimeException | ServletException | IOException e) {
            MDC.put("request-payload", convertRequestPayloadToString(request));
            // You can also log your exception here
            throw e;
        } finally {
            if (response.getStatus() != 200) {
                MDC.put("request-payload", convertRequestPayloadToString(request));
            }
        }
    }

    private String convertRequestPayloadToString(HttpServletRequest request) {
        ContentCachingRequestWrapper wrapper =
            WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        StringBuilder sb = new StringBuilder();
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                int length = Math.min(buf.length, MAX_PAYLOAD_LENGTH);
                String payload;
                try {
                    payload = new String(buf, 0, length, wrapper.getCharacterEncoding());
                } catch (UnsupportedEncodingException ex) {
                    payload = "[unknown]";
                }
                sb.append(payload);
            }
        }
        return sb.toString();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...