How to include JSON response body in Spring Boot Actuator's Trace?
Asked Answered
A

4

18

Spring Boot Actuator's Trace does a good job of capturing input/output HTTP params, headers, users, etc. I'd like to expand it to also capture the body of the HTTP response, that way I can have a full view of what is coming in and going out of the the web layer. Looking at the TraceProperties, doesn't look like there is a way to configure response body capturing. Is there a "safe" way to capture the response body without messing up whatever character stream it is sending back?

Aboutface answered 7/3, 2016 at 16:23 Comment(0)
D
8

Recently, I wrote a blog post about customization of Spring Boot Actuator's trace endpoint and while playing with Actuator, I was kinda surprised that response body isn't one of the supported properties to trace.

I thought I may need this feature and came up with a quick solution thanks to Logback's TeeFilter.

To duplicate output stream of the response, I copied and used TeeHttpServletResponse and TeeServletOutputStream without too much examination.

Then, just like I explained in the blog post, extended WebRequestTraceFilter like:

@Component
public class RequestTraceFilter extends WebRequestTraceFilter {

    RequestTraceFilter(TraceRepository repository, TraceProperties properties) {
        super(repository, properties);
    }

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

        filterChain.doFilter(request, teeResponse);

        teeResponse.finish();

        request.setAttribute("responseBody", teeResponse.getOutputBuffer());

        super.doFilterInternal(request, teeResponse, filterChain);
    }

    @Override
    protected Map<String, Object> getTrace(HttpServletRequest request) {
        Map<String, Object> trace = super.getTrace(request);

        byte[] outputBuffer = (byte[]) request.getAttribute("responseBody");

        if (outputBuffer != null) {
            trace.put("responseBody", new String(outputBuffer));
        }

        return trace;
    }
}

Now, you can see responseBody in the JSON trace endpoint serves.

Danged answered 2/9, 2017 at 0:18 Comment(4)
I tried to use this answer. Copied the exact code from above and the two classes TeeHttpServletResponse and TeeServletOutputStream. Now / trace gives me 500 with "Cannot call sendError() after the response has been committed", and any other resources gives me 200 with the body "syntax error". In the spring logs I cannot see any errorsAckack
RequestTraceFilter.java's package should be same with TeeHttpServletResponse , because teeResponse.finish() and teeResponse.getOutputBuffer() are private methods.Bigg
In spring boot 2 the class have been moved to HttpTraceFilterErb
Can we get an update on how to do this in Spring Boot 2.3.xDenote
C
0

From one of the spring maintainers:

Tracing the request and response body has never been supported out of the box. Support for tracing parameters was dropped as, when the request is POSTed form data, it requires reading the entire request body.

https://github.com/spring-projects/spring-boot/issues/12953

Chickamauga answered 8/10, 2020 at 4:32 Comment(0)
G
0

With a webflux reactive stack, it is possible to capture http request and response body using spring-cloud-gateway and inject them into actuator httptrace by defining a custom HttpTraceWebFilter.

See full associated code at https://gist.github.com/gberche-orange/06c26477a313df9d19d20a4e115f079f

This requires quite a bit of duplication, hopefully springboot team will help reduce this duplication, see related https://github.com/spring-projects/spring-boot/issues/23907

Glori answered 26/10, 2020 at 13:39 Comment(0)
B
0

Due to some final methods and private fields it's not possible to add things to a trace in Spring Boot 2.5.4 (and others I would assume). You can however just log out the response as below if you really need it like I did.

@Component
public class RequestTraceFilter extends HttpTraceFilter {

    public RequestTraceFilter(HttpTraceRepository repository, HttpExchangeTracer tracer) {
        super(repository, tracer);
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        TeeHttpServletResponse teeResponse = new TeeHttpServletResponse(response);

        filterChain.doFilter(request, teeResponse);

        teeResponse.finish();

        log.info(new String(teeResponse.getOutputBuffer()));
        
        super.doFilterInternal(request, teeResponse, filterChain);
    }
}

I also stole the use of TeeHttpServletResponse from the above answer, put it and TeeServletOutputStream in the same package as the filter.

Breadnut answered 3/10, 2023 at 16:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.