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

news2024/10/6 6:19:45

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");


    @Autowired
    private 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;
    }


    @Override
    protected 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 json
            if ((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);
    }

    @Override
    public 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;

    @NestedConfigurationProperty
    private List<InterfaceLogConfig> interfaceLogConfigs;

    @Data
    public 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;

    @Override
    public void initialize(InStrings constraintAnnotation) {
        mustIn = constraintAnnotation.in();
    }

    @Override
    public 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: info
      print: req

在这里插入图片描述

9.测试打印

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

六大Pixel新AI功能提升使用体验

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

探索如何赋予对象迭代魔法,轻松实现非传统解构赋值的艺术

前言 今天下午在网上冲浪过程中看到这样一个问题 面试题&#xff1a;如何让 var [a, b] {a: 1, b: 2} 解构赋值成功&#xff1f; 据说是某大厂面试题&#xff0c;于是我学习了一下这个问题&#xff0c;写下这篇文章记录一下。 学习过程 要想解决这个问题首先要知道什么是解…

nginx安装升级修复HTTP头信息泄露Nginx版本信息漏洞(并保持https配置)

文章目录 1. 准备工作2. 修改web服务器所使用的nginx的名称和版本信息2.1 修改以下三个文件&#xff1a;(1) src/core目录下的nginx.h文件(2) src/http目录下的ngx_http_header_filter_module.c文件(3) src/http目录下的ngx_http_special_response.c文件 2.2 重新编译nginx2.3 …

Spring框架的学习前言

1.注意事项 1.在接下来的学习中我们会将jdk的版本升级到17。 2.引入maven仓库用来存储依赖 3.在后面的javaSpring框架中要第一个项目的创建要选javaweb和lombook这两个依赖 2.Maven的主要功能 &#xff08;1&#xff09;maven的主要功能是引入依赖和管理依赖&#xff0c;在…

第7章:Electron文件系统操作(2)

7.2 文件对话框 Electron 提供了 dialog 模块用于显示文件打开和保存对话框。 7.2.1 显示文件打开对话框 主进程代码&#xff1a; const { app, BrowserWindow, ipcMain, dialog } require(electron); const path require(path);let mainWindow;const createMainWindow …

【Ubuntu】详细说说Parallels DeskTop安装和使用Ubuntu系统

希望文章能给到你启发和灵感~ 如果觉得文章对你有帮助的话,点赞 + 关注+ 收藏 支持一下博主吧~ 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境二、Ubuntu系统的使用2.1 系统的下载2.2 系统的安装2.3 安装桌面版(可选)2.3.1 安装/更新apt2.3.2 安装桌面版2.3…

4.通过制作trackbar控件了解3原色在opencv的应用-cnblog

通过制作trackbar控件了解3原色在opencv的应用 什么是3原色 一张彩色图片通常是由三种基本颜色&#xff0c;即红色、绿色和蓝色&#xff08;通常称为RGB&#xff09;的混合组成的。这三种颜色以不同的比例混合可以产生几乎所有其他颜色。在数字图像中&#xff0c;每个像素通常…

找不到msvcr110.dll是怎么回事?彻底解决msvcr110.dll丢失的方法

当您的电脑提示遇到msvcr110.dll丢失时&#xff0c;您知道如何解决此问题吗&#xff1f;事实上&#xff0c;解决此类dll文件丢失的问题相对较为简单。只要我们深入了解msvcr110.dll丢失的具体情况&#xff0c;便可轻松解决此问题。以下为您介绍msvcr110.dll修复方法。 一&#…

算法库应用-顺序串(串比较)

学习贺利坚老师博客 数据结构例程——串的顺序存储应用_使用顺序串存储身份证号-CSDN博客 本人详细解析博客 串的顺序存储结构应用_(1)假设串采用顺序串存储,设计一个算法程序,按顺序比较两个串s和t的大小。请-CSDN博客 版本日志 V1.0: 利用顺序串, 进行简单的判断比较, 也算是…

某DingTalk企典 - Token

