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命令的详细用法,包括它的所有常用选项和一些示例。 基本语法 tar [选项] [归档文件] [文件或目录]常用选项 基本操作 -c:创建一个新的归档文件(create)…...
MySQL 日期和时间函数知识点总结
引言 在数据库管理和开发中,日期查询是一项基础且频繁使用的功能。MySQL提供了丰富的日期和时间处理函数,使得我们能够灵活地进行日期查询和数据处理。本文将详细介绍MySQL中关于日期查询的几个重要知识点,并附上具体的案例。 1. MySQL的日…...
鸿蒙登录页面及页面跳转的设计
目录 任务目标任务分析任务实施1.新建工程项目HMLogin2.设计登录页面Index.visual3.设计第二个页面SecondPage4.修改Index.ets代码5.修改SecondPage.ets代码6.运行工程 任务目标 设计一个简单的登录页面,要求可以将第一页的登录信息,传递到第二个页面&a…...
【居家养老实训室】:看中医保健在养老中的应用
本文以居家养老实训室为视角,深入探讨了中医保健在养老中的应用。通过对中医保健理念、常用方法以及在居家养老中的具体实践进行分析,阐述了其在改善老年人健康状况、提高生活质量方面的重要作用。同时,也指出了目前应用中存在的问题…...
【区块链+基础设施】区块链服务网络 BSN | FISCO BCOS应用案例
BSN(Blockchain-based Service Network,区块链服务网络)是一个跨云服务、跨门户、跨底层框架,用于部 署和运行各类区块链应用的全球性基础设施网络,旨在为开发者提供低成本和技术互通的区块链一站式服务。 2019 年 12…...
六、快速启动框架:SpringBoot3实战-个人版
六、快速启动框架:SpringBoot3实战 文章目录 六、快速启动框架: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,取得下行时钟同步,并获取小区的PCI;如果失败则转步骤1搜索下一个频点;否则继续后续步骤; 3.解析Mib,…...
图像的灰度直方图
先来认识一下灰度直方图,灰度直方图是图像灰度级的函数,用来描述每个灰度级在图像矩阵中的像素个数或者占有率。接下来使用程序实现直方图: 首先导入所需的程序包: In [ ]: import cv2 import numpy as np import matplotlib…...
软件测试面试题:Redis的五种数据结构,以及使用的场景是什么?
字符串(Strings):简单直接,就像记事本一样,用来存储和快速访问简单的数据,比如缓存网页或者保存用户会话信息。 列表(Lists):有序的数据集合,适合用来存储按…...
Java后端每日面试题(day1)
目录 JavaWeb三大组件依赖注入的方式Autowire和Resurce有什么区别?Spring Boot的优点Spring IoC是什么?说说Spring Aop的优点Component和Bean的区别自定义注解时使用的RetentionPolicy枚举类有哪些值?如何理解Spring的SPI机制?Spr…...
AI与测试相辅相成
AI助力软件测试 1.AI赋能软件测试 使用AI工具来帮助测试人员提高测试效率,提供缺陷分析和缺陷预测。 语法格式 设定角色 具体指示 上下文格式 例: 角色:你是一个测试人员 内容:请帮我生成登录案例的测试用例 1.只有输入正确账号和密码才…...
搜索+动态规划
刷题刷题刷题刷题 Forgery - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路: 需要两个数组,一个数组全部初始化为".",另一个数组输入数据,每碰到一个“.”就进行染色操作,将其周围的…...
strcpy,srtcmp,strlen函数漏洞利用
strcpy,srtcmp,strlen函数漏洞利用 strcpy strcpy函数用于将字符串复制到另一个指针指向的空间中,遇到空字符 **b’x\00’**时停止,: 所以可以利用 strcpy不检查缓冲区 的漏洞(构造的字符串要以\0结尾),…...
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是一款用于管理和查看磁盘空间的工具,它集成了于微软的Windows操作系统资源管理器中,以显示直观的磁盘空间使用情况。该软件通过生成图形化地图,帮助用户组织和管理大量文件和文件夹,从而高效地管理磁盘空间。用…...
-bash: /snap/bin/docker: 没有那个文件或目录
-bash: /snap/bin/docker: 没有那个文件或目录 解决办法 export PATH$PATH:/usr/bin/docker然后,重新加载配置文件 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,并执行,安装playwright。 //安装模块 import process.python.pip; //process.python.path "python.exe";/* 安装模块。 参数可以用一个字…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
