background

In general, to facilitate problem location, we need to record the incoming and outgoing parameters of the interface. However, the unrepeatable read nature of stream can cause unexpected problems.

Wrapper

As a wrapper class for Request and Response, we can rewrite getInputStream and getOutputStream to control the flow of data, so as to achieve repeatable reading of data.

HttpServletRequestWrapper

package cn.caojiantao.spider;

import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/ * * *@author caojiantao
 */
public class RequestWrapper extends HttpServletRequestWrapper {

    private byte[] data;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        data = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream(a) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
        return new ServletInputStream() {
            @Override
            public boolean isFinished(a) {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener listener) {}@Override
            public int read(a) throws IOException {
                returnbyteArrayInputStream.read(); }}; }public byte[] toByteArray() throws IOException {
        returndata; }}Copy the code

HttpServletResponseWrapper

package cn.caojiantao.spider;

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

/ * * *@author caojiantao
 */
public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream byteArrayOutputStream;

    private ServletOutputStream servletOutputStream;

    public ResponseWrapper(HttpServletResponse response) {
        super(response);
        byteArrayOutputStream = new ByteArrayOutputStream();
        servletOutputStream = new ServletOutputStream() {
            @Override
            public boolean isReady(a) {
                return false;
            }

            @Override
            public void setWriteListener(WriteListener writeListener) {}@Override
            public void write(int b) throws IOException {
                response.getOutputStream().write(b);
                // Write the byte array simultaneouslybyteArrayOutputStream.write(b); }}; }@Override
    public ServletOutputStream getOutputStream(a) throws IOException {
        return servletOutputStream;
    }

    public byte[] toByteArray() {
        returnbyteArrayOutputStream.toByteArray(); }}Copy the code

Response.getoutputstream () and Response.getwriter () are mutually exclusive and cannot be used at the same time.

Instance – Log filter

package cn.caojiantao.spider.configuration;

import cn.caojiantao.spider.RequestWrapper;
import cn.caojiantao.spider.ResponseWrapper;
import cn.caojiantao.spider.util.LogContext;
import cn.caojiantao.spider.util.NetUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/ * * *@author caojiantao
 */
@Slf4j
@WebFilter(urlPatterns = {"/ *"})
public class SpiderFilter implements Filter {

    private List<String> excludePathList = Arrays.asList("/"."/favicon.ico"."/index.html"."/css/*"."/js/*");

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if (excludePathList.contains(request.getRequestURI())) {
            filterChain.doFilter(request, response);
            return;
        }

        // Trace log
        LogContext.setTraceId();
        // Wrap stream, repeatable read
        RequestWrapper requestWrapper = new RequestWrapper(request);
        ResponseWrapper responseWrapper = new ResponseWrapper(response);
        // Request parameters
        String traceId = LogContext.getTraceId();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String data = new String(requestWrapper.toByteArray());
        String query = request.getQueryString();
        String ip = NetUtils.getIpAddress(request);

        log.info("request traceId:{} method:{} uri:{} data:{} query:{} ip:{}", traceId, method, uri, data, query, ip);

        long t = System.currentTimeMillis();
        filterChain.doFilter(requestWrapper, responseWrapper);
        // Response parameters
        String resp = new String(responseWrapper.toByteArray());
        long cost = System.currentTimeMillis() - t;

        log.info("response traceId:{} method:{} uri:{} data:{} query:{} ip:{} response:{} cost:{}", traceId, method, uri, data, query, ip, resp, cost); LogContext.clear(); }}Copy the code

LogContext is used to manage log tracing traceId and is implemented using ThreadLocal to facilitate fault locating.

package cn.caojiantao.spider.util;

import java.util.UUID;

/ * * *@author caojiantao
 */
public class LogContext {

    private static ThreadLocal<String> traceIdLocal = new ThreadLocal<>();

    public static void setTraceId(a) {
        String traceId = UUID.randomUUID().toString().replaceAll("-"."");
        setTraceId(traceId);
    }

    public static void setTraceId(String traceId) {
        traceIdLocal.set(traceId);
    }

    public static String getTraceId(a) {
        return traceIdLocal.get();
    }

    public static void clear(a) { traceIdLocal.remove(); }}Copy the code