SpringMVC框架中利用Filter实现请求日志打印
之前利用HttpServletRequest.getInputStream()和RequestWrapper实现了请求的requestBody获取,现在提出将一个请求的RequestBody和ResponseBody都提出来并打印日志&落入数据库,以便统计和查找问题。查找资料后确定两种技术方案:
1. 使用AOP对所有Controller的方法进行环绕通知处理;
2. 使用Filter拦截所有的Request和Response,并获取body。
最后选择了第二种方式,具体实现记录如下。
具体实现
日志记录过滤器
public class RequestFilter implements Filter{ private static final String LOG_FORMATTER_IN = "请求路径:{%s},请求方法:{%s},参数:{%s},来源IP:{%s},请求开始时间{%s},返回:{%s},请求结束时间{%s},用时:{%s}ms,操作类型:{%s},操作人:{%s}"; public static final String USER_TOKEN_REDIS_PREFIX = "token_prefix"; private static final Logger log = LoggerFactory.getLogger(RequestFilter.class); //request拦截的conten-type列表 private List<String> contentTypes; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; //请求路径 String path = httpServletRequest.getRequestURI(); String method = httpServletRequest.getMethod(); //所有请求参数的Map Map<String,String> paramMap = new HashMap<>(); //请求的真实IP String requestedIP = RequestUtils.getRealIP(httpServletRequest); //是否拦截并包装请求,如果需要拦截则会获取RequestBody,一般为application/json才拦截 boolean filterRequestFlag = checkFilter(request.getContentType()); if (filterRequestFlag) { httpServletRequest = new MyRequestBodyReaderWrapper(httpServletRequest); } //获取所有queryString和requestBody Map<String, String> requestParamMap = RequestUtils.getRequestParamMap(httpServletRequest); if (requestParamMap != null && !requestParamMap.isEmpty()){ paramMap.putAll(requestParamMap); } //获取header参数 Map<String, String> headerMap = RequestUtils.getHeaders(httpServletRequest); if (headerMap != null && !headerMap.isEmpty()){ paramMap.putAll(headerMap); } //获取路径参数 Map<String,String> uriTemplateMap = RequestUtils.getUriTemplateVar(httpServletRequest); if (uriTemplateMap != null && !uriTemplateMap.isEmpty()){ paramMap.putAll(uriTemplateMap); } //包装Response,重写getOutputStream()和getWriter()方法,并用自定义的OutputStream和Writer来拦截和保存ResponseBody MyResponseWrapper responseWrapper = new MyResponseWrapper(httpServletResponse); //请求开始时间 Long dateStart = System.currentTimeMillis(); //Spring通过DispatchServlet处理请求 chain.doFilter(httpServletRequest, responseWrapper); //请求结束时间 Long dateEnd = System.currentTimeMillis(); String responseBody; if (responseWrapper.getMyOutputStream() == null){ if (responseWrapper.getMyWriter() != null){ responseBody = responseWrapper.getMyWriter().getContent(); //一定要flush,responseBody会被复用 responseWrapper.getMyWriter().myFlush(); } }else { responseBody = responseWrapper.getMyOutputStream().getBuffer(); <