SpringMVC框架中如何使用Filter实现请求日志打印
这篇文章主要为大家展示了“SpringMVC框架中如何使用Filter实现请求日志打印”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“SpringMVC框架中如何使用Filter实现请求日志打印”这篇文章吧。
具体实现
日志记录过滤器
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;@Overridepublic 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(); //一定要flush,responseBody会被复用 responseWrapper.getMyOutputStream().myFlush(); } String params = JSONObject.toJSONString(paramMap); log.info(String.format(LOG_FORMATTER_IN, path, method, params, requestedIP, dateStart, responseBody, dateEnd,(dateEnd - dateStart));}private boolean checkFilter(String contentType) { boolean filterFlag = false;//是否继续拦截 for (String p : getContentTypes()) { if (StringUtils.contains(contentType, p)){ filterFlag = true; } } if (StringUtils.isEmpty(contentType)){ filterFlag = true; } return filterFlag;}}
Request包装器
@Slf4jpublic class MyRequestBodyReaderWrapper extends HttpServletRequestWrapper {//存放JSON数据主体private final byte[] body;public MyRequestBodyReaderWrapper(HttpServletRequest request) throws IOException { super(request); body = getBody(request).getBytes(Charset.forName("UTF-8"));}@Overridepublic ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } };}@Overridepublic BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(this.getInputStream()));}public static String getBody(ServletRequest 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) { log.error("MyRequestBodyReaderWrapper.getBody()异常-->",e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error("MyRequestBodyReaderWrapper.getBody()异常-->",e); } } if (reader != null) { try { reader.close(); } catch (IOException e) { log.error("MyRequestBodyReaderWrapper.getBody()异常-->",e); } } } return sb.toString();}}
RequestUtils
public class RequestUtils {private static final Logger logger = LoggerFactory.getLogger(RequestUtils.class);public static Map<String,String> getHeaders(HttpServletRequest request){ Map<String,String> headerMap = new HashMap<>(); List<String> headers = getCommonHeaders(); headers.add("Postman-Token"); headers.add("Proxy-Connection"); headers.add("X-Lantern-Version"); headers.add("Cookie"); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String headerName = headerNames.nextElement(); if (headers.contains(headerName)){ continue; } headerMap.put(headerName,request.getHeader(headerName)); } return headerMap;}public static Map<String, String> getUriTemplateVar(HttpServletRequest request) { NativeWebRequest webRequest = new ServletWebRequest(request); Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); return uriTemplateVars;}public static String getRealIP(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { //多次反向代理后会有多个ip值,第一个ip才是真实ip int index = ip.indexOf(","); if (index != -1) { return ip.substring(0, index); } else { return ip; } } ip = request.getHeader("X-Real-IP"); if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { return ip; } return request.getRemoteAddr();}public static Map<String,String> getRequestParamMap(HttpServletRequest request) { Map<String,String> paramMap = new HashMap<>(); //获取QueryString中的参数,GET方式 或application/x-www-form-urlencoded Map<String, String> queryParamMap = RequestUtils.getUriQueryVar(request); if (queryParamMap != null){ paramMap.putAll(queryParamMap); } //获取Body中的参数,POST/PATCH等方式,application/json Map<String,String> bodyParamMap = null; try { //当为POST请求且 application/json时,request被RequestFilter处理为wrapper类 if (!(request instanceof MyRequestBodyReaderWrapper)){ return paramMap; } MyRequestBodyReaderWrapper readerWrapper = (MyRequestBodyReaderWrapper) request; String requestBody = new String(readerWrapper.getBody(), "UTF-8"); if (com.zhongan.health.common.utils.StringUtils.isNotBlank(requestBody)){ bodyParamMap = JSONObject.parseObject(requestBody, new TypeReference<LinkedHashMap<String,String>>(){}, Feature.OrderedField); } } catch (Exception e) { logger.error("获取请求Body异常-->",e); } if (bodyParamMap != null){ paramMap.putAll(bodyParamMap); } return paramMap;}private static List<String> getCommonHeaders(){ List<String> headers = new ArrayList<>(); Class<HttpHeaders> clazz = HttpHeaders.class; Field[] fields = clazz.getFields(); for (Field field : fields) { field.setAccessible(true); if (field.getType().toString().endsWith("java.lang.String") && Modifier.isStatic(field.getModifiers())){ try { headers.add((String) field.get(HttpHeaders.class)); } catch (IllegalAccessException e) { logger.error("反射获取属性值异常-->",e); } } } return headers;}}
Response包装器
public class MyResponseWrapper extends HttpServletResponseWrapper {private ResponsePrintWriter writer;private MyServletOutputStream out;public MyResponseWrapper(HttpServletResponse response) { super(response);}@Overridepublic ServletOutputStream getOutputStream() throws IOException { //一定要先判断当前out为空才能去新建out对象,否则一次请求会出现多个out对象 if (out == null){ out = new MyServletOutputStream(super.getOutputStream()); } return out;}@Overridepublic PrintWriter getWriter() throws IOException { //一定要先判断当前writer为空才能去新建writer对象,否则一次请求会出现多个writer对象 if (writer == null){ writer = new ResponsePrintWriter(super.getWriter()); } return writer;}public ResponsePrintWriter getMyWriter() { return writer;}public MyServletOutputStream getMyOutputStream(){ return out;}}
自定义Writer
public class ResponsePrintWriter extends PrintWriter{private StringBuffer buffer;public ResponsePrintWriter(PrintWriter out) { super(out); buffer = new StringBuffer();}public String getContent(){ return buffer == null ? null : buffer.toString();}@Overridepublic void flush() { super.flush();}//清空buffer,以便下一次重新使用public void myFlush(){ buffer = null;}@Overridepublic void write(char[] buf, int off, int len) { super.write(buf, off, len); char[] destination = new char[len]; System.arraycopy(buf,off,destination,0,len); buffer.append(destination);}@Overridepublic void write(String s) { super.write(s); buffer.append(s);}}
自定义OutputStream
public class MyServletOutputStream extends ServletOutputStream {private ServletOutputStream outputStream;private StringBuffer buffer;public MyServletOutputStream(ServletOutputStream outputStream) { this.outputStream = outputStream; buffer = new StringBuffer();}@Overridepublic void write(int b) throws IOException { outputStream.write(b);}@Overridepublic void write(byte[] b, int off, int len) throws IOException { outputStream.write(b, off, len); byte[] bytes = new byte[len]; System.arraycopy(b, off, bytes, 0, len); buffer.append(new String(bytes,"UTF-8"));}@Overridepublic void write(byte[] b) throws IOException { outputStream.write(b);}@Overridepublic void flush() throws IOException { super.flush();}//清空buffer,以便下一次重新使用public void myFlush(){ outputStream = null; buffer = null;}public String getBuffer() { if (buffer != null){ return buffer.toString(); } return null;}}
总结
之前利用HttpServletRequest.getInputStream()和RequestWrapper实现了请求的requestBody获取,现在提出将一个请求的RequestBody和ResponseBody都提出来并打印日志&落入数据库,以便统计和查找问题。
查找资料后确定两种技术方案
使用AOP对所有Controller的方法进行环绕通知处理;
使用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;@Overridepublic 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(); //一定要flush,responseBody会被复用 responseWrapper.getMyOutputStream().myFlush(); } String params = JSONObject.toJSONString(paramMap); log.info(String.format(LOG_FORMATTER_IN, path, method, params, requestedIP, dateStart, responseBody, dateEnd,(dateEnd - dateStart));}private boolean checkFilter(String contentType) { boolean filterFlag = false;//是否继续拦截 for (String p : getContentTypes()) { if (StringUtils.contains(contentType, p)){ filterFlag = true; } } if (StringUtils.isEmpty(contentType)){ filterFlag = true; } return filterFlag;}}
Request包装器
@Slf4jpublic class MyRequestBodyReaderWrapper extends HttpServletRequestWrapper {//存放JSON数据主体private final byte[] body;public MyRequestBodyReaderWrapper(HttpServletRequest request) throws IOException { super(request); body = getBody(request).getBytes(Charset.forName("UTF-8"));}@Overridepublic ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } };}@Overridepublic BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(this.getInputStream()));}public static String getBody(ServletRequest 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) { log.error("MyRequestBodyReaderWrapper.getBody()异常-->",e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error("MyRequestBodyReaderWrapper.getBody()异常-->",e); } } if (reader != null) { try { reader.close(); } catch (IOException e) { log.error("MyRequestBodyReaderWrapper.getBody()异常-->",e); } } } return sb.toString();}}
RequestUtils
public class RequestUtils {private static final Logger logger = LoggerFactory.getLogger(RequestUtils.class);public static Map<String,String> getHeaders(HttpServletRequest request){ Map<String,String> headerMap = new HashMap<>(); List<String> headers = getCommonHeaders(); headers.add("Postman-Token"); headers.add("Proxy-Connection"); headers.add("X-Lantern-Version"); headers.add("Cookie"); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String headerName = headerNames.nextElement(); if (headers.contains(headerName)){ continue; } headerMap.put(headerName,request.getHeader(headerName)); } return headerMap;}public static Map<String, String> getUriTemplateVar(HttpServletRequest request) { NativeWebRequest webRequest = new ServletWebRequest(request); Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); return uriTemplateVars;}public static String getRealIP(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { //多次反向代理后会有多个ip值,第一个ip才是真实ip int index = ip.indexOf(","); if (index != -1) { return ip.substring(0, index); } else { return ip; } } ip = request.getHeader("X-Real-IP"); if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { return ip; } return request.getRemoteAddr();}public static Map<String,String> getRequestParamMap(HttpServletRequest request) { Map<String,String> paramMap = new HashMap<>(); //获取QueryString中的参数,GET方式 或application/x-www-form-urlencoded Map<String, String> queryParamMap = RequestUtils.getUriQueryVar(request); if (queryParamMap != null){ paramMap.putAll(queryParamMap); } //获取Body中的参数,POST/PATCH等方式,application/json Map<String,String> bodyParamMap = null; try { //当为POST请求且 application/json时,request被RequestFilter处理为wrapper类 if (!(request instanceof MyRequestBodyReaderWrapper)){ return paramMap; } MyRequestBodyReaderWrapper readerWrapper = (MyRequestBodyReaderWrapper) request; String requestBody = new String(readerWrapper.getBody(), "UTF-8"); if (com.zhongan.health.common.utils.StringUtils.isNotBlank(requestBody)){ bodyParamMap = JSONObject.parseObject(requestBody, new TypeReference<LinkedHashMap<String,String>>(){}, Feature.OrderedField); } } catch (Exception e) { logger.error("获取请求Body异常-->",e); } if (bodyParamMap != null){ paramMap.putAll(bodyParamMap); } return paramMap;}private static List<String> getCommonHeaders(){ List<String> headers = new ArrayList<>(); Class<HttpHeaders> clazz = HttpHeaders.class; Field[] fields = clazz.getFields(); for (Field field : fields) { field.setAccessible(true); if (field.getType().toString().endsWith("java.lang.String") && Modifier.isStatic(field.getModifiers())){ try { headers.add((String) field.get(HttpHeaders.class)); } catch (IllegalAccessException e) { logger.error("反射获取属性值异常-->",e); } } } return headers;}}
Response包装器
public class MyResponseWrapper extends HttpServletResponseWrapper {private ResponsePrintWriter writer;private MyServletOutputStream out;public MyResponseWrapper(HttpServletResponse response) { super(response);}@Overridepublic ServletOutputStream getOutputStream() throws IOException { //一定要先判断当前out为空才能去新建out对象,否则一次请求会出现多个out对象 if (out == null){ out = new MyServletOutputStream(super.getOutputStream()); } return out;}@Overridepublic PrintWriter getWriter() throws IOException { //一定要先判断当前writer为空才能去新建writer对象,否则一次请求会出现多个writer对象 if (writer == null){ writer = new ResponsePrintWriter(super.getWriter()); } return writer;}public ResponsePrintWriter getMyWriter() { return writer;}public MyServletOutputStream getMyOutputStream(){ return out;}}
自定义Writer
public class ResponsePrintWriter extends PrintWriter{private StringBuffer buffer;public ResponsePrintWriter(PrintWriter out) { super(out); buffer = new StringBuffer();}public String getContent(){ return buffer == null ? null : buffer.toString();}@Overridepublic void flush() { super.flush();}//清空buffer,以便下一次重新使用public void myFlush(){ buffer = null;}@Overridepublic void write(char[] buf, int off, int len) { super.write(buf, off, len); char[] destination = new char[len]; System.arraycopy(buf,off,destination,0,len); buffer.append(destination);}@Overridepublic void write(String s) { super.write(s); buffer.append(s);}}
自定义OutputStream
public class MyServletOutputStream extends ServletOutputStream {private ServletOutputStream outputStream;private StringBuffer buffer;public MyServletOutputStream(ServletOutputStream outputStream) { this.outputStream = outputStream; buffer = new StringBuffer();}@Overridepublic void write(int b) throws IOException { outputStream.write(b);}@Overridepublic void write(byte[] b, int off, int len) throws IOException { outputStream.write(b, off, len); byte[] bytes = new byte[len]; System.arraycopy(b, off, bytes, 0, len); buffer.append(new String(bytes,"UTF-8"));}@Overridepublic void write(byte[] b) throws IOException { outputStream.write(b);}@Overridepublic void flush() throws IOException { super.flush();}//清空buffer,以便下一次重新使用public void myFlush(){ outputStream = null; buffer = null;}public String getBuffer() { if (buffer != null){ return buffer.toString(); } return null;}}
以上是“SpringMVC框架中如何使用Filter实现请求日志打印”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341