拿来吧你——一个类帮你搞定SpringBoot中的请求日志打印
日常开发工作中避免不了要打印请求日志,这个功能几乎在所有的项目中都需要编写一次,重复的次数多了,难免会感觉繁琐,因此打算搞一个通用类把这块功能拆出来。
废话不多说——先上代码。
我是代码
@WebFilter(filterName = "logFilter", urlPatterns = "/planet/*")
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class LogFilter implements Filter {private static final Set<String> IGNORE_PATHS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("/**/swagger-ui/**", "/**/swagger-resources/**", "/**/api-docs")));@SneakyThrows@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;// 忽略文件上传String contentType = httpServletRequest.getHeader("content-type");if (contentType != null && contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {filterChain.doFilter(httpServletRequest, httpServletResponse);return;}// 忽略指定urlString path = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()).replaceAll("[/]+$", "");PathMatcher matcher = new AntPathMatcher();boolean isIgnore = IGNORE_PATHS.stream().anyMatch(ignore -> matcher.match(ignore, path));if (isIgnore) {filterChain.doFilter(httpServletRequest, httpServletResponse);return;}ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);long before = System.currentTimeMillis();log.info("========================================== Request Start ==========================================");log.info("URL : {}", httpServletRequest.getRequestURL().toString());Map<String, String> headers = new HashMap<>(32);Enumeration<String> names = httpServletRequest.getHeaderNames();while (names.hasMoreElements()) {String name = names.nextElement();headers.put(name, httpServletRequest.getHeader(name));}log.info("Headers : {}", headers);log.info("HTTP Method : {}", httpServletRequest.getMethod());log.info("IP : {}", httpServletRequest.getRemoteAddr());log.info("Request Body : {}", JSON.toJSON(requestWrapper.getBody()));filterChain.doFilter(requestWrapper, wrapperResponse);byte[] content = wrapperResponse.getContent();log.info("Response Status : {}", wrapperResponse.getStatus());log.info("Response Content : {}", JSON.toJSON(new String(content)));log.info("Time-Consuming : {} ms", System.currentTimeMillis() - before);log.info("=========================================== End ===========================================");ServletOutputStream out = httpServletResponse.getOutputStream();out.write(content);out.flush();}
}class ResponseWrapper extends HttpServletResponseWrapper {private final ByteArrayOutputStream buffer;private final ServletOutputStream out;public ResponseWrapper(HttpServletResponse httpServletResponse) {super(httpServletResponse);buffer = new ByteArrayOutputStream();out = new WrapperOutputStream(buffer);}@Overridepublic ServletOutputStream getOutputStream() {return out;}@Overridepublic void flushBuffer() throws IOException {if (out != null) {out.flush();}}public byte[] getContent() throws IOException {flushBuffer();return buffer.toByteArray();}static class WrapperOutputStream extends ServletOutputStream {private final ByteArrayOutputStream bos;public WrapperOutputStream(ByteArrayOutputStream bos) {this.bos = bos;}@Overridepublic void write(int b) {bos.write(b);}@Overridepublic boolean isReady() {return false;}@Overridepublic void setWriteListener(WriteListener arg0) {}}}class RequestWrapper extends HttpServletRequestWrapper {private final String body;public RequestWrapper(HttpServletRequest request) {super(request);StringBuilder stringBuilder = new StringBuilder();BufferedReader bufferedReader = null;InputStream inputStream = null;try {inputStream = request.getInputStream();if (inputStream != null) {bufferedReader = new BufferedReader(new InputStreamReader(inputStream));char[] charBuffer = new char[128];int bytesRead;while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {stringBuilder.append(charBuffer, 0, bytesRead);}}} catch (IOException ignored) {} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (bufferedReader != null) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}body = stringBuilder.toString();}@Overridepublic ServletInputStream getInputStream() {final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());return new ServletInputStream() {@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}@Overridepublic int read() {return byteArrayInputStream.read();}};}@Overridepublic BufferedReader getReader() {return new BufferedReader(new InputStreamReader(this.getInputStream()));}public String getBody() {return this.body;}}
相关问题
这个类是干啥用的?
这个类是一个过滤器,用来在Request与Response之间打印请求与响应日志。
tip:一次编写、处处粘贴。
这个类的处理逻辑是啥?
众所周知,Servlet用来处理用户请求并且对用户请求进行响应,而Filter则是在请求达到Servlet之前,服务器响应返回到Servlet之前对数据进行的一次预处理。
里面几个内部类是干啥用的?
因为request、response都是以流的形式进行传输的,而流有一个特性就是只允许读取一次,因此需要对Request与Response进行一次扩展,使其支持多次读取,其实现方式为:在第一次读取的时候将数据缓存起来,后续读取时直接读取缓存的内容。
还有其他问题吗?
过滤器虽然功能比较强大,但是其影响面也是十分巨大的。
因为过滤器会在所有的请求前后做一些预处理操作,如果预处理操作比较耗时则会降低部分系统性能(QPS、TPS)。
Ctrl+C之前,记得先点个赞哦。