1

Spring Boot REST app here. I'm trying to configure Spring Boot request auditing to log each and every HTTP request that any resources/controllers receive with the following info:

  1. I need to see in the logs the exact HTTP URL (path) that was requested by the client, including the HTTP method and any query string parameters; and
  2. If there is a request body (such as with a POST or PUT) I need to see the contents of that body in the logs as well

My best attempt so far:

@Component
public class MyAppAuditor {
    private Logger logger;

    @EventListener
    public void handleAuditEvent(AuditApplicationEvent auditApplicationEvent) {
        logger.info(auditApplicationEvent.auditEvent);
    }
}

public class AuditingTraceRepository implements TraceRepository {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher

    @Override
    List<Trace> findAll() {
        throw new UnsupportedOperationException("We don't expose trace information via /trace!");
    }

    @Override
    void add(Map<String, Object> traceInfo) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        AuditEvent traceRequestEvent = new AuditEvent(new Date(), "SomeUser", 'http.request.trace', traceInfo);
        AuditApplicationEvent traceRequestAppEvent = new AuditApplicationEvent(traceRequestEvent);

        applicationEventPublisher.publishEvent(traceRequestAppEvent);
    }
}

However at runtime if I use the following curl command:

curl -i -H "Content-Type: application/json" -X GET 'http://localhost:9200/v1/data/profiles?continent=NA&country=US&isMale=0&height=1.5&dob=range('1980-01-01','1990-01-01')'

Then I only see the following log messages (where MyAppAuditor send audit events):

{ "timestamp" : "14:09:50.516", "thread" : "qtp1293252487-17", "level" : "INFO", "logger" : "com.myapp.ws.shared.auditing.MyAppAuditor", "message" : {"timestamp":"2018-06-29T18:09:50+0000","principal":"SomeUser","type":"http.request.trace","data":{"method":"GET","path":"/v1/data/profiles","headers":{"request":{"User-Agent":"curl/7.54.0","Host":"localhost:9200","Accept":"*/*","Content-Type":"application/json"},"response":{"X-Frame-Options":"DENY","Cache-Control":"no-cache, no-store, max-age=0, must-revalidate","X-Content-Type-Options":"nosniff","Pragma":"no-cache","Expires":"0","X-XSS-Protection":"1; mode=block","X-Application-Context":"application:9200","Date":"Fri, 29 Jun 2018 18:09:50 GMT","Content-Type":"application/json;charset=utf-8","status":"200"}},"timeTaken":"89"}} }

So as you can see, the auditor is picking up the base path (/v1/data/profiles) but is not logging any of the query string parameters. I also see a similar absence of request body info when I hit POST or PUT endpoints that do require a request body (JSON).

What do I need to do to configure these classes (or other Spring classes/configs) so that I get the level of request auditing that I'm looking for?

hotmeatballsoup
  • 385
  • 6
  • 58
  • 136
  • I posted what I think would be the best solution for this. Let me know if you have any questions about the solution and if it needs any clarification – Dovmo Jul 05 '18 at 13:36

1 Answers1

2

Fortunately, Actuator makes it very easy to configure those Trace events.

Adding parameters to Trace info

You can take a look at all of the options. You'll notice the defaults (line 42) are:

Include.REQUEST_HEADERS, 
Include.RESPONSE_HEADERS, 
Include.COOKIES, 
Include.ERRORS, 
Include.TIME_TAKEN

So you'll need to also add Include.PARAMETERS and anything else you'd like to have in the trace. To configure that, there's a configuration property for that management.trace.include.

So to get what you want (i.e. parameters), plus the defaults, you'd have:

management.trace.include = parameters, request-headers, response-headers, cookies, errors, time-taken

Adding request body to Trace info

In order to get the body, you're going to have to add in this Bean to your Context:

@Component
public class WebRequestTraceFilterWithPayload extends WebRequestTraceFilter {

    public WebRequestTraceFilterWithPayload(TraceRepository repository, TraceProperties properties) {
        super(repository, properties);
    }

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

        String body = null;
        try {
            body = request.getReader().lines().collect(Collectors.joining("\n"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if(body != null) {
            trace.put("body", body);
        }

        return trace;
    }

}

The above code will override the AutoConfigure'd WebRequestTraceFilter bean (which, because it is @ConditionalOnMissingBean will give deference to your custom bean), and pull the extra payload property off of the request then add it to to the Map properties that get published to your TraceRepository!

Summary

  1. Request Parameters can be added to TraceRepository trace events by via the management.trace.include property
  2. The Request Body can be added to the TraceRepository trace events by creating an extended Bean to read the body off of the HTTP request and supplementing the trace events
Dovmo
  • 8,121
  • 3
  • 30
  • 44