002-日志增强版

日志增强版

  • 一、需求
  • 二、引入依赖
  • 三、配置日志处理切面
  • 四、配置RequestWrapper
  • 五、效果展示

一、需求

需要打印请求参数和返回参数

二、引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

三、配置日志处理切面

@EnableAspectJAutoProxy(proxyTargetClass = true)
@Order(Integer.MAX_VALUE)
@Aspect
@Component
@Slf4j
public class WebLogAspect {/*** 拦截所有Controller*/@Pointcut("execution(* org.example..controller..*.*(..))")public void cutController() {}@Around("cutController()")public Object traceMethod(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();Date requestStartTime = new Date();String requyestUrl = request.getRequestURL().toString();String requestParam = JSONUtil.toJsonStr(request.getParameterMap());String traceId = MDC.get(GlobalConstants.TRACE_ID);String requestBody;String requestHeader;if (request instanceof RequestWrapper) {RequestWrapper wrapper = (RequestWrapper) request;requestBody = wrapper.getRequestBodyStr();requestHeader = JSONUtil.toJsonStr(wrapper.getHeaders());} else {requestBody = "";requestHeader = "";}String contentType = request.getHeader("content-type");Object returnVal = null;Exception nowException = null;try {returnVal = proceedingJoinPoint.proceed();} catch (Exception e) {nowException = e;throw nowException;} finally {Exception finalNowException = nowException;Object finalReturnVal = returnVal;CompletableFuture.runAsync(() -> {Date requestEndTime = new Date();Map<String, Object> param = new LinkedHashMap<>();param.put("requestTime", DateUtil.formatDateTime(requestStartTime));param.put("requestUrl", requyestUrl);param.put("requestTimeUse", requestEndTime.getTime() - requestStartTime.getTime());param.put("requestParam", requestParam);param.put("requestHeader", requestHeader);param.put("traceId", traceId);if (StringUtils.isBlank(contentType) || !contentType.contains("multipart")) {// 文件上传相关不记录param.put("requestBody", requestBody);}param.put("isException", false);param.put("isBusinessException", false);if (finalNowException == null) {String requestResult = finalReturnVal != null ? JSONUtil.toJsonStr(finalReturnVal) : "null";if (requestResult.contains("异常") && !requestResult.contains("0000")) {param.put("isException", true);param.put("exceptionInfo", "请求出现异常但被捕获,请到对应服务器查看异常日志!");} else {param.put("requestResult", requestResult);}} else {param.put("isException", true);param.put("exceptionInfo", ExceptionUtils.getStackTrace(finalNowException));}log.debug("==========报文参数[{}]",JSONUtil.toJsonStr(param));});}return returnVal;}}

四、配置RequestWrapper

@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {private final String charset = "UTF-8";//参数字节数组@Getterprivate byte[] requestBody;//Http请求对象private final HttpServletRequest request;//请求参数private final Map<String, String[]> params = new HashMap<>();//请求headerprivate final Map<String, String> header = new HashMap<>();private final String contentType;public RequestWrapper(HttpServletRequest request) {super(request);this.request = request;this.contentType = this.getHeader("content-type");this.initParams();}private void initParams() {Map<String, String[]> parameterMap = request.getParameterMap();  //接受客户端的数据if (MapUtils.isNotEmpty(parameterMap)) {for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {String name = entry.getKey();String[] values = entry.getValue();if (values == null) {params.put(name, new String[]{});continue;}String[] newvalues = new String[values.length];for (int i = 0; i < values.length; i++) {String value = values[i];newvalues[i] = convert(value);}params.put(name, newvalues);}return;} else {parseFormUrlencodedParameters();}}/*** @return* @throws IOException*/@Overridepublic ServletInputStream getInputStream() throws IOException {/*** 每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中* 解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题*/if (null == this.requestBody) {ByteArrayOutputStream baos = new ByteArrayOutputStream();IOUtils.copy(request.getInputStream(), baos);this.requestBody = baos.toByteArray();}final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);return new ServletInputStream() {@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener listener) {}@Overridepublic int read() {return bais.read();}};}@SneakyThrowspublic String getRequestBodyStr() {if (requestBody != null && requestBody.length > 0) {return new String(this.getRequestBody(), charset);} else {try {this.getInputStream();} catch (IOException e) {log.error("", e);}if (requestBody != null && requestBody.length > 0) {return new String(this.getRequestBody(), charset);}}return "";}@Overridepublic Map<String, String[]> getParameterMap() {return this.params;}@Overridepublic String getParameter(String name) {String[] values = params.get(name);return values == null || values.length == 0 ? null : values[0];}@Overridepublic String[] getParameterValues(String key) {return params.get(key);}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(this.getInputStream()));}public Map<String, String> getHeaders() {if (MapUtils.isNotEmpty(header)) {return header;}Enumeration<String> headers = this.getHeaderNames();while (headers.hasMoreElements()) {String key = (String) headers.nextElement();String value = this.getHeader(key);header.put(key, value);}return header;}private boolean isContentTypeFormUrlencoded() {if (StringUtils.isNotBlank(contentType) && contentType.contains("x-www-form-urlencoded")) {return true;}return false;}/*** 如果请求类型为[x-www-form-urlencoded],如果body有值,则需要将body的值转到request param中, 由于该类型的特性【如果先获取参数值,那么body则为空】,那么最后将body手动清空*/private void parseFormUrlencodedParameters() {if (!isContentTypeFormUrlencoded()) {return;}if (requestBody == null || requestBody.length == 0) {return;}String bodyStr = this.getRequestBodyStr();if (StringUtils.isBlank(bodyStr)) {return;}String[] paramsArray = bodyStr.split("&");for (String s : paramsArray) {String[] p = s.split("=");if (p.length == 2) {if (StringUtils.isBlank(p[0]) || StringUtils.isBlank(p[1]) || params.containsKey(p[0])) {continue;}params.put(p[0], new String[]{UrlUtil.getURLDecoderString(p[1])});}}//请空body数据requestBody = new byte[]{};}private String convert(String target) {if (StringUtils.isBlank(target)) {return target;}String requestCharset = "ISO-8859-1";//检查请求是否指定编码类型if (StringUtils.isNotBlank(contentType) && contentType.contains("charset") && !contentType.contains("charset ")) {String[] array = contentType.split(";");for (int i = 0; i < array.length; i++) {String[] p = array[i].split("=");if (p.length == 2) {if ("charset".equals(p[0].trim())) {requestCharset = p[1].trim();}}}}try {return new String(target.trim().getBytes(requestCharset), charset);} catch (UnsupportedEncodingException e) {return target;}}
}

五、效果展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/483323.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spire.PDF for .NET【页面设置】演示:旋放大 PDF 边距而不改变页面大小

PDF 页边距是正文内容和页面边缘之间的空白。与 Word 不同&#xff0c;PDF 文档中的页边距不易修改&#xff0c;因为 Adobe 不提供任何功能供用户自由操作页边距。但是&#xff0c;您可以更改页面缩放比例&#xff08;放大/压缩内容&#xff09;或裁剪页面以获得合适的页边距。…

服务器数据恢复—EVA存储硬盘磁头和盘片损坏离线的数据恢复案例

服务器存储数据恢复环境&故障&#xff1a; 一台HP EVA存储中有23块硬盘&#xff0c;挂接到一台windows server操作系统的服务器。 EVA存储上有三个硬盘指示灯亮黄灯&#xff0c;此刻存储还能正常使用。管理员在更换硬盘的过程中&#xff0c;又出现一块硬盘对应的指示灯亮黄…

探索仓颉编程语言:官网上线,在线体验与版本下载全面启航

文章目录 每日一句正能量前言什么是仓颉编程语言仓颉编程语言的来历如何使用仓颉编程语言在线版本版本下载后记 每日一句正能量 当你被孤独感驱使着去寻找远离孤独的方法时&#xff0c;会处于一种非常可怕的状态。因为无法和自己相处的人也很难和别人相处&#xff0c;无法和别人…

idea 自动导包,并且禁止自动导 *(java.io.*)

自动导包配置 进入 idea 设置&#xff0c;可以按下图所示寻找位置&#xff0c;也可以直接输入 auto import 快速定位到配置。 Add unambiguous imports on the fly&#xff1a;自动帮我们优化导入的包Optimize imports on the fly&#xff1a;自动去掉一些没有用到的包 禁止导…

【时时三省】(C语言基础)结构体的自引用

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 结构体的自引用 在结构中包含一个类型为该结构体本身的成员是否可以呢&#xff1f; 在struct B里面包含了一个结构体struct A叫sa 结构体类型里面是可以包含另一个结构体类型变量作为它的成…

GoReplay开源工具使用教程

目录 一、GoReplay环境搭建 1、Mac、Linux安装GoReplay环境 二、GoReplay录制与重播 1、搭建练习接口 2、录制命令 3、重播命令 三、GoReplay单个命令 1、常用命令 2、其他命令 3、命令示例 4、性能测试 5、正则表达式 四、gorepaly组合命令 1、组合命令实例 2、…

宏海科技募资额有所缩减,最大销售和采购都重度依赖美的集团

《港湾商业观察》施子夫 11月29日&#xff0c;北交所上市审核委员会将召开2024年第24次上市委审议会议&#xff0c;届时将审议武汉宏海科技股份有限公司&#xff08;以下简称&#xff0c;宏海科技&#xff09;的首发上会事项。 在上会之前&#xff0c;宏海科技共收到北交所下…

驱动篇的开端

准备 在做之后的动作前&#xff0c;因为win7及其以上的版本默认是不支持DbgPrint&#xff08;大家暂时理解为内核版的printf&#xff09;的打印&#xff0c;所以&#xff0c;为了方便我们的调试&#xff0c;我们先要修改一下注册表 创建一个reg文件然后运行 Windows Registr…

渗透测试--Windows凭证收集

在渗透测试过程中&#xff0c;我们终究会遇到攻陷了某台加域Windows主机的情况。而这种情况下&#xff0c;我们很需要搜集当前主机的凭证信息。为进一步利用这些相互信任的凭证来进行横向打下基础。 在凭证收集中&#xff0c;我们主要有以下场景&#xff1a; 1.lsass.exe的DMP文…

PH热榜 | 2024-12-03

1. Vela OS 标语&#xff1a;利用人工智能&#xff0c;创业投资精准度提升十倍。 介绍&#xff1a;Vela操作系统是一个原生AI操作系统&#xff0c;它能让创业投资者的预测准确率比传统风投公司高出十倍。借助一群AI智能体&#xff0c;投资者可以发现蓬勃发展的市场、公司和人…

微信小程序 运行出错 弹出提示框(获取token失败,请重试 或者 请求失败)

原因是&#xff1a;需要登陆微信公众平台在开发管理 中设置 相应的 服务器域名 中的 request合法域名 // index.jsPage({data: {products:[],cardLayout: grid, // 默认卡片布局为网格模式isGrid: true, // 默认为网格布局page: 0, // 当前页码size: 10, // 每页大小hasMore…

Google Cloud 混合云部署连接方式最佳实践案例讲解

混合云部署连接方式 GCP 的混合云部署连接方式提供了多种选择&#xff0c;企业可以根据自身需求选择合适的解决方案。实施最佳实践&#xff0c;将有助于提高混合云架构的性能、安全性和可用性。通过合理的规划和管理&#xff0c;企业可以充分利用混合云的优势&#xff0c;实现…

nfs服务器搭建

目录 1&#xff0c;nfs服务端 1&#xff09;安装 2&#xff09;修改nfs配置文件 3&#xff09;创建共享目录 4&#xff09;启动服务nfs配置文件 2&#xff0c;客户端 3&#xff0c;测试 1&#xff09;客户端 2&#xff09;服务端查看 4&#xff0c;systemd实现客户…

从0开始学PHP面向对象内容之常用设计模式(策略,观察者)

PHP设计模式——行为型模式 PHP 设计模式中的行为模式&#xff08;Behavioral Patterns&#xff09;主要关注对象之间的通信和交互。行为模式的目的是在不暴露对象之间的具体通信细节的情况下&#xff0c;定义对象的行为和职责。它们常用于解决对象如何协调工作的问题&#xff…

Python办公——openpyxl处理Excel每个sheet每行 修改为软雅黑9号剧中+边框线

目录 专栏导读背景1、库的介绍①&#xff1a;openpyxl 2、库的安装3、核心代码4、完整代码5、最快的方法(50万行44秒)——表头其余单元格都修改样式总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍…

【AI系统】TVM 实践案例

TVM 实践案例 在本文我们探讨一下&#xff0c;如何利用 AI 编译器在新的硬件上部署一个神经网络&#xff0c;从算法设计到实际运行&#xff0c;有哪些需要考虑的地方&#xff1f;本节将以 TVM 为例&#xff0c;首先介绍一下 TVM 的工作流&#xff1a; 导入模型。TVM 可以从 Te…

【AI系统】昇腾异构计算架构 CANN

昇腾异构计算架构 CANN 本文将介绍昇腾 AI 异构计算架构 CANN&#xff08;Compute Architecture for Neural Networks&#xff09;&#xff0c;这是一套为高性能神经网络计算需求专门设计和优化的架构。CANN 包括硬件层面的达芬奇架构和软件层面的全栈支持&#xff0c;旨在提供…

C++:map容器——自定义数据类型进行自定义排序规则

map容器和set容器自带排序操作&#xff0c;但是&#xff0c;对于自定义数据类型&#xff0c;二者必须指定排序规则。本文以map容器为例&#xff0c;针对自定义数据类型作为key值的指定排序进行程序实现。 首先&#xff0c;自定义数据类型&#xff1a;Person类&#xff0c;该类将…

Vue Web开发遇到问题汇总

1.Vue Web开发遇到问题汇总 1.1. vue项目main.js文件下import router from ‘./router‘默认导入router文件夹下index.js的原因 vue项目main.js文件下import router from ./router’默认导入router文件夹下index.js的原因 import router from ./router //等效于 //import rou…

lua download

https://www.lua.org/ https://www.lua.org/versions.html#5.4