统一记录日志

统一日志记录

项目中经常会遇到日志打印,通常的做法是使用Filter统一拦截处理。但如果想打印body里的数据,会出现不能再次读取的问题,servlet的requestbody以及response的body只能被读取一次,一旦流被读取了,就无法再次读取了。

推荐使用Spring本身提供的Wrapper类来解决此问题,当然也可以自己封装个Wrapper类,继承HttpServletRequestWrapper即可。本文介绍Spring提供的Wrapper的使用。

caching wrapper

ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

spring提供了ContentCachingRequestWrapper以及ContentCachingResponseWrapper两个类,来解决这类问题。 读取完body之后再设置回去。

wrappedResponse.copyBodyToResponse();

Filter
完整的filter如下,可参考修改使用:


import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Objects;
import java.util.UUID;


@Component
public class LogFilter extends OncePerRequestFilter {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final String TRACE_ID = "traceId";
    public static final int SLOW_TIME_MILLIS = 5000;
    public static final int LOG_MAX_LENGTH = 1024;

    @Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String url = null;
        String headers = null;
        String traceId = "";

        StopWatch watch = null;
        try {
            url = getCompleteUrl(request);
            // 暂时不打印headers内容
            // headers = getHeaders(request).toString();
            // 设置traceId
            traceId = request.getHeader(TRACE_ID);
            if (StringUtils.isBlank(traceId)) {
                traceId = UUID.randomUUID().toString();
            }
            MDC.put(TRACE_ID, traceId);

            request = new ContentCachingRequestWrapper(request);
            response = new ContentCachingResponseWrapper(response);

            watch = new StopWatch();
            watch.start();

            logger.info("start request, ip:{} host:{} traceId:{} url:{} header:{} body:{}", getRemortIP(request),
                    request.getRemoteHost(), traceId, url, headers, getRequestBody(request));
            filterChain.doFilter(request, response);

        } finally {
            if (watch != null) {
                watch.stop();
                if (watch.getTotalTimeMillis() > SLOW_TIME_MILLIS && !url.endsWith(".js")) {
                    logger.info("end slow request, ip:{} host:{} traceId:{} url:{} header:{} cost:{} ms resp:{}", getRemortIP(request),
                            request.getRemoteHost(), traceId, url, headers, watch.getTotalTimeMillis(), getResponseBody(response));
                } else {
                    logger.info("end request, ip:{} host:{} traceId:{} url:{} header:{} cost:{} ms resp:{}", getRemortIP(request),
                            request.getRemoteHost(), traceId, url, headers, watch.getTotalTimeMillis(), getResponseBody(response));
                }
            }
            MDC.clear();
            updateResponse(response);
        }
    }

    @Override
    public void destroy() {

    }


    private String getRequestBody(HttpServletRequest request) {
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper == null) {
            return "";
        }
        return getBody(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
    }

    private String getBody(byte[] contentAsByteArray, String characterEncoding) {
        String body = "";
        try {
            body = IOUtils.toString(contentAsByteArray, characterEncoding);
            if (body == null) {
                return body;
            }
            // 最多打印1024字符
            ///body = body.replaceAll("\r\n", "");
            if (body.length() > LOG_MAX_LENGTH) {
                body = body.substring(0, LOG_MAX_LENGTH) + "...";
            }
        } catch (Exception e) {
            // NOOP
        }
        return body;
    }

    /**
     * 获取 response body
     *
     * @param response
     * @return 最长返回1024字符,避免打印过长
     */
    private String getResponseBody(HttpServletResponse response) {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (responseWrapper == null) {
            return "";
        }
        return getBody(responseWrapper.getContentAsByteArray(), "UTF-8");
    }

    private void updateResponse(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        Objects.requireNonNull(responseWrapper).copyBodyToResponse();
    }

    private String getCompleteUrl(HttpServletRequest request) {
        String url = request.getRequestURL().toString();
        String queryString = "";
        if (StringUtils.isNotEmpty(request.getQueryString())) {
            try {
                queryString = URLDecoder.decode(request.getQueryString(), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                logger.error("URLDecoder.decode error:" + e.getMessage(), e);
            }
            url = url + "?" + queryString;
        }
        return url;
    }

    /**
     * 获取header信息
     *
     * @param request
     * @return headersMap
     */
    private JSONObject getHeaders(HttpServletRequest request) {
        JSONObject map = new JSONObject();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    private String getRemortIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

}


posted @ 2021-03-29 20:52  huonan  阅读(511)  评论(0编辑  收藏  举报