【spring请求日志过滤器】

1. 方式一

直接继承OncePerRequestFilter

import com.alibaba.fastjson.JSON;
import com.util.IpUtil;
import org.slf4j.MDC;
import org.springframework.beans.BeanWrapper;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.util.*;

/**
 * HTTP request logging
 */
public class RequestLoggerFilter extends OncePerRequestFilter {

    private int maxPayloadLength = 25000;

    private String getContentAsString(byte[] buf, int maxLength, String charsetName) {
        if (buf == null || buf.length == 0) return "";
        int length = Math.min(buf.length, this.maxPayloadLength);
        try {
            return new String(buf, 0, length, charsetName);
        } catch (UnsupportedEncodingException ex) {
            return "Unsupported Encoding";
        }
    }

    @Override
    protected void initBeanWrapper(BeanWrapper bw) {

    }

    /**
     * Log each request and respponse with full Request URI, content payload and duration of the request in ms.
     *
     * @param request     the request
     * @param response    the response
     * @param filterChain chain of filters
     * @throws ServletException 可能生成的异常
     * @throws IOException      可能生成的异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String url = request.getRequestURI();
        // 1资源文件,前端视图文件,全部放过.childlist为配置列表,也删除
        if (url.contains(".css") || url.contains(".js") || url.contains(".png") || url.contains(".jpg") || url.contains("/v/") || url.contains(".ico") || url.contains("/demo/") || url.contains("/swagger-resources") || url.contains("/v2") || url.contains("/index") || url.contains("/res") || url.contains("/swagger-ui") || url.contains("/webjars") || url.contains(".html") || url.contains(".css")) {
            filterChain.doFilter(request, response);
            return;
        }
        if (request.getMethod().toLowerCase().equals("get")) {
            filterChain.doFilter(request, response);
            return;
        }


        long startTime = System.currentTimeMillis();
        StringBuffer reqInfo = new StringBuffer()
                .append(request.getMethod())
                .append(":")
                .append(request.getRequestURL());

        Long logId = System.currentTimeMillis() << 10;
        Random rand = new Random(logId);
        logId += rand.nextInt(1000);
        MDC.put("logid", logId.toString());

        Map<String, String> reqHeaderMap = new HashMap<>();
        Enumeration<String> headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            String headerName = headers.nextElement();
            String headerValue = request.getHeader(headerName);

            reqHeaderMap.put(headerName, request.getHeader(headerName));
        }

        String queryString = request.getQueryString();
        if (queryString != null) {
            reqInfo.append("?").append(queryString);
        }

        if (request.getAuthType() != null) {
            reqInfo.append(", authType=")
                    .append(request.getAuthType());
        }
        if (request.getUserPrincipal() != null) {
            reqInfo.append(", principalName=")
                    .append(request.getUserPrincipal().getName());
        }

        // ========= Log request and response payload ("body") ========
        // We CANNOT simply read the request payload here, because then the InputStream would be consumed and cannot be read again by the actual processing/server.
        //    String reqBody = DoogiesUtil._stream2String(request.getInputStream());   // THIS WOULD NOT WORK!
        // So we need to apply some stronger magic here :-)
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
        HttpServletResponseCopier wrappedResponse = new HttpServletResponseCopier(response);

        // ======== This performs the actual request!
        filterChain.doFilter(wrappedRequest, wrappedResponse);

        //下面进行日志处理
        long duration = System.currentTimeMillis() - startTime;
        Map<String, String> rspHeaderMap = new HashMap<>();
        Collection<String> rspHeaders = response.getHeaderNames();
        for (String header : rspHeaders) {
            rspHeaderMap.put(header, response.getHeader(header));
        }

        // I can only log the request's body AFTER the request has been made and ContentCachingRequestWrapper did its work.
        byte[] reqContent = wrappedRequest.getContentAsByteArray();
        String requestBody = this.getContentAsString(reqContent, this.maxPayloadLength, request.getCharacterEncoding());

        byte[] respContent = wrappedResponse.getCopy();
        String respBody = getContentAsString(respContent, this.maxPayloadLength, response.getCharacterEncoding());

        reqHeaderMap.remove("accept-charset");
        String log = String.format("[%d] ip[%s] req-header[%s] req-info[%s] req-body[%s] rsp-head[%s] rsp-body[%s] r-status[%s] t[%s]"
                , logId
                , IpUtil.getIpAddr(request)
                , JSON.toJSONString(reqHeaderMap)
                , reqInfo
                , requestBody
                , JSON.toJSONString(rspHeaderMap)
                , respBody
                , response.getStatus()
                , duration);
        logger.info(log);

        // 重要:要把response的内容拷贝回原始响应中
        wrappedResponse.flushBuffer();
    }


    public static class HttpServletResponseCopier extends HttpServletResponseWrapper {

        private ServletOutputStream outputStream;
        private PrintWriter writer;
        private ServletOutputStreamCopier copier;

        HttpServletResponseCopier(HttpServletResponse response) throws IOException {
            super(response);
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            if (writer != null) {
                throw new IllegalStateException("getWriter() has already been called on this response.");
            }

            if (outputStream == null) {
                outputStream = getResponse().getOutputStream();
                copier = new ServletOutputStreamCopier(outputStream, getResponse().getBufferSize());
            }

            return copier;
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            if (outputStream != null) {
                throw new IllegalStateException("getOutputStream() has already been called on this response.");
            }

            if (writer == null) {
                copier = new ServletOutputStreamCopier(getResponse().getOutputStream(), getResponse().getBufferSize());
                writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);
            }

            return writer;
        }

        @Override
        public void flushBuffer() throws IOException {
            if (writer != null) {
                writer.flush();
            } else if (outputStream != null) {
                copier.flush();
            }
        }

        byte[] getCopy() {
            if (copier != null) {
                return copier.getCopy();
            } else {
                return new byte[0];
            }
        }
    }

    public static class ServletOutputStreamCopier extends ServletOutputStream {

        private OutputStream outputStream;
        private ByteArrayOutputStream copy;

        ServletOutputStreamCopier(OutputStream outputStream, int size) {
            this.outputStream = outputStream;
            this.copy = new ByteArrayOutputStream(size);
        }

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

        byte[] getCopy() {
            return copy.toByteArray();
        }

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

        @Override
        public void setWriteListener(WriteListener listener) {
        }
    }
}

注入bean

/**
 * 请求日志
 *
 * @return
 */
@Bean
public FilterRegistrationBean requestLoggerRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new RequestLoggerFilter());
    registration.addUrlPatterns("/*");
    registration.setOrder(3);
    return registration;
}
``
工具类
```java
public class IpUtil {
    /**
     * 获取客户端IP
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
}

2. 方式二

还可以采用直接继承spring提供的CommonsRequestLoggingFilter来打印请求日志。
CommonsRequestLoggingFilter也间接继承了OncePerRequestFilter

package org.springframework.web.filter;

import javax.servlet.http.HttpServletRequest;

/**
 * Simple request logging filter that writes the request URI
 * (and optionally the query string) to the Commons Log.
 *
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @since 1.2.5
 * @see #setIncludeQueryString
 * @see #setBeforeMessagePrefix
 * @see #setBeforeMessageSuffix
 * @see #setAfterMessagePrefix
 * @see #setAfterMessageSuffix
 * @see org.apache.commons.logging.Log#debug(Object)
 */
public class CommonsRequestLoggingFilter extends AbstractRequestLoggingFilter {

	@Override
	protected boolean shouldLog(HttpServletRequest request) {
		return logger.isDebugEnabled();
	}

	/**
	 * Writes a log message before the request is processed.
	 */
	@Override
	protected void beforeRequest(HttpServletRequest request, String message) {
		logger.debug(message);
	}

	/**
	 * Writes a log message after the request is processed.
	 */
	@Override
	protected void afterRequest(HttpServletRequest request, String message) {
		logger.debug(message);
	}

}

在这里插入图片描述

  1. 可以设置请求体里哪些内容可以被log

  2. shouldLog判断是否可以log ,定制log前缀后缀

  3. beforeRequest, afterRequest重写请求前后log的信息
    使用:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
 
import javax.servlet.http.HttpServletRequest;
 
 
@Configuration
public class CalendarLoggingFilter extends CommonsRequestLoggingFilter {
 
    private final Logger logger = LoggerFactory.getLogger(CalendarLoggingFilter.class);
    private static long beforeTime;
    private static long afterTime;
 
    public MyDeskCalendarLoggingFilter(){
        super.setIncludeClientInfo(false);
        super.setIncludeHeaders(false);
        super.setIncludePayload(true);
        super.setMaxPayloadLength(2000);
        super.setIncludeQueryString(true);
        
    }
 
    @Override
    protected boolean shouldLog(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        // Checks if request matches /api/calendarCompletionDate
        boolean shouldLog = requestURI.matches("^/api\\/calendarCompletionDate$");
 
        if (shouldLog) {
            long currentUserId = SecurityUtils.getCurrentAccountUserId();
            String method = request.getMethod();
            super.setBeforeMessagePrefix("Before request [" + method + "," + "currentUserId:" + currentUserId + ","); //Default is just Before request
            super.setAfterMessagePrefix("After request [" + method + "," + "currentUserId:" + currentUserId + ",");   //Default is just After request
        }
        return shouldLog;
    }
 
    @Override
    protected void beforeRequest(HttpServletRequest request, String message) {
        beforeTime = System.currentTimeMillis();
        MemoryLogUtil.logUsed("beforeRequest");//only shows at this moment in time
        super.beforeRequest(request, message);
    }
 
    @Override
    protected void afterRequest(HttpServletRequest request, String message) {
        afterTime = System.currentTimeMillis();
        logger.info("afterRequest: Time taken: " + (afterTime-beforeTime) + " in milliseconds");
        MemoryLogUtil.logUsed("afterRequest");//only shows at this moment in time
        super.afterRequest(request, message);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值