java - Logging all network traffic in Spring mvc -


i have spring mvc application using requestbody , responsebody annotations. configured mappingjackson2httpmessageconverter. have slf4j set up. log json comes in , out controller. did extend

mappingjackson2httpmessageconverter  @override public object read(type type, class<?> contextclass, httpinputmessage inputmessage)         throws ioexception, httpmessagenotreadableexception {     logstream(inputmessage.getbody());     return super.read(type, contextclass, inputmessage); } 

i can input stream, if read content becomes empty , loose message. mark() , reset() not supported. implemented pushbackinputstream, tried read it's content , push this:

public void logstream(inputstream is) {     if (is instanceof pushbackinputstream)     try {         pushbackinputstream pushbackinputstream = (pushbackinputstream) is;         byte[] bytes = new byte[20000];         stringbuilder sb = new stringbuilder(is.available());         int red = is.read();         int pos =0;         while (red > -1) {             bytes[pos] = (byte) red;             pos=1 + pos;             red = is.read();         }         pushbackinputstream.unread(bytes,0, pos-1);         log.info("json payload " + sb.tostring());     } catch (exception e) {         log.error("ignoring exception in logger ", e);     } } 

but exception

java.io.ioexception: push buffer full 

i tried turn on logging on http level described here:spring resttemplate - how enable full debugging/logging of requests/responses? without luck.

after more whole work day of experimenting got working solution. consists of logging filter, 2 wrappers request , response , registration of logging filter:

the filter class is:

/**  * http logging filter, wraps around request , response in  * each http call , logs  * whole request , response bodies. enabled   * putting instance filter chain  * overriding getservletfilters() in    * abstractannotationconfigdispatcherservletinitializer.  */ public class loggingfilter extends abstractrequestloggingfilter {  private static final logger log = loggerfactory.getlogger(loggingfilter.class);  @override protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain)         throws servletexception, ioexception {     long id = system.currenttimemillis();     requestloggingwrapper requestloggingwrapper = new requestloggingwrapper(id, request);     responseloggingwrapper responseloggingwrapper = new responseloggingwrapper(id, response);     log.debug(id + ": http request " + request.getrequesturi());     super.dofilterinternal(requestloggingwrapper, responseloggingwrapper, filterchain);     log.debug(id + ": http response " + response.getstatus() + " finished in " + (system.currenttimemillis() - id) + "ms"); }  @override protected void beforerequest(httpservletrequest request, string message) {  }  @override protected void afterrequest(httpservletrequest request, string message) {  } } 

this class using stream wrappers, suggested master slave , david ehrmann.

request wrapper looks this:

/**  * request logging wrapper using proxy split stream extract request body  */ public class requestloggingwrapper extends httpservletrequestwrapper { private static final logger log =  loggerfactory.getlogger(requestloggingwrapper.class); private final bytearrayoutputstream bos = new bytearrayoutputstream(); private long id;  /**  * @param requestid , id gets logged output file. it's used bind request  *                  response  * @param request   request want extract post data  */ public requestloggingwrapper(long requestid, httpservletrequest request) {     super(request);     this.id = requestid; }  @override public servletinputstream getinputstream() throws ioexception {     final servletinputstream servletinputstream = requestloggingwrapper.super.getinputstream();     return new servletinputstream() {         private teeinputstream tee = new teeinputstream(servletinputstream, bos);          @override         public int read() throws ioexception {             return tee.read();         }          @override         public int read(byte[] b, int off, int len) throws ioexception {             return tee.read(b, off, len);         }          @override         public int read(byte[] b) throws ioexception {             return tee.read(b);         }          @override         public boolean isfinished() {             return servletinputstream.isfinished();         }          @override         public boolean isready() {             return servletinputstream.isready();         }          @override         public void setreadlistener(readlistener readlistener) {             servletinputstream.setreadlistener(readlistener);         }          @override         public void close() throws ioexception {             super.close();             // logging             logrequest();         }     }; }  public void logrequest() {     log.info(getid() + ": http request " + new string(tobytearray())); }  public byte[] tobytearray() {     return bos.tobytearray(); }  public long getid() {     return id; }  public void setid(long id) {     this.id = id; } } 

and response wrapper different in close/flush method (close doesn't called)

public class responseloggingwrapper extends httpservletresponsewrapper { private static final logger log = loggerfactory.getlogger(responseloggingwrapper.class); private final bytearrayoutputstream bos = new bytearrayoutputstream(); private long id;  /**  * @param requestid , id gets logged output file. it's used bind response  *                  response (they have same id, currenttimemilis used)  * @param response  response want extract stream data  */ public responseloggingwrapper(long requestid, httpservletresponse response) {     super(response);     this.id = requestid; }  @override public servletoutputstream getoutputstream() throws ioexception {     final servletoutputstream servletoutputstream = responseloggingwrapper.super.getoutputstream();     return new servletoutputstream() {         private teeoutputstream tee = new teeoutputstream(servletoutputstream, bos);          @override         public void write(byte[] b) throws ioexception {             tee.write(b);         }          @override         public void write(byte[] b, int off, int len) throws ioexception {             tee.write(b, off, len);         }          @override         public void flush() throws ioexception {             tee.flush();             logrequest();         }          @override         public void write(int b) throws ioexception {             tee.write(b);         }          @override         public boolean isready() {             return servletoutputstream.isready();         }          @override         public void setwritelistener(writelistener writelistener) {             servletoutputstream.setwritelistener(writelistener);         }           @override         public void close() throws ioexception {             super.close();             // logging             logrequest();         }     }; }  public void logrequest() {     byte[] tolog = tobytearray();     if (tolog != null && tolog.length > 0)         log.info(getid() + ": http response " + new string(tolog)); }  /**  * method clear buffer,  *  * @return captured bytes stream  */ public byte[] tobytearray() {     byte[] ret = bos.tobytearray();     bos.reset();     return ret; }  public long getid() {     return id; }  public void setid(long id) {     this.id = id; } 

}

at last loggingfilter needs registered in abstractannotationconfigdispatcherservletinitializer this:

 @override protected filter[] getservletfilters() {     loggingfilter requestloggingfilter = new loggingfilter();      return new filter[]{requestloggingfilter}; } 

i know, there maven lib this, don't want include whole lib because of small logging utility. harder thought. expected achieve modifying log4j.properties. still think should part of spring.


Comments

Popular posts from this blog

powershell Start-Process exit code -1073741502 when used with Credential from a windows service environment -

twig - Using Twigbridge in a Laravel 5.1 Package -

c# - LINQ join Entities from HashSet's, Join vs Dictionary vs HashSet performance -