美文网首页SpringBoot个人学习SpringCloud
SpringBoot怎么监听所有请求(保存api日志)

SpringBoot怎么监听所有请求(保存api日志)

作者: 刘坤林 | 来源:发表于2021-05-14 09:23 被阅读0次

    功能描述

    在SpringBoot中如要实现记录接口被调用的频率和生成api日志,以便查看接口使用情况,那么监听所有api请求的功能就诞生了。

    功能实现

    一、自定义request继承HttpServletRequestWrapper

    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import com.alibaba.fastjson.JSONObject;
    
    public class MyRequestWrapper extends HttpServletRequestWrapper {
        private byte[] body;
        private ServletInputStreamWrapper inputStreamWrapper;
        private Object requestBody;
    
        public MyRequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            int le = request.getContentLength();
            if (le > 0) {
                body = new byte[le];
                request.getInputStream().read(body);
                requestBody = JSONObject.parse(body, 0, body.length, Charset.forName("UTF-8").newDecoder(), 0);
            } else {
                body = new byte[0];
                requestBody = "";
            }
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);
            this.inputStreamWrapper = new ServletInputStreamWrapper(byteArrayInputStream);
            resetInputStream();
        }
    
        public String getRequestBody() {
            return String.valueOf(requestBody);
        }
    
        public void setRequestBody(Object requestBody) {
            this.requestBody = requestBody;
        }
    
        private void resetInputStream() {
            this.inputStreamWrapper.setInputStream(new ByteArrayInputStream(this.body != null ? this.body : new byte[0]));
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            return this.inputStreamWrapper;
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.inputStreamWrapper));
        }
    
        private static class ServletInputStreamWrapper extends ServletInputStream {
    
            private InputStream inputStream;
    
            public ServletInputStreamWrapper(InputStream inputStream) {
                super();
                this.inputStream = inputStream;
            }
    
            public InputStream getInputStream() {
                return inputStream;
            }
    
            public void setInputStream(InputStream inputStream) {
                this.inputStream = inputStream;
            }
    
            @Override
            public boolean isFinished() {
                return true;
            }
    
            @Override
            public boolean isReady() {
                return false;
            }
    
            @Override
            public void setReadListener(ReadListener readListener) {
    
            }
    
            @Override
            public int read() throws IOException {
                return this.inputStream.read();
            }
        }
    }
    

    二、自定义response继承HttpServletResponseWrapper

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.nio.charset.Charset;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.WriteListener;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import com.alibaba.fastjson.JSONObject;
    public class MyResponseWrapper extends HttpServletResponseWrapper {
        private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        private HttpServletResponse response;
        private Object reponseBody;
    
        public MyResponseWrapper(HttpServletResponse response) {
            super(response);
            this.response = response;
        }
    
        public String getResponseBody() {
            byte[] bytes = byteArrayOutputStream.toByteArray();
            reponseBody = JSONObject.parse(bytes, 0, bytes.length, Charset.forName("UTF-8").newDecoder(), 0);
            return String.valueOf(reponseBody);
        }
    
        @Override
        public ServletOutputStream getOutputStream() {
            return new ServletOutputStreamWrapper(this.byteArrayOutputStream, this.response);
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            return new PrintWriter(
                    new OutputStreamWriter(this.byteArrayOutputStream, this.response.getCharacterEncoding()));
        }
    
        private static class ServletOutputStreamWrapper extends ServletOutputStream {
    
            private ByteArrayOutputStream outputStream;
            private HttpServletResponse response;
    
            public ServletOutputStreamWrapper(ByteArrayOutputStream outputStream, HttpServletResponse response) {
                this.outputStream = outputStream;
                this.response = response;
            }
    
            @Override
            public boolean isReady() {
                return true;
            }
    
            @Override
            public void setWriteListener(WriteListener listener) {
    
            }
    
            @Override
            public void write(int b) throws IOException {
                this.outputStream.write(b);
            }
    
            @Override
            public void flush() throws IOException {
                if (!this.response.isCommitted()) {
                    byte[] body = this.outputStream.toByteArray();
                    ServletOutputStream outputStream = this.response.getOutputStream();
                    outputStream.write(body);
                    outputStream.flush();
                }
            }
        }
    

    三、新建一个类RequestWrapperFilter继承OncePerRequestFilter,重写doFilterInternal()方法,具体如下。

    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    import com.lkl.web.maven1.api.help.MyRequestWrapper;
    import com.lkl.web.maven1.api.help.MyResponseWrapper;
    
    /**
     * 监听所有请求
     * 
     * @author Administrator
     *
     */
    @Component
    public class RequestWrapperFilter extends OncePerRequestFilter {
        private MyRequestWrapper requestWrapper;
        private MyResponseWrapper reponseWrapper;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
            try {
                requestWrapper = new MyRequestWrapper(request);
                reponseWrapper = new MyResponseWrapper(response);
                filterChain.doFilter(requestWrapper, reponseWrapper);
                String requestBody = requestWrapper.getRequestBody();
                String reponseBody = reponseWrapper.getResponseBody();
                            //做你想做的事情
            } catch (Exception e) {
                            //失败时,默认即可
                filterChain.doFilter(request, response);
            }
        }
    }
    

    简单总结

    1.自定义reques和response无非就是想从流中拿到byte[],因为在outputStream和inputStream中read()后再次read()会读不到数据。
    2.有时候会因为各种请求体或响应体为空导致doFilterInternal失败,因此加个try catch块。
    3.注意RequestWrapperFilter中标注@Component。
    4.保存日志有可能是高频率的io操作,可通过定时器来定时,选择性保存日志。

    以上都是我的个人理解,如有不足,请在评论区指出。不胜感激。

    相关文章

      网友评论

        本文标题:SpringBoot怎么监听所有请求(保存api日志)

        本文链接:https://www.haomeiwen.com/subject/ajnfjltx.html