springboot如何优雅的打印项目日志
文章目录
- 如何优雅的打印项目日志
- 原理
- 实现
- 日志打印Filter
- 注入容器
如何优雅的打印项目日志
框架
springboot
原理
使用filter拦截请求,打印出请求、响应,及耗时

知识点
1、OncePerRequestFilter
Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.
过滤器基类,旨在保证在任何 servlet 容器上每个请求调度一次执行。
兼容多种servlet版本,保证在任何 servlet 容器上每个请求调度一次执行。
2、ContentCachingRequestWrapper,ContentCachingResponseWrapper
HttpServletRequest 包装器,用于缓存从输入流和读取器读取的所有内容,并允许通过字节数组检索此内容。
源代码如下,第一次获取输入流时,复制了一份

因为request的数据流只能读取一次,通过过滤器读取一次后,后面的业务处理会读不到数据
通过ContentCachingRequestWrapper将请求包装,可以进行多次读取
实现
继承OncePerRequestFilter,重写doFilterInternal方法
日志打印Filter
import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;@Slf4j
public class LogFilter extends OncePerRequestFilter {private Integer printMaxSize;public LogFilter() {}public LogFilter(Integer printMaxSize) {if (printMaxSize == null) {printMaxSize = 1024;}this.printMaxSize = printMaxSize;}@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {if (isAsyncDispatch(httpServletRequest)) {filterChain.doFilter(httpServletRequest, httpServletResponse);} else {doLogFilter(getRequestWrapper(httpServletRequest), getResponseWrapper(httpServletResponse), filterChain);}}private void doLogFilter(ContentCachingRequestWrapper requestWrapper, ContentCachingResponseWrapper responseWrapper, FilterChain filterChain) throws IOException, ServletException {StringBuilder sb = new StringBuilder();sb.append(System.lineSeparator()).append("Method: [").append(requestWrapper.getMethod()).append("] ").append("URI: ").append(requestWrapper.getRequestURI()).append(System.lineSeparator());long start = System.currentTimeMillis();filterChain.doFilter(requestWrapper, responseWrapper);try {sb.append(getParams(requestWrapper)).append(System.lineSeparator());sb.append(getBody(requestWrapper));sb.append(getResponse(responseWrapper)).append(System.lineSeparator());} catch (Exception e) {log.warn("日志打印失败 e:{}", Throwables.getStackTraceAsString(e));} finally {responseWrapper.copyBodyToResponse();long end = System.currentTimeMillis();sb.append("use time :").append(end - start).append("ms");log.info(sb.toString());}}private String getBody(ContentCachingRequestWrapper requestWrapper) {StringBuilder sb = new StringBuilder();String contentType = requestWrapper.getContentType();if (requestWrapper.getMethod().equalsIgnoreCase("POST")&& (MediaType.APPLICATION_JSON_VALUE.equals(contentType)|| MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType))) {sb.append("body: ").append(System.lineSeparator());sb.append(new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8));sb.append(System.lineSeparator());}return sb.toString();}private String getResponse(ContentCachingResponseWrapper responseWrapper) {String responseStr = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);responseStr = responseStr.length() > printMaxSize ? responseStr.substring(0, printMaxSize) : responseStr;return "response: " + responseStr;}private String getParams(ContentCachingRequestWrapper requestWrapper) {Enumeration<String> enumeration = requestWrapper.getParameterNames();StringBuilder sb = new StringBuilder();sb.append("params: ");while (enumeration.hasMoreElements()) {String paramName = enumeration.nextElement();sb.append(paramName);sb.append(" : ");sb.append(requestWrapper.getParameter(paramName));sb.append(", ");}if (sb.length() > 2) {sb.replace(sb.length() - 2, sb.length(), "");}return sb.toString();}private ContentCachingResponseWrapper getResponseWrapper(HttpServletResponse httpServletResponse) {if (httpServletResponse instanceof ContentCachingResponseWrapper) {return (ContentCachingResponseWrapper) httpServletResponse;} else {return new ContentCachingResponseWrapper(httpServletResponse);}}private ContentCachingRequestWrapper getRequestWrapper(HttpServletRequest httpServletRequest) {if (httpServletRequest instanceof ContentCachingRequestWrapper) {return (ContentCachingRequestWrapper) httpServletRequest;} else {return new ContentCachingRequestWrapper(httpServletRequest, printMaxSize);}}
}
注入容器
@Configuration
public class FilterConfig {/*** 日志长度限制*/private Integer printMaxSize = 1024;@Beanpublic LogFilter getLogFilter() {return new LogFilter(printMaxSize);}}
相关文章:
springboot如何优雅的打印项目日志
文章目录如何优雅的打印项目日志原理实现日志打印Filter注入容器如何优雅的打印项目日志 框架 springboot 原理 使用filter拦截请求,打印出请求、响应,及耗时 知识点 1、OncePerRequestFilter Filter base class that aims to guarantee a single …...
【JAVA程序设计】(C00127)基于SSM+vue开发的音乐播放管理系统-有文档
基于SSMvue开发的音乐管理系统-有文档项目简介项目获取开发环境项目技术运行截图项目简介 基于ssm框架vue以及html前台的开发的音乐管理系统共分为二个角色:管理员、用户 管理员角色包含以下功能: 登录、个人中心(修改密码、个人信息修改&am…...
C#|调用C/C++动态库
参考:C#总结(四)调用C动态库(https://www.shuzhiduo.com/A/A2dmV49qze/) 文章目录C#加载C动态库C#加载C#动态库涉及到的概念知识:托管DLL和非托管DLL的区别(https://www.tinymind.net.cn/articl…...
让chatGPT当我的老师如何? 通过和chatGPT交互式学习,了解在ES中,一条JSON数据是如何写到磁盘上的
最近一直有一个问题,如鲠在喉。争取早一天解决,早一天踏踏实实的睡觉。 问题是:在ES中,一条JSON数据是如何写入到磁盘上的? 如何解决这个问题?我想到了chatGPT,还有lucene的学习资料。这篇文章&…...
chapter-7数据库事务
以下课程来源于MOOC学习—原课程请见:数据库原理与应用 考研复习 DBMS保证系统中一切事务的原子性、一致性、隔离性和持续性 DBMS必须对事务故障、系统故障和介质故障进行恢复 恢复中最经常使用的技术:数据库转储和登记日志文件 恢复的基本原理&#…...
阿里本地生活再出发:口碑入高德,备战美团、抖音
配图来自Canva可画 近日,有传言称高德地图将和阿里本地生活旗下的到店业务口碑正式合并,未来阿里旗下所有的本地生活到店业务都将统一整合在高德地图的入口中。3月22日,高德地图正式确认了此事,并表示高德地图作为“出门好生活开…...
SSM学习记录3:响应(注释方式 + SprigMVC项目 + 2022发布版本IDEA)
响应 ResponseBody注解的作用是将当前控制器中方法的返回值作为响应体 1.返回页面 无需在方法上进行ResponseBody注解,只需RequestMapping匹配地址,并且返回值为带后缀的页面名字符串 前面学习中除了json数据,所有带ResponseBody注解的方法…...
Linux·gcc 编译优化简介
1、gcc 编译优化简介 gcc 提供了为了满足用户不同程度的的优化需要,提供了近百种优化选项,用来对 { 编译时间,目标文件长度,执行效率 } 这个三维模型进行不同的取舍和平衡。优化的方法不一而足,总体上将有以下几类&…...
【电子学会】2022年12月图形化一级 -- 潜水
潜水 暑假小雨和爸爸去玩了潜水,他见到了各种各样的海洋生物。 1. 准备工作 (1)添加背景“Underwater 2”; (2)删除小猫角色,添加角色“Diver2”、“Fish”、“Jellyfish”、“Shark”; (3)为背景添加声音“Xylo2”。 2. 功能实现 (1)点击绿旗,播放背景音乐…...
MySQL日期时间函数汇总、时间格式转换方法
MySQL日期时间函数汇总、时间格式转换方法时间函数日期时间格式转换date_format函数EXTRACT()DATE_ADD()DATE_SUB()DATEDIFF函数时间函数 函数描述NOW()返回当前的日期和时间CURDATE()返回当前的日期CURTIME()返回当前的时间DATE()返回日期或日期/时间表达式的日期部分HOUR()获…...
【CSS】使用绝对定位 / 浮动解决外边距塌陷问题 ( 为父容器 / 子元素设置内边距 / 边框 | 为子元素设置浮动 | 为子元素设置绝对定位 )
文章目录一、外边距塌陷描述1、没有塌陷的情况2、外边距塌陷情况二、传统方法解决外边距塌陷 - 为父容器 / 子元素设置内边距 / 边框三、使用浮动解决外边距塌陷 - 为子元素设置浮动四、使用绝对定位解决外边距塌陷 - 为子元素设置绝对定位一、外边距塌陷描述 在 标准流的父盒子…...
前端手写综合考题
1 实现一个 // 使用 promise来实现 sleepconst sleep (time) > {return new Promise(resolve > setTimeout(resolve, time))}sleep(1000).then(() > {// 这里写你的骚操作}) sleep 函数,比如 sleep(1000) 意味着等待1000毫秒 2 给定两个数组,…...
数据结构-排序
本节目标: 1.排序的概念及其运用 2.常见排序算法的实现 3.排序算法复杂度及稳定性分析 1.排序的概念及其应用 1.1排序的概念 排序就是按照某个我们设定的关键字,或者关键词,递增或者递减,完成这样的操作就是排序。 1.2排…...
ROS话题通信自定义+发布订阅代码--03
话题通信自定义msg 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味…...
【MySQL】实验七 视图
文章目录 1. 建立city值为上海、北京的顾客视图2. 建立城市为上海的客户2016年的订单信息视图3. SQL视图:建立视图AVG_CJ4. SQL视图:建立视图IS_STUDENT5. SQL视图:建立视图CJ_STUDENT6. SQL视图:根据视图CJ_STUDENT创建视图CJ_TJ1. 建立city值为上海、北京的顾客视图 建立…...
Linux常见操作命令【三】
一、系统资源 1.1 ps(process staus) ps -ef e显示所有进程、f全格式 ps -aux 显示所有包含其他使用者的进程 ps -ef | grep CCC 查找含有CCC进程的格式 ps -u username 显示指定进程用户信息1.2 kill kill 12345 杀死进程12345 kill -KILL…...
C-关键字(下)
文章目录循环控制switch-case-break-defaultdo-while-forgetchar()break-continuegotovoidvoid*returnconstconst修饰变量const修饰数组const修饰指针指针补充const 修饰返回值volatilestruct柔型数组union联合体联合体空间开辟问题利用联合体的性质,判断机器是大端还是小端enu…...
关于电商商品数据API接口列表,你想知道的(详情页、Sku信息、商品描述、评论问答列表)
目录 一、商品数据API接口列表 二、商品详情数据API调用代码item_get 三、获取sku详细信息item_sku 四、获得淘宝商品评论item_review 五、数据说明文档 进入 一、商品数据API接口列表 二、商品详情数据API调用代码item_get <?php// 请求示例 url 默认请求参数已经URL…...
232:vue+openlayers选择左右两部分的地图,不重复,横向卷帘
第232个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中自定义js实现横向卷帘。这个示例中从左右两个选择框中来选择不同的地图,做了不重复的处理,即同一个数组,两部分根据选择后的状态做disabled处理,避免重复选择。 直接复制下面的 vue+openlayers…...
溯源取证-内存取证 高难度篇
今天的场景依然是windows场景,只不过此次场景分为两个镜像,本次学习主要学习如何晒别钓鱼邮件、如何提取钓鱼邮件、如何修复损坏的恶意文件、如何提取DLL动态链接库文件 本次需要使用的工具: volatility_2.6_lin64_standalone readpst clams…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...
