当前位置: 首页 > news >正文

java项目自定义打印日志,打印请求方式,参数用时等

1.相关依赖

<!-- 私人工具包 --><dependency><groupId>cn.changeforyou</groupId><artifactId>location</artifactId><version>1.13-SNAPSHOT</version></dependency><!-- hutool工具依赖 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-crypto</artifactId><version>5.5.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

2.添加日志过滤类LogFilter.java

package com.abliner.test.common.log;import cn.changeforyou.web.utils.http.ServletUtils;
import cn.changeforyou.web.utils.http.warpper.BufferedHttpResponseWrapper;
import cn.hutool.json.JSONUtil;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import com.abliner.test.common.log.LogRecordConfig.InterfaceLogConfig;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.*;
import java.util.concurrent.ConcurrentHashMap;@Component
public class LogFilter extends OncePerRequestFilter {Logger log = LoggerFactory.getLogger("reqResp");@Autowiredprivate LogRecordConfig logRecordConfig;private final Set<String> urls;private final AntPathMatcher antPathMatcher;private final Map<String, InterfaceLogConfig> url2Config = new ConcurrentHashMap<>();public LogRecordConfig getLogRecordConfig() {return logRecordConfig;}public LogRecordConfig addInterfaceLogConfig(InterfaceLogConfig config) {logRecordConfig.getInterfaceLogConfigs().add(config);initMatcher();return logRecordConfig;}public LogRecordConfig removeInterfaceLogConfig(String url) {if (url2Config.containsKey(url)) {InterfaceLogConfig config = url2Config.remove(url);logRecordConfig.getInterfaceLogConfigs().remove(config);initMatcher();}return logRecordConfig;}public LogRecordConfig updateDefaultInterfaceLogLevel(InterfaceLogConfig config) {logRecordConfig.setDefaultInterfaceLogConfig(config);return logRecordConfig;}public LogFilter() {urls = Collections.synchronizedSet(new HashSet<>());antPathMatcher = new AntPathMatcher();}private InterfaceLogConfig matches(String url) {if (urls.isEmpty()) {return null;}for (String s : urls) {if (antPathMatcher.match(s, url)) {return url2Config.get(s);}}return null;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {long requestTime = System.currentTimeMillis();String uri = request.getRequestURI();String contextPath = request.getContextPath();String url = uri.substring(contextPath.length());InterfaceLogConfig thisConfig = matches(url);if (null == thisConfig) {thisConfig = logRecordConfig.getDefaultInterfaceLogConfig();}if (!thisConfig.printLog()) {filterChain.doFilter(request, response);return;}String requestBody = "";String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);if (requestContentType != null) {// xml jsonif ((requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE) || requestContentType.startsWith(MediaType.APPLICATION_XML_VALUE)) && request.getMethod().equalsIgnoreCase("POST")) {StringBuilder sb = new StringBuilder();request = ServletUtils.getRequestBody(request, sb);requestBody = sb.toString();// 普通表单提交} else if (requestContentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {requestBody = toJson(request.getParameterMap());// 文件表单提交} else if (requestContentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {requestBody = getFormParam(request);} else {requestBody = toJson(request.getParameterMap());}} else if (request.getMethod().equals(HttpMethod.GET.name())) {requestBody = toJson(request.getParameterMap());}BufferedHttpResponseWrapper responseWrapper = new BufferedHttpResponseWrapper(response);if (thisConfig.printReq()) {if (thisConfig.isDebugEnabled()) {log.debug("URL: {}, requestBody: {}", url, requestBody);} else {log.info("URL: {}, requestBody: {}", url, requestBody);}}filterChain.doFilter(request, responseWrapper);long costTime = System.currentTimeMillis() - requestTime;String responseBody = "";// 暂定只有json 输出响应体String contentType = responseWrapper.getContentType();if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) {responseBody = new String(responseWrapper.getBuffer(), StandardCharsets.UTF_8);}StringBuilder sb = new StringBuilder();sb.append("URL:").append(url).append(", total time:").append(costTime).append(" ms, ");if (thisConfig.printRes()) {sb.append(", responseBody:").append(responseBody);}if (responseWrapper.getStatus() >= 200 && responseWrapper.getStatus() < 1000) {if (thisConfig.isDebugEnabled()) {log.debug(sb.toString());} else {log.info(sb.toString());}} else {log.error(sb.toString());}response.getOutputStream().write(responseWrapper.getBuffer());}private String getFormParam(HttpServletRequest request) {MultipartResolver resolver = new StandardServletMultipartResolver();MultipartHttpServletRequest mRequest = resolver.resolveMultipart(request);Map<String, Object> param = new HashMap<>();Map<String, String[]> parameterMap = mRequest.getParameterMap();if (!parameterMap.isEmpty()) {param.putAll(parameterMap);}Map<String, MultipartFile> fileMap = mRequest.getFileMap();if (!fileMap.isEmpty()) {for (Map.Entry<String, MultipartFile> fileEntry : fileMap.entrySet()) {MultipartFile file = fileEntry.getValue();param.put(fileEntry.getKey(), file.getOriginalFilename() + "(" + file.getSize() + " byte)");}}return toJson(param);}@Overridepublic void afterPropertiesSet() throws ServletException {super.afterPropertiesSet();initMatcher();}private void initMatcher() {List<InterfaceLogConfig> configs = logRecordConfig.getInterfaceLogConfigs();this.urls.clear();if (CollectionUtils.isNotEmpty(configs)) {for (InterfaceLogConfig config : configs) {this.urls.add(config.getUrl());url2Config.put(config.getUrl(), config);}}}private static String toJson(Object object) {return JSONUtil.toJsonStr(object);}}

3.添加日志配置类LogRecordConfig.java

package com.abliner.test.common.log;import com.abliner.test.common.validator.InStrings;
import com.abliner.test.common.validator.ValidatorConstant;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;import javax.validation.constraints.NotEmpty;
import java.util.List;@ConfigurationProperties(prefix = "log.record")
@Data
@Component
public class LogRecordConfig {private InterfaceLogConfig defaultInterfaceLogConfig;@NestedConfigurationPropertyprivate List<InterfaceLogConfig> interfaceLogConfigs;@Datapublic static class InterfaceLogConfig {@NotEmpty(groups = ValidatorConstant.InsertAndUpdate.class)@NotEmpty(groups = ValidatorConstant.Delete.class)private String url;/**** 1: info* 2: debug*/@NotEmpty(groups = ValidatorConstant.InsertAndUpdate.class)@InStrings(in= {"info", "debug"}, groups = ValidatorConstant.InsertAndUpdate.class)@InStrings(in= {"info", "debug"}, groups = ValidatorConstant.UpdateDefault.class)private String logLevel;/**** res* req* all* none*/@NotEmpty(groups = ValidatorConstant.InsertAndUpdate.class)@InStrings(in= {"res", "req", "all", "none"},groups = ValidatorConstant.InsertAndUpdate.class)@InStrings(in= {"res", "req", "all", "none"},groups = ValidatorConstant.UpdateDefault.class)private String print;public boolean isDebugEnabled() {return "debug".equalsIgnoreCase(logLevel);}public boolean printLog() {return !"none".equalsIgnoreCase(print);}public boolean printRes() {return "res".equalsIgnoreCase(print) || "all".equalsIgnoreCase(print);}public boolean printReq() {return "req".equalsIgnoreCase(print) || "all".equalsIgnoreCase(print);}}}

4.添加使用到的注解类InStrings.java

package com.abliner.test.common.validator;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Documented
@Constraint(validatedBy = {InStringsValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(InStrings.List.class)
public @interface InStrings {String message() default "字符串不在设定范围内";String[] in();Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};/*** Defines several {@code @NotEmpty} constraints on the same element.** @see InStrings*/@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Documented@interface List {InStrings[] value();}
}

5.添加InStringsValidator.java

package com.abliner.test.common.validator;import cn.changeforyou.utils.string.StringUtils;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;public class InStringsValidator implements ConstraintValidator<InStrings, String> {private String[] mustIn;@Overridepublic void initialize(InStrings constraintAnnotation) {mustIn = constraintAnnotation.in();}@Overridepublic boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {if (StringUtils.isEmpty(s)) {return false;}return StringUtils.in(s, mustIn);}
}

6.添加常量ValidatorConstant.java

package com.abliner.test.common.validator;public interface ValidatorConstant {interface Insert {}interface Update {}interface Delete {}interface Select {}interface InsertAndUpdate {}interface SelectAndDelete {}interface UpdateDefault {}
}

7.项目结构图

在这里插入图片描述

8.在主配置文件application.yml添加代码

log:record:defaultInterfaceLogConfig:logLevel: infoprint: req

在这里插入图片描述

9.测试打印

在这里插入图片描述

相关文章:

java项目自定义打印日志,打印请求方式,参数用时等

1.相关依赖 <!-- 私人工具包 --><dependency><groupId>cn.changeforyou</groupId><artifactId>location</artifactId><version>1.13-SNAPSHOT</version></dependency><!-- hutool工具依赖 --><dependency>…...

03:EDA的进阶使用

使用EDA设计一个38译码器电路和245放大电路 1、38译码器1.1、查看74HC138芯片数据1.2、电路设计 2、245放大电路2.1、查看数据手册2.2、设计电路 3、绘制PCB3.1、导入3.2、放置3.3、飞线3.4、特殊方式连接GND3.5、泪滴3.6、配置丝印和划分区域3.7、添加typc接口供电 1、38译码器…...

Linux/Unix系统指令:(tar压缩和解压)

tar 是一个在Linux和Unix系统中用于创建和处理归档文件的命令。 下面是tar命令的详细用法&#xff0c;包括它的所有常用选项和一些示例。 基本语法 tar [选项] [归档文件] [文件或目录]常用选项 基本操作 -c&#xff1a;创建一个新的归档文件&#xff08;create&#xff09…...

MySQL 日期和时间函数知识点总结

引言 在数据库管理和开发中&#xff0c;日期查询是一项基础且频繁使用的功能。MySQL提供了丰富的日期和时间处理函数&#xff0c;使得我们能够灵活地进行日期查询和数据处理。本文将详细介绍MySQL中关于日期查询的几个重要知识点&#xff0c;并附上具体的案例。 1. MySQL的日…...

鸿蒙登录页面及页面跳转的设计

目录 任务目标任务分析任务实施1.新建工程项目HMLogin2.设计登录页面Index.visual3.设计第二个页面SecondPage4.修改Index.ets代码5.修改SecondPage.ets代码6.运行工程 任务目标 设计一个简单的登录页面&#xff0c;要求可以将第一页的登录信息&#xff0c;传递到第二个页面&a…...

【居家养老实训室】:看中医保健在养老中的应用

本文以居家养老实训室为视角&#xff0c;深入探讨了中医保健在养老中的应用。通过对中医保健理念、常用方法以及在居家养老中的具体实践进行分析&#xff0c;阐述了其在改善老年人健康状况、提高生活质量方面的重要作用。同时&#xff0c;也指出了目前应用中存在的问题&#xf…...

【区块链+基础设施】区块链服务网络 BSN | FISCO BCOS应用案例

BSN&#xff08;Blockchain-based Service Network&#xff0c;区块链服务网络&#xff09;是一个跨云服务、跨门户、跨底层框架&#xff0c;用于部 署和运行各类区块链应用的全球性基础设施网络&#xff0c;旨在为开发者提供低成本和技术互通的区块链一站式服务。 2019 年 12…...

六、快速启动框架:SpringBoot3实战-个人版

六、快速启动框架&#xff1a;SpringBoot3实战 文章目录 六、快速启动框架&#xff1a;SpringBoot3实战一、SpringBoot3介绍1.1 SpringBoot3简介1.2 系统要求1.3 快速入门1.4 入门总结回顾复习 二、SpringBoot3配置文件2.1 统一配置管理概述2.2 属性配置文件使用2.3 YAML配置文…...

SA 注册流程

目录 1. UE开机后按照3GPP TS 38.104定义的Synchronization Raster搜索特定频点 2.UE尝试检测PSS/SSS&#xff0c;取得下行时钟同步&#xff0c;并获取小区的PCI&#xff1b;如果失败则转步骤1搜索下一个频点&#xff1b;否则继续后续步骤&#xff1b; 3.解析Mib&#xff0c;…...

图像的灰度直方图

先来认识一下灰度直方图&#xff0c;灰度直方图是图像灰度级的函数&#xff0c;用来描述每个灰度级在图像矩阵中的像素个数或者占有率。接下来使用程序实现直方图&#xff1a; 首先导入所需的程序包&#xff1a; In [ ]: import cv2 import numpy as np import matplotlib…...

软件测试面试题:Redis的五种数据结构,以及使用的场景是什么?

字符串&#xff08;Strings&#xff09;&#xff1a;简单直接&#xff0c;就像记事本一样&#xff0c;用来存储和快速访问简单的数据&#xff0c;比如缓存网页或者保存用户会话信息。 列表&#xff08;Lists&#xff09;&#xff1a;有序的数据集合&#xff0c;适合用来存储按…...

Java后端每日面试题(day1)

目录 JavaWeb三大组件依赖注入的方式Autowire和Resurce有什么区别&#xff1f;Spring Boot的优点Spring IoC是什么&#xff1f;说说Spring Aop的优点Component和Bean的区别自定义注解时使用的RetentionPolicy枚举类有哪些值&#xff1f;如何理解Spring的SPI机制&#xff1f;Spr…...

AI与测试相辅相成

AI助力软件测试 1.AI赋能软件测试 使用AI工具来帮助测试人员提高测试效率&#xff0c;提供缺陷分析和缺陷预测。 语法格式 设定角色 具体指示 上下文格式 例: 角色&#xff1a;你是一个测试人员 内容&#xff1a;请帮我生成登录案例的测试用例 ​ 1.只有输入正确账号和密码才…...

搜索+动态规划

刷题刷题刷题刷题 ​​​​​​​​​​​​​​Forgery - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路&#xff1a; 需要两个数组&#xff0c;一个数组全部初始化为".",另一个数组输入数据&#xff0c;每碰到一个“.”就进行染色操作&#xff0c;将其周围的…...

strcpy,srtcmp,strlen函数漏洞利用

strcpy,srtcmp,strlen函数漏洞利用 strcpy strcpy函数用于将字符串复制到另一个指针指向的空间中&#xff0c;遇到空字符 **b’x\00’**时停止&#xff0c;&#xff1a; 所以可以利用 strcpy不检查缓冲区 的漏洞&#xff08;构造的字符串要以\0结尾&#xff09;&#xff0c;…...

SketchUp + Enscape+ HTC Focus3 VR

1. 硬件: 设备连接 2. 软件: 安装steam steamVR Vive Business streaming 3. 操作: 双方登录steam 账号,然后带上头盔,用手柄在HTC Focus3 安装 串流软件,选择串流软件,在Enscape中选择 VR 模式即可 4.最终效果: SketchUp Enscape HTC Focus 3 VR 实时预览_哔哩哔哩_bi…...

推荐3款Windows系统的神级软件,免费、轻量、绝对好用!

DiskView DiskView是一款用于管理和查看磁盘空间的工具&#xff0c;它集成了于微软的Windows操作系统资源管理器中&#xff0c;以显示直观的磁盘空间使用情况。该软件通过生成图形化地图&#xff0c;帮助用户组织和管理大量文件和文件夹&#xff0c;从而高效地管理磁盘空间。用…...

-bash: /snap/bin/docker: 没有那个文件或目录

-bash: /snap/bin/docker: 没有那个文件或目录 解决办法 export PATH$PATH:/usr/bin/docker然后&#xff0c;重新加载配置文件 source ~/.bashrc...

[深度学习]卷积理解

单通道卷积 看这个的可视化就很好理解了 https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md 多通道卷积 当输入有多个通道时,卷积核需要拥有相同的通道数. 假设输入有c个通道,那么卷积核的每个通道分别于相应的输入数据通道进行卷积,然后将得到的特征图对…...

基于aardio web.view2库和python playwright包的内嵌浏览器自动化操作

通过cdp协议可以实现playwright操控webview。 新建Python窗口工程 修改pip.aardio 修改pip.aardio&#xff0c;并执行&#xff0c;安装playwright。 //安装模块 import process.python.pip; //process.python.path "python.exe";/* 安装模块。 参数可以用一个字…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...

【WebSocket】SpringBoot项目中使用WebSocket

1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖&#xff0c;添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...