Spring boot给接口返回添加request、response log

来源:互联网 发布:php多用户博客系统 编辑:程序博客网 时间:2024/06/11 01:57

title: spring boot给接口返回添加输入输出日志

tag: spring boot

data: 2017-06-09


Spring boot返回request、response 添加log

目前有个需求需要给接口返回添加输入输出日志,目前比价常用的方法就是使用过滤器,就可以很方便的拦截到输入请求和输出请求,那么在过滤器里面如何HttpServletRequest HttpServletRequest这两个对象输入和输出日志呢?

解决办法

事实上javax包里面本来就提供HttpServletRequstWrapper、HttpServletResponseWrapper两个包装类进行对请求、和放回进行拦截、我们实现这两个,这样我们就能够写一些单独的东西,就可以实现我们的效果

上代码

先写一个ResponseWrapper类来继承HttpServletResponseWrapper

package com.dzy.itar.pinpin.support.filter;import org.apache.commons.io.output.TeeOutputStream;import javax.servlet.ServletOutputStream;import javax.servlet.ServletResponse;import javax.servlet.WriteListener;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.PrintWriter;public class ResponseWrapper extends HttpServletResponseWrapper {    private final ByteArrayOutputStream bos = new ByteArrayOutputStream();    private PrintWriter writer = new PrintWriter(bos);    private long id;    public ResponseWrapper(Long requestId, HttpServletResponse response) {        super(response);        this.id = requestId;    }    @Override    public ServletResponse getResponse() {        return this;    }    @Override    public ServletOutputStream getOutputStream() throws IOException {        return new ServletOutputStream() {            @Override            public boolean isReady() {                return false;            }            @Override            public void setWriteListener(WriteListener writeListener) {            }            private TeeOutputStream tee = new TeeOutputStream(ResponseWrapper.super.getOutputStream(), bos);            @Override            public void write(int b) throws IOException {                tee.write(b);            }        };    }    @Override    public PrintWriter getWriter() throws IOException {        return new TeePrintWriter(super.getWriter(), writer);    }    public byte[] toByteArray(){        return bos.toByteArray();    }    public long getId() {        return id;    }    public void setId(long id) {        this.id = id;    }}
package com.dzy.itar.pinpin.support.filter;import java.io.PrintWriter;public class TeePrintWriter extends PrintWriter{    PrintWriter branch;    public TeePrintWriter(PrintWriter main, PrintWriter branch) {        super(main, true);        this.branch = branch;    }    public void write(char buf[], int off, int len) {        super.write(buf, off, len);        super.flush();        branch.write(buf, off, len);        branch.flush();    }    public void write(String s, int off, int len) {        super.write(s, off, len);        super.flush();        branch.write(s, off, len);        branch.flush();    }    public void write(int c) {        super.write(c);        super.flush();        branch.write(c);        branch.flush();    }    public void flush() {        super.flush();        branch.flush();    }}
package com.dzy.itar.pinpin.support.filter;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.concurrent.atomic.AtomicLong;@Componentpublic class LoggingFilter extends OncePerRequestFilter {    protected static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);    private static final String REQUEST_PREFIX = "Request: ";    private static final String RESPONSE_PREFIX = "Response: ";    private AtomicLong id = new AtomicLong(1);    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException {        if (logger.isDebugEnabled()) {            long requestId = id.incrementAndGet();            request = new RequestWrapper(requestId, request);            response = new ResponseWrapper(requestId, response);        }        try {            filterChain.doFilter(request, response);//            response.flushBuffer();        } finally {            if (logger.isDebugEnabled()) {                logRequest(request);                logResponse((ResponseWrapper) response);            }        }    }    private void logRequest(final HttpServletRequest request) {        StringBuilder msg = new StringBuilder();        msg.append(REQUEST_PREFIX);        if (request instanceof RequestWrapper) {            msg.append("request id=").append(((RequestWrapper) request).getId()).append("; ");        }        HttpSession session = request.getSession(false);        if (session != null) {            msg.append("session id=").append(session.getId()).append("; ");        }        if (request.getMethod() != null) {            msg.append("method=").append(request.getMethod()).append("; ");        }        if (request.getContentType() != null) {            msg.append("content type=").append(request.getContentType()).append("; ");        }        msg.append("uri=").append(request.getRequestURI());        if (request.getQueryString() != null) {            msg.append('?').append(request.getQueryString());        }        if (request instanceof RequestWrapper && !isMultipart(request) && !isBinaryContent(request)) {            RequestWrapper requestWrapper = (RequestWrapper) request;            try {                String charEncoding = requestWrapper.getCharacterEncoding() != null ? requestWrapper.getCharacterEncoding() :                        "UTF-8";                msg.append("; payload=").append(new String(requestWrapper.toByteArray(), charEncoding));            } catch (UnsupportedEncodingException e) {                logger.warn("Failed to parse request payload", e);            }        }        logger.debug(msg.toString());    }    private boolean isBinaryContent(final HttpServletRequest request) {        if (request.getContentType() == null) {            return false;        }        return request.getContentType().startsWith("image") || request.getContentType().startsWith("video") || request.getContentType().startsWith("audio");    }    private boolean isMultipart(final HttpServletRequest request) {        return request.getContentType() != null && request.getContentType().startsWith("multipart/form-data");    }    private void logResponse(final ResponseWrapper response) {        StringBuilder msg = new StringBuilder();        msg.append(RESPONSE_PREFIX);        msg.append("request id=").append((response.getId()));        try {            msg.append("; payload=").append(new String(response.toByteArray(), response.getCharacterEncoding()));        } catch (UnsupportedEncodingException e) {            logger.warn("Failed to parse response payload", e);        }        logger.debug(msg.toString());    }}

PrintWriter是一种写入字符的一种操作类,可以写入字符,TeePrintWriter继承了他,主要功能是把原始的字符流copy到branch里面。

LoggingFilter是一种filter,继承OncePerRequestFilter,每次请求都会经过他。

主要代码就是上面这样的,我把代码放到了这里大家可以下载借鉴

源码解析

SpringMVC在处理完毕请求之后,会调用AbstractMessageConverterMethodProcessor里的writeWithMessageConverters方法,然后会调用AbstractGenericHttpMessageConverter 的write方法,把返回的接口输出到HttpOutputMessage里面去,事实上,然后会调用WriteInternal方法

然后里面有getBody()方法,然后里面就调用到了我们的getOutputStream方法,终于走到我们写的方法了

然后我们就可以处理这个流了

主要是copy了一份,然后再filter里面打印了出来

完毕

原创粉丝点击