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


    @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.测试打印

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

路由器的ip地址与网关的区别是什么

在网络世界中&#xff0c;路由器扮演着至关重要的角色&#xff0c;它负责数据的传输和网络的互联。而在路由器的设置中&#xff0c;有两个常见的概念&#xff1a;IP地址和网关。那么&#xff0c;路由器的IP地址与网关的区别是什么&#xff1f;下面与虎观代理小二一起了解一下吧…

HQ-SAM

不建议复现

前后端分离:四种开发模式与实践指南

前后端分离&#xff1a;四种开发模式与实践指南 什么是前后端分离 当业务变得越来越复杂或产品线越来越多时&#xff0c;原有的开发模式就无法满足业务需求了。 产品越来越多&#xff0c;展现层的变化越来越快、越来越多&#xff0c;此时应该进行前后端分离的分层抽象&#…

MySQL数据恢复(适用于误删后马上发现)

首先解释一下标题&#xff0c;之所以适用于误删后马上发现是因为太久了之后时间和当时操作的数据表可能会记不清楚&#xff0c;不是因为日志丢失 1.首先确保自己的数据库开启了binlog&#xff08;我的是默认开启的我没有配置过&#xff09; 根据这篇博客查看自己的配置和自己…

线段树求区间最值问题

引言 今天主要还是练了两道题&#xff0c;是有关线段树如何去求一个区间内的最值问题的&#xff0c;我们可以用线段树来解决。 对应一个无法改变顺序的数组&#xff0c;我们想要去求一个区间内的最值&#xff0c;假设有n个结点&#xff0c;m次询问&#xff0c;暴力的解决办法…

Spring Bean生命周期

Bean生命周期&#xff1a; 创建 Bean 的实例&#xff1a;Bean 容器首先会找到配置文件中的 Bean 定义&#xff0c;然后使用 Java 反射 API 来创建 Bean 的实例。 Bean 属性赋值/填充&#xff1a;为 Bean 设置相关属性和依赖&#xff0c;例如Autowired 等注解注入的对象、Value…

怎样将word默认Microsoft Office,而不是WPS

设置——>应用——>默认应用——>选择"word"——>将doc和docx都选择Microsoft Word即可

Java-数据结构

数据结构概述 常见的数据结构 栈 队列 数组 链表 二叉树 二叉查找树 平衡二叉树 红黑树 示例&#xff1a;

电气-伺服(4)CANopen

一、CAN Controller Area Network ,控制器局域网&#xff0c;80年的德国Bosch的一家公司研发可以测量仪器直接的实时数据交换而开发的一款串行通信协议。 CAN发展历史 二、CAN 的osi 模型 CAN特性&#xff1a; CAN 的数据帧 三、CANopen 什么是CANopen CANopen 的网络模型 …

怎么用AI合成PPT?这5款风靡全球的AIPPT软件一定要知道!

当下我们已进入信息过载的时代&#xff0c;每天有无数的信息试图争夺我们的注意力&#xff0c;与此同时&#xff0c;我们也需要向别人展示和呈现信息&#xff0c;这就要求我们能够以最低的成本&#xff0c;在短时间内引起对方的注意&#xff0c;这其中最常用到的工具非PPT莫属。…

CVPR 2024最佳论文:“神兵”的组合器 Generative Image Dynamics

CVPR 2024的最佳论文来自谷歌、美国加州大学圣迭戈分校。两篇都来至于视频生成领域&#xff0c;可见今年外界对视频生成领域关注度很高。今天的这篇是“Generative Image Dynamics”&#xff0c;Google Research发布的。它的研究成果令人震惊&#xff0c;从单张RGB图像生成连续…

c语言回顾-内存操作函数

目录 前言 1.memcpy 函数 1.1函数介绍 1.2与strcpy的区别 1.3memcpy的模拟 2.memmove 函数 2.1函数介绍和使用 2.2函数的模拟 3.memset函数 3.1函数介绍 3.2函数的模拟 4.memcmp函数 4.1函数的使用 4.2函数的模拟 结束语 前言 在动态内存的章节中小编详细讲解了动…

【代码随想录】【算法训练营】【第51天】 [115]不同的子序列 [583]两个字符串的删除操作 [72]编辑距离

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 51&#xff0c;周四&#xff0c;又是不能坚持的一天~ 题目详情 [115] 不同的子序列 题目描述 115 不同的子序列 解题思路 前提&#xff1a;转换为t为s的子序列的个数&#xff0c;元素的相对…

flask项目部署总结

这个部署的时候要用虚拟环境&#xff0c;cd进项目文件夹 python3 -m venv myenv source myenv/bin/activate激活 之后就安装一些库包之类的&#xff0c;&#xff08;flask&#xff0c;requests,bs4,等等&#xff09; 最重要的是要写.flaskenv文件并且pip install 一个能运行…

【MySQL】InnoDB的存储结构

InnoDB的存储结构&#xff1a;每个表都会生成一个表空间文件&#xff0c;这个文件里面最小结构就是行&#xff0c;存储的真正的数据&#xff0c;一个页来管理若干行&#xff0c;一个区来管理若干页&#xff0c;一个区组来管理若干区。段并不是真正的物理存储结构&#xff0c;它…

计组期末复习

本内容是我在计组期末复习时的记录&#xff0c;可能对你的复习帮助不大。下面是我复习时看的一些资料和视频&#xff1a; 知识体系&#xff1a; 【【计算机组成原理】计算机组成原理期末考试速成课&#xff0c;不挂科&#xff01;&#xff01;】https://www.bilibili.com/video…

轻松跨越国界:使用WildCard畅享全球AI服务

大家好&#xff0c;现在AI技术已经深入到我们的日常生活中。然而&#xff0c;许多朋友仍然难以获取优质的AI工具和应用。那么&#xff0c;如何才能使用像ChatGPT这样的AI服务呢&#xff1f; 今天我为大家介绍一个“一劳永逸”的解决方案&#xff0c;它就是我们的主角——WildC…

spdlog一个非常好用的C++日志库(四): 源码分析之logger类

目录 1.简介 2.类图关系 3.logger数据成员 4.logger函数成员 4.1.构造与析构 4.1.1.构造函数 4.1.2.拷贝构造、移动构造 4.2.交换操作 4.3.log()记录日志消息 4.3.1.格式串 4.3.2.普通字符串 4.3.3.日志级别 4.3.4.宽字符支持 4.4.sink_it_&#xff1a;将log消息…

【内网渗透】从0到1的内网渗透基础概念笔记

目录 域 域的介绍 单域 父域和子域 域树 域森林 域名服务器 活动目录 活动目录介绍 域内权限 组 域本地组 全局组 通用组 总结 示例 A-G-DL-P策略 重要的域本地组 重要的全局组、通用组 安全域划分 域 域的介绍 Windows域是计算机网络的一种形式&#xf…

币界网讯,预计以太坊现货 ETF 将于 7 月中旬推出

刚刚 ETF Store 总裁 Nate Geraci 在 X &#xff08;前Twitter&#xff09;平台上宣布&#xff0c;备受数字货币市场期待的SEC以太坊现货 ETF提案&#xff0c;将于7 月中旬通过美国证券交易委员会&#xff08;SEC&#xff09;批准。Nate Geraci透露修订后的 S-1 文件将于 7 月 …