SpringBoot 拦截器 统一日志 记录用户请求返回日志

你请求我接口,传了什么参数,我返回了什么值给你,全部记下来。防止扯皮

需求:记录每次用户请求Controller的Body参数,

思路:在每个Controller 该当中记录,容易漏记,如果在拦截器里面记的话,可以统一处理

问题:在postHandle 里面记,request.getInputStream() 取出来是空的,放在preHandle里面,就进不到 Controller 里面了。报:I/O error while reading input message; nested exception is java.io.IOException: Stream closed

原因:在拦截器已经读取了请求体中的内容,这时候请求的流中已经没有了数据,就是说HttpServletRequest请求体中的内容一旦读取就不不存在了,所以直接读取是不行的

方案:对httprequest进行修饰,自定义的包装类来实现

添加 RequestLogFilter

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

 
@Component
@WebFilter(filterName = "requestLogFilter",urlPatterns = {"/*"}, 
initParams = {@WebInitParam(name = "ignoredUrl", value = ".css;.js;.jpg;.png;.gif;.ico;.html"), 
@WebInitParam(name = "filterPath", value = "/user/login#/user/registerUser")})
public class RequestLogFilter implements OncePerRequestFilter {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
 
    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        boolean writeLog = true;
        String ignoreLog = "swagger;upload;.jpg;.png;.zip;.dat;.ico;.pdf;download"; //可以放配置文件
        RequestLogWrapper requestWapper = null;
        if (servletRequest instanceof HttpServletRequest) {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            for (String item : ignoreLog.split(";")) {
                if ("/".equals(request.getRequestURI()) || request.getRequestURI().toLowerCase().contains(item)) {
                    //有一个包含就不记
                    writeLog = false;
                    break;
                }
            }
            if (writeLog) {
                requestWapper = new RequestLogWrapper(request);
            }
        }

        ResponseLogWrapper responseLogWrapper = new ResponseLogWrapper(servletResponse);

        //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
        // 在chain.doFiler方法中传递新的request对象
        if (requestWapper == null) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            if (writeLog) {
                filterChain.doFilter(requestWapper, responseLogWrapper);
            } else {
                filterChain.doFilter(requestWapper, servletResponse);
            }
        }

        if (writeLog) {
            //打印返回响应日志
            String result = new String(responseLogWrapper.getResponseData());
            ServletOutputStream outputStream = servletResponse.getOutputStream();
            outputStream.write(result.getBytes());
            outputStream.flush();
            outputStream.close();

            String queryStr = StrUtil.isEmpty(servletRequest.getQueryString()) ? "" : "?" + servletRequest.getQueryString();
            logger.info("Response => {}{} \r\n{}", servletRequest.getRequestURI(), queryStr, result);
        }
    }

    @Override
    public void destroy() {
        logger.info(">>>> RequestLogFilter destroy <<<<");
    }
}

 

添加 RequestLogWapper

import cn.hutool.core.util.StrUtil; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;


public class RequestLogWrapper extends HttpServletRequestWrapper {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private String requestBody;

    public String getRequestBody() {
        return requestBody;
    }

    public RequestLogWrapper(HttpServletRequest request) throws IOException {
        super(request); 
        requestBody = getBodyString(request);
        String queryStr = StrUtil.isEmpty(request.getQueryString()) ? "" : "?" + request.getQueryString();

        String remoteAddr = request.getRemoteAddr();
        Enumeration<String> headerNames = request.getHeaderNames();
        String headerStr = "";
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headerStr += StrUtil.format("{}:{};", headerName, headerValue);
        }
        logger.info("Request => {}{} RemoteAddr => {} \r\n Headers => {}\r\n{}", request.getRequestURI(), queryStr, remoteAddr, headerStr, requestBody);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    public String getBodyString(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

 

 

ResponseLogWrapper

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;


public class ResponseLogWrapper extends HttpServletResponseWrapper {


    private ByteArrayOutputStream buffer = null;//输出到byte array
    private ServletOutputStream out = null;
    private PrintWriter writer = null;

    public ResponseLogWrapper(HttpServletResponse resp) throws IOException {
        super(resp);
        buffer = new ByteArrayOutputStream();// 真正存储数据的流
        out = new WapperedOutputStream(buffer);
        writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
    }

    /** 重载父类获取outputstream的方法 */
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }

    /** 重载父类获取writer的方法 */
    @Override
    public PrintWriter getWriter() throws UnsupportedEncodingException {
        return writer;
    }

    /** 重载父类获取flushBuffer的方法 */
    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
        if (writer != null) {
            writer.flush();
        }
    }

    @Override
    public void reset() {
        buffer.reset();
    }

    /** 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 */
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    /** 内部类,对ServletOutputStream进行包装 */
    private class WapperedOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bos = null;

        public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
            bos = stream;
        }

        @Override
        public void write(int b) throws IOException {
            bos.write(b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            bos.write(b, 0, b.length);
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener listener) {

        }
    }
}

 

 

 

参考资源:

https://www.jianshu.com/p/ba2d5101ad90

https://blog.csdn.net/qq_38132283/article/details/107685797

posted @ 2022-01-20 14:43  VipSoft  阅读(2222)  评论(0编辑  收藏  举报