⚠️前言⚠️ 本文仅用于学术交流。 学习探讨逆向知识&#xff0c;欢迎私信共享学习心得。 如有侵权&#xff0c;联系博主删除。 请勿商用&#xff0c;否则后果自负。 网址 aHR0cHM6Ly9kaW5ndGFsay5jb20vcWlkaWFuLw 浅聊一下 没毛病&#xff0c;就这字段&#xff0c;有效期…

【前端CSS3】CSS显示模式(黑马程序员)

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、CSS元素显示模式&#xff1a;☀️☀️☀️2.1 什么是元素显示模式2.2 块元素2.3 行内元素2.4 行块元素2.5 元素显示模式的转换 三、总结&#x1f680;&#x1f680;&#x1f680; 一、前言&#x1f680;&#x1f…

小程序 npm 支持

使用 npm 包 目前小程序已经支持使用 npm 安装第三方包&#xff0c;因为 node_modules 目录中的包不会参与小程序项目的编译、 上传和打包&#xff0c;因此在小程序 项目中要使用的 npm 包&#xff0c;必须走一遍构建 npm 的过程。在构建成功以后&#xff0c;默认 会在小程序目…

015-GeoGebra基础篇-定点旋转物体、动态显示数值并显示运动轨迹

这可能是我能想到的最大概率可以被你搜索到的标题了&#xff0c;容我先喘口气~ 目录 一、成品展示二、涉及内容三、做图步骤&#xff08;1&#xff09;绘制三角形t&#xff08;2&#xff09;建立定点D&#xff08;3&#xff09;制作角度滑动条&#xff08;4&#xff09;图形绕点…

如何利用AI撰写短文案获客?分享6大平台和3大步骤!

从去年开始&#xff0c;很多大厂都在裁员&#xff0c;原因就是因为AI的火爆&#xff0c;替代了很多机械式的劳动力。以前很多人可以通过机械式的工作来摸鱼&#xff0c;现在AI完成的效率比人工的要高很多倍。 国内好用的AI平台非常多&#xff0c;有时候也可以使用几个AI平台结合…

线程(基础概念)

文章目录 一、线程和进程&#xff1f;二、线程初识2.1 线程属性2.2 线程的调度策略2.3 线程的优先级2.3 线程实验 一、线程和进程&#xff1f; 我们经常描述进程&#xff08;process&#xff09;和线程&#xff08;thread&#xff09;&#xff1a; 进程是资源管理的最小单位&a…

每天五分钟深度学习框架pytorch:tensor向量的统计函数的运算

本文重点 给定一个向量,我们如何才能获取到这个向量中重要的那部分呢?比如均值,最大值等等,我们可以使用pytorch中已经封装好的方法来完成这些任务。 常用的统计方法 L1范式 L1范式就是将向量中所有元素的绝对值相加求和,以上是对a、b、c三个向量求L1范式,都是8 L2范数…

GEE计算遥感生态指数RSEI

目录 RESI湿度绿度热度干度源代码归一化函数代码解释整体的代码功能解释:导出RSEI计算结果参考文献RESI RSEI = f (Greenness,Wetness,Heat,Dryness)其遥感定义为: RSEI = f (VI,Wet,LST,SI)式中:Greenness 为绿度;Wetness 为湿度;Thermal为热度;Dryness 为干度;VI 为植被指数…

QT5.12环境搭建与源码编译

一、概述 QT版本&#xff1a;QT5.12.10 Qt网址&#xff1a;http://download.qt.io/archive/qt/ 编译平台 ubuntu18.04 二、安装交叉编译工具链 1、获取交叉编译工具链 一般如果是编译系统如果有对应的gcc 就是用这个就可以了 比如rk3128 lin…

校园失物招领系统带万字文档java项目失物招领管理系统java课程设计java毕业设计springboot vue

文章目录 校园失物招领系统一、项目演示二、项目介绍三、万字字项目文档四、部分功能截图五、部分代码展示六、底部获取项目源码带万字文档&#xff08;9.9&#xffe5;带走&#xff09; 校园失物招领系统 一、项目演示 校园失物招领系统 二、项目介绍 语言: Java 数据库&…