score:28

Accepted answer

use an interceptor:

  • extend handlerinterceptoradapter and override prehandle
  • define it with <mvc:interceptors> in dispatcher-servlet.xml

it will run for every request.

score:1

as any tech answer ... it depends .. on the tech stack you are using and what your requirements are.

for example the more generic you want to make your logging, the further upfront you would want to do it. in your case, you are logging only requests which are logging enabled and being handled in the spring context. so you could be "missing" other requests.

i would look at the container or the web server you are using to run your app. that will remove this dependency on spring. plus containers provide you the flexibility of plugging in a logging provider and then configuring the format of the log outside code. for example, if you are using apache web server, use apache web server logging to log all http requests in the access logging layer. but be careful, some of the logging options have performance penalties. log only what you seriously need for an access pattern monitoring perspective.

if you are using tomcat, then tomcat also will allow you to log stuff. search for access valve in the tomcat documentation for the tomcat you are using. that will open up a world of possibilities.

more extensive logging should be the domain of the exception strategy ie the kind of detail you want to see when a problem occurs in the system.

score:2

adding to what @b.ali has answered. if you are using this in a spring asynchronous request (serlvet 3.0 or greater) handling scenario, then the following code is what worked for me.

public class onceperrequestloggingfilter extends onceperrequestfilter {

@override
protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain) throws servletexception, ioexception {
    boolean isfirstrequest = !isasyncdispatch(request);
    httpservletrequest requesttouse = request;
    httpservletresponse responsetouse = response;

    // the below check is critical and if not there, then the request/response gets corrupted.
    // probably because in async case the filter is invoked multiple times.
    if (isfirstrequest && !(request instanceof contentcachingrequestwrapper)) {
        requesttouse = new contentcachingrequestwrapper(request);
    }

    if (isfirstrequest && !(response instanceof contentcachingresponsewrapper)) {
        responsetouse = new contentcachingresponsewrapper(response);
    }

    filterchain.dofilter(requesttouse, responsetouse);

    if (!isasyncstarted(request)) {
        contentcachingresponsewrapper responsewrapper =
                webutils.getnativeresponse(response, contentcachingresponsewrapper.class);
        responsewrapper.copybodytoresponse(); // important to copy it back to response
    }
}

@override
protected boolean shouldnotfilterasyncdispatch() {
    return false; // important this is true by default and wont work in async scenario.
}

}

score:6

here's a small library i wrote you can use: spring-mvc-logger

i made it available via maven central:

<dependency>
    <groupid>com.github.isrsal</groupid>
    <artifactid>spring-mvc-logger</artifactid>
    <version>0.2</version>
</dependency>

score:8

the main issue with reading request is that as soon as the input stream is consumed its gone whoof... and cannot be read again. so the input stream has to be cached. instead of writing your own classes for caching (which can be found at several places on web), spring provides a couple of useful classes i.e. contentcachingrequestwrapper and contentcachingresponsewrapper. these classes can be utilized very effectively, for example, in filters for logging purposes.

define a filter in web.xml:

<filter>
    <filter-name>loggingfilter</filter-name>
    <filter-class>org.springframework.web.filter.delegatingfilterproxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>loggingfilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

since the filter is declared as delegatingfilterproxy, it can be declared as a bean using @component or @bean annotations. in the loggingfilter's dofilter method, wrap the request and response with spring provided classes before passing it to the filter chain:

httpservletrequest requesttocache = new contentcachingrequestwrapper(request);
httpservletresponse responsetocache = new contentcachingresponsewrapper(response);
chain.dofilter(requesttocache, responsetocache);
string requestdata = getrequestdata(requesttocache);
string responsedata = getresponsedata(responsetocache);

the input stream will be cached in the wrapped request as soon as the input stream is consumed after chain.dofilter(). then it can be accessed as below:

public static string getrequestdata(final httpservletrequest request) throws unsupportedencodingexception {
    string payload = null;
    contentcachingrequestwrapper wrapper = webutils.getnativerequest(request, contentcachingrequestwrapper.class);
    if (wrapper != null) {
        byte[] buf = wrapper.getcontentasbytearray();
        if (buf.length > 0) {
            payload = new string(buf, 0, buf.length, wrapper.getcharacterencoding());
        }
    }
    return payload;
}

however, things are a bit different for response. since the response was also wrapped before passing it to the filter chain, it will also be cached to the output stream as soon as it is written on its way back. but since the output stream will also be consumed so you have to copy the response back to the output stream using wrapper.copybodytoresponse(). see below:

public static string getresponsedata(final httpservletresponse response) throws ioexception {
    string payload = null;
    contentcachingresponsewrapper wrapper =
        webutils.getnativeresponse(response, contentcachingresponsewrapper.class);
    if (wrapper != null) {
        byte[] buf = wrapper.getcontentasbytearray();
        if (buf.length > 0) {
            payload = new string(buf, 0, buf.length, wrapper.getcharacterencoding());
            wrapper.copybodytoresponse();
        }
    }
    return payload;
}

hope it helps!

score:46

edit: also, see @membersound's comment on this answer, which improves this answer.

spring supports this. see commonsrequestloggingfilter. if using spring boot, just register a bean of that type and boot will apply it to the filter chain. like:

@bean
public filter logfilter() {
    commonsrequestloggingfilter filter = new commonsrequestloggingfilter();
    filter.setincludequerystring(true);
    filter.setincludepayload(true);
    filter.setmaxpayloadlength(5120);
    return filter;
}

also, this logging filter requires the log level be set to debug. e.g. do this in a logback.xml with:

<logger name="org.springframework.web.filter.commonsrequestloggingfilter" level="debug"/>

Related Query

More Query from same tag