拿来吧你——一个类帮你搞定SpringBoot中的请求日志打印

news/2024/11/1 22:30:01/

拿来吧你——一个类帮你搞定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之前,记得先点个赞哦。


http://www.ppmy.cn/news/60500.html

相关文章

计算机网络笔记:HTTP协议关于缓存

强缓存 强缓存分为两种情况&#xff0c;一种是发送HTTP请求&#xff0c;一种不需要发送。 首先检查强缓存&#xff0c;这个阶段不需要发送HTTP请求。通过查找不同的字段来进行&#xff0c;不同的HTTP版本所以不同。 HTTP1.0版本&#xff0c;使用的是Expires&#xff0c;HTTP1…

51单片机的中断系统

文章目录 51单片机的中断系统一、中断系统简介程序中断中断可以实现的主要功能中断执行过程 二、51单片中断系统使用中断源中断控制中断允许中断判优中断函数定义 三、中断系统使用案例例一:P3.2 引脚上接了一个按键&#xff0c;P0口连接了8个发光二极管&#xff0c; 要求每次按…

vue修饰符的使用

事件修饰符&#xff1a; 在处理事件时调用 event.preventDefault() 或 event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用&#xff0c;但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。 为解决这一问题&#xff0c;Vue 为 v-on 提供了事件…

uni-app获取手机号-获取用户地理位置-根据位置获取经纬度跳转高德

一.获取手机号 1.获取手机号首先要先登录拿到code&#xff0c;用code去获取session_key 2.获取 code需要知道自己的AppID(小程序ID)和AppSecret(小程序密钥) 3.解密后得到手机号 登录微信公众平台拿到自己的AppID(小程序ID)和AppSecret(小程序密钥) 微信公众平台 获取sessio…

各SQL引擎的SQL转换过程对比

SQL引擎 参考文档:高级语言的解析过程—解析树 从 MySQL、Oracle、TiDB、CK,到 Hive、HBase、Spark,从关系型数据库到大数据计算引擎,他们大都可以借助 SQL 引擎,实现 “接受一条 sql 语句然后返回查询结果” 的功能。 他们核心的执行逻辑都是一样的,大致可以通过下面…

Java 基础进阶篇(十)—— Java集合详细总结

文章目录 一、集合类体系结构二、Collection系列集合2.1 Collection 集合体系2.2 Collection 集合体系特点2.3 Collection 常用API2.4 Collection 集合的遍历方式2.4.1 方式一&#xff1a;迭代器2.4.2 方式二&#xff1a;foreach&#xff08;增强for循环&#xff09;2.4.3 方式…

Spring事务失效场景

&#xff08;1&#xff09;事务方法所在的类没有加载到容器中&#xff08;未被Spring管理的Bean&#xff09; 如果一个被注解的类不是由Spring容器来创建的&#xff0c;比如手动new对象&#xff0c;那么该类的事务注解则不会生效。可以通过将该类交给Spring容器来解决此问题。 …

【Java】BitSet的使用

BitSet和boolean[]为什么不用boolean[]BitSet相关的API为什么不用boolean[]? boolean[]占用的内存比较大。 以1024个位置为例。 boolean[1024]总共占用1040 bytes,包括对象头等大小。(对象头20bytes) boolean[] bits = new boolean[1024];使用BitSet(1024)的话,总共占用…