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";/* 安装模块。 参数可以用一个字…...

《数据仓库与数据挖掘》 总复习
试卷组成 第一章图 第二章图 第三章图 第四章图 第五章图 第六章图 第九章图 第一章 DW与DM概述 (特点、特性) DB到DW 主要特征 (1)数据太多,信息贫乏(Data Rich, Information Poor)。 &a…...

EtherCAT主站IGH-- 8 -- IGH之domain.h/c文件解析
EtherCAT主站IGH-- 8 -- IGH之domain.h/c文件解析 0 预览一 该文件功能`domain.c` 文件功能函数预览二 函数功能介绍1. `ec_domain_init`2. `ec_domain_clear`3. `ec_domain_add_fmmu_config`4. `ec_domain_add_datagram_pair`5. `ec_domain_finish`6. `ecrt_domain_reg_pdo_en…...

《昇思25天学习打卡营第10天|使用静态图加速》
文章目录 今日所学:一、背景介绍1. 动态图模式2. 静态图模式 三、静态图模式的使用场景四、静态图模式开启方式1. 基于装饰器的开启方式2. 基于context的开启方式 总结: 今日所学: 在上一集中,我学习了保存与加载的方法ÿ…...

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(二十二)
课程地址: 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程,一套精通鸿蒙应用开发 (本篇笔记对应课程第 32 节) P32《31.通知-基础通知》 基础文本类型通知:briefText 没有用,写了也白写。 长文本类型…...

六西格玛绿带培训如何告别“走过场”?落地生根
近年来,六西格玛绿带培训已经成为了众多企业提升管理水平和员工技能的重要途径。然而,不少企业在实施六西格玛绿带培训时,往往陷入形式主义的泥潭,导致培训效果大打折扣。那么,如何避免六西格玛绿带培训变成“走过场”…...

Linux——提取包文件到指定目录,命令解释器-shell,type 命令
- 提取包文件到指定目录 bash tar xf/-xf/-xzf 文件名.tar.gz [-C 目标路径] tar xf/-xf/-xjf 文件名.tar.bz2 [-C 目标路径] tar xf/-xf/-xJf 文件名.tar.xz [-C 目标路径] ### 示例 - 将/etc下所有内容打包压缩到/root目录中 bash [rootserver ~]# tar -cvf taretc…...

【最详细】PhotoScan(MetaShape)全流程教程
愿天下心诚士子,人人会PhotoScan! 愿天下惊艳后辈,人人可剑开天门! 本教程由CSDN用户CV_X.Wang撰写,所用数据均来自山东科技大学视觉测量研究团队,特此鸣谢!盗版必究! 一、引子 Ph…...

Excel多表格合并
我这里一共有25张表格: 所有表的表头和格式都一样,但是内容不一样: 现在我要做的是把所有表格的内容合并到一起,研究了一下发现WPS的这项功能要开会员的,本来想用代码撸出来的,但是后来想想还是找其他办法,后来找到"易用宝"这个插件,这个插件可以从如下地址下载:ht…...

AI作画工具深度剖析:Midjourney vs. Stable Diffusion (SD)
在人工智能技术的推动下,艺术创作的边界被不断拓宽,AI作画工具成为数字艺术家与创意人士的新宠。其中,Midjourney与Stable Diffusion(SD)作为当前领域的佼佼者,以其独特的算法机制、丰富的功能特性及高质量…...

ASP.NET Core Blazor 5:Blazor表单和数据
本章将描述 Blazor 为处理 HTML 表单提供的特性,包括对数据验证的支持。 1 准备工作 继续使用上一章项目。 创建 Blazor/Forms 文件夹并添加一个名为 EmptyLayout.razor 的 Razor 组件。本章使用这个组件作为主要的布局。 inherits LayoutComponentBase<div …...