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";/* 安装模块。 参数可以用一个字…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
