0

I need log HttpRequest body and HttpResponse body in custom format. For example "Send request. Headers: {}, Body: {}, uri: {}"

How can I get request body?

I'm try this:

log.info("Send request. Headers: {}, Body: {}, uri: {}", request, headers, path);
        return webClient.post()
                .uri(path)
                .headers(httpHeaders -> {....})
                .bodyValue(request)...

But in this case, the log does not correspond to the fact of sending the request, but is only part of the reactor pipeline being formed

Can I log HttpRequest body and HttpResponse body in custom format in the moment of send/received this?

  • Does this answer your question? [How to extract response header & status code from Spring 5 WebClient ClientResponse](https://stackoverflow.com/questions/50223891/how-to-extract-response-header-status-code-from-spring-5-webclient-clientrespo) – maio290 Aug 18 '23 at 11:32
  • @maio290 no, I need custom log headers, status and BODY of response. In your link only about headers and status – Влад Савостиков Aug 18 '23 at 11:45

1 Answers1

0

You can use RequestBodyAdviceAdapter & ResponseBodyAdvice for logging request-body, headers etc.
Please find the code snippets.

CustomResponseBodyAdviceAdapter.class.

import com.pmi.hyperface.constants.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {

    @Autowired
    private LoggingService loggingService;

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
            Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
            ServerHttpResponse serverHttpResponse) {

        if (serverHttpRequest instanceof ServletServerHttpRequest
                && serverHttpResponse instanceof ServletServerHttpResponse
                && (!serverHttpRequest.getURI().toString().contains(Constants.ACTUATOR_HEALTH_PATH))) {
            loggingService.logResponse(((ServletServerHttpRequest) serverHttpRequest).getServletRequest(),
                    ((ServletServerHttpResponse) serverHttpResponse).getServletResponse(), o);
        }

        return o;
    }

CustomRequestBodyAdviceAdapter.class.

import com.pmi.hyperface.constants.Constants;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import java.lang.reflect.Type;

@ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {

    @Autowired
    LoggingService loggingService;

    @Autowired
    HttpServletRequest httpServletRequest;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type,
            Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
            Class<? extends HttpMessageConverter<?>> converterType) {
        if (!httpServletRequest.getRequestURI().contains(Constants.ACTUATOR_HEALTH_PATH))
            loggingService.logRequest(httpServletRequest, body);

        return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
}

LoggingServiceImpl.class.

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class LoggingServiceImpl implements LoggingService {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void logRequest(HttpServletRequest httpServletRequest, Object body) {
        StringBuilder stringBuilder = new StringBuilder();
        Map<String, String> parameters = buildParametersMap(httpServletRequest);

        stringBuilder.append("\nREQUEST");
        stringBuilder.append("\nmethod=[").append(httpServletRequest.getMethod()).append("]");
        stringBuilder.append("\npath=[").append(httpServletRequest.getRequestURI()).append("]");
        stringBuilder.append("\nheaders=[").append(buildHeadersMap(httpServletRequest)).append("]");

        if (!parameters.isEmpty()) {
            stringBuilder.append("\nparameters=[").append(parameters).append("] ");
        }

        if (body != null) {
            try {
                stringBuilder.append("\nbody=[" + objectMapper.writeValueAsString(body) + "]");
            } catch (JsonProcessingException e) {
                log.error("Json processing exception while logging request details");
            }
        }

        log.info(stringBuilder.toString());
    }

    @Override
    public void logResponse(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
            Object body) {
        StringBuilder stringBuilder = new StringBuilder();

        stringBuilder.append("\nRESPONSE");
        stringBuilder.append("\nmethod=[").append(httpServletRequest.getMethod()).append("]");
        stringBuilder.append("\npath=[").append(httpServletRequest.getRequestURI()).append("]");
        stringBuilder.append("\nresponseHeaders=[").append(buildHeadersMap(httpServletResponse)).append("]");
        try {
            stringBuilder.append("\nresponseBody=[").append(objectMapper.writeValueAsString(body)).append("] ");
        } catch (JsonProcessingException e) {
            log.error("Json processing exception while logging request details");
        }

        log.info(stringBuilder.toString());
    }

    private Map<String, String> buildParametersMap(HttpServletRequest httpServletRequest) {
        Map<String, String> resultMap = new HashMap<>();
        Enumeration<String> parameterNames = httpServletRequest.getParameterNames();

        while (parameterNames.hasMoreElements()) {
            String key = parameterNames.nextElement();
            String value = httpServletRequest.getParameter(key);
            resultMap.put(key, value);
        }

        return resultMap;
    }

    private Map<String, String> buildHeadersMap(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();

        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }

        return map;
    }

    private Map<String, String> buildHeadersMap(HttpServletResponse response) {
        Map<String, String> map = new HashMap<>();

        Collection<String> headerNames = response.getHeaderNames();
        for (String header : headerNames) {
            map.put(header, response.getHeader(header));
        }

        return map;
    }

}
Ajinkya Karode
  • 157
  • 1
  • 12