springboot如何优雅的打印项目日志

news2024/11/16 11:53:13

文章目录

  • 如何优雅的打印项目日志
    • 原理
    • 实现
      • 日志打印Filter
      • 注入容器

如何优雅的打印项目日志

框架
springboot

原理

使用filter拦截请求,打印出请求、响应,及耗时
在这里插入图片描述

知识点
1、OncePerRequestFilter

Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.
过滤器基类,旨在保证在任何 servlet 容器上每个请求调度一次执行。

兼容多种servlet版本,保证在任何 servlet 容器上每个请求调度一次执行。

2、ContentCachingRequestWrapper,ContentCachingResponseWrapper
HttpServletRequest 包装器,用于缓存从输入流和读取器读取的所有内容,并允许通过字节数组检索此内容。
源代码如下,第一次获取输入流时,复制了一份
在这里插入图片描述

因为request的数据流只能读取一次,通过过滤器读取一次后,后面的业务处理会读不到数据
通过ContentCachingRequestWrapper将请求包装,可以进行多次读取

实现

继承OncePerRequestFilter,重写doFilterInternal方法

日志打印Filter

import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

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.Enumeration;

@Slf4j
public class LogFilter extends OncePerRequestFilter {

    private Integer printMaxSize;

    public LogFilter() {
    }

    public LogFilter(Integer printMaxSize) {
        if (printMaxSize == null) {
            printMaxSize = 1024;
        }
        this.printMaxSize = printMaxSize;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        if (isAsyncDispatch(httpServletRequest)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } else {
            doLogFilter(getRequestWrapper(httpServletRequest), getResponseWrapper(httpServletResponse), filterChain);
        }
    }

    private void doLogFilter(ContentCachingRequestWrapper requestWrapper, ContentCachingResponseWrapper responseWrapper, FilterChain filterChain) throws IOException, ServletException {
        StringBuilder sb = new StringBuilder();
        sb.append(System.lineSeparator())
                .append("Method: [").append(requestWrapper.getMethod()).append("] ")
                .append("URI: ").append(requestWrapper.getRequestURI())
                .append(System.lineSeparator());
        long start = System.currentTimeMillis();

        filterChain.doFilter(requestWrapper, responseWrapper);

        try {
            sb.append(getParams(requestWrapper)).append(System.lineSeparator());
            sb.append(getBody(requestWrapper));
            sb.append(getResponse(responseWrapper)).append(System.lineSeparator());
        } catch (Exception e) {
            log.warn("日志打印失败 e:{}", Throwables.getStackTraceAsString(e));
        } finally {
            responseWrapper.copyBodyToResponse();
            long end = System.currentTimeMillis();
            sb.append("use time :").append(end - start).append("ms");
            log.info(sb.toString());
        }
    }

    private String getBody(ContentCachingRequestWrapper requestWrapper) {
        StringBuilder sb = new StringBuilder();
        String contentType = requestWrapper.getContentType();
        if (requestWrapper.getMethod().equalsIgnoreCase("POST")
                && (MediaType.APPLICATION_JSON_VALUE.equals(contentType)
                || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType))) {
            sb.append("body: ").append(System.lineSeparator());
            sb.append(new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8));
            sb.append(System.lineSeparator());
        }
        return sb.toString();
    }

    private String getResponse(ContentCachingResponseWrapper responseWrapper) {
        String responseStr = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
        responseStr = responseStr.length() > printMaxSize ? responseStr.substring(0, printMaxSize) : responseStr;
        return "response: " + responseStr;
    }

    private String getParams(ContentCachingRequestWrapper requestWrapper) {
        Enumeration<String> enumeration = requestWrapper.getParameterNames();
        StringBuilder sb = new StringBuilder();
        sb.append("params: ");
        while (enumeration.hasMoreElements()) {
            String paramName = enumeration.nextElement();
            sb.append(paramName);
            sb.append(" : ");
            sb.append(requestWrapper.getParameter(paramName));
            sb.append(", ");
        }
        if (sb.length() > 2) {
            sb.replace(sb.length() - 2, sb.length(), "");
        }
        return sb.toString();
    }

    private ContentCachingResponseWrapper getResponseWrapper(HttpServletResponse httpServletResponse) {
        if (httpServletResponse instanceof ContentCachingResponseWrapper) {
            return (ContentCachingResponseWrapper) httpServletResponse;
        } else {
            return new ContentCachingResponseWrapper(httpServletResponse);
        }
    }

    private ContentCachingRequestWrapper getRequestWrapper(HttpServletRequest httpServletRequest) {
        if (httpServletRequest instanceof ContentCachingRequestWrapper) {
            return (ContentCachingRequestWrapper) httpServletRequest;
        } else {
            return new ContentCachingRequestWrapper(httpServletRequest, printMaxSize);
        }
    }
}

注入容器

@Configuration
public class FilterConfig {

    /**
     * 日志长度限制
     */
    private Integer printMaxSize = 1024;

    @Bean
    public LogFilter getLogFilter() {
        return new LogFilter(printMaxSize);
    }

}

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

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

相关文章

【数据结构与算法篇】时间复杂度与空间复杂度

目录 一、数据结构和算法 1.什么是数据结构&#xff1f; 2.什么是算法&#xff1f; 3.数据结构和算法的重要性 二、算法的时间复杂度和空间复杂度 1.算法效率 2.算法的复杂度 3.复杂度在校招中的考察 4.时间复杂度 5.空间复杂度 6.常见复杂度对比 7.复杂度的OJ练…

鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…

数据结构入门-10-AVL

文章目录一、AVL的性质1.2 平衡二叉树定义二、添加需达到平衡2.1 平衡因子2.1.2 平衡因子的实现2.2 判断该二叉树是否为平衡二叉树2.3 左旋右旋2.3.1 左旋LL右旋RR基本原理2.3.2 LR RLLRRL三、AVL中删除一、AVL的性质 平衡二叉树 AVL树得名于它的俄罗斯发明者G. M. Adelson-Ve…

信息系统项目管理师第四版知识摘编:第23章 组织通用管理​

第23章 组织通用管理​ 组织通用管理是项目管理的关键前提和基础&#xff0c;它为项目管理提供思想路线和基本原则与方法&#xff0c;项目管理则是通用管理方法在特定场景下的具体表现。​ 23.1人力资源管理​ 23.1.1人力资源管理基础​ 在人力资源管理方面&#xff0c;组织…

对XSS攻击进行的一些总结

简介 XSS漏洞最早被发现是在1996年&#xff0c;由于JavaScript的出现&#xff0c;导致在Web应用程序中存在了一些安全问题。在1997年&#xff0c;高智文(Gareth Owen)也就是“XSS之父”&#xff0c;在他的博客中描述了一种称为“脚本注入”(script injection)的攻击技术&#x…

分享一个RecyclerView嵌套webview 滑动不流畅的解决方法

因RecyclerView 和webview 或者X5的webview 都具有滑动的功能 所以在做嵌套的时候 会出现滑动不流畅 等问题 解决思路 就是判断滑动的时候 是否自身消耗 或者父布局的RecycleView消耗 开发中还遇到个问题 就是 更换成x5的时候 getScrolly() 返回的一直是0 改成getWebScrollY(…

2022国赛16:神州路由器交换机BGP配置实例1

实验拓扑图 一、基本配置: R1配置: Router>ena Router#conf Router_config#host R1 R1_config#int g0/0 R1_config_g0/0#ip add 202.11.1.1 255.255.255.252 R1_config_g0/0#int l0 R1_config_l0#ip add 1.1.1.1 255.255.255.255 R1_confi

【iOS】iOS语音通话回音消除(AEC)技术实现

一、前言 在语音通话、互动直播、语音转文字类应用或者游戏中&#xff0c;需要采集用户的麦克风音频数据&#xff0c;然后将音频数据发送给其它终端或者语音识别服务。如果直接使用采集的麦克风数据&#xff0c;就会存在回音问题。所谓回音就是在语音通话过程中&#xff0c;如…

Minikube安装、运行

1.Minikube是什么 本地的k8s集群&#xff0c;方便开发者学习k8s。 2.安装的前提条件 2个CPU货以上。2G内存或以上。20G磁盘或以上。可以链接互联网。安装docker&#xff08;官网说或者一个虚拟环境&#xff0c;这个不考虑&#xff09;。 3.官网地址 minikube start | minik…

docker部署mysql5.7

1、拉取镜像 docker pull mysql:5.72、运行容器 docker run -p 3306:3306 --name my-mysql -v $PWD/conf:/etc/mysql -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORDqazWSX123 -d mysql:5.7-v $PWD/conf:/etc/mysql指令是挂载mysql的配置文件到宿主机 -v $PWD/data:/var…

基于Java+SpringBoot+vue的口腔管家平台设计与实现【源码(完整源码请私聊)+论文+演示视频+包运行成功】

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战 &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例&#xff08;300套&#xff09; 目录 一、效果演示 二、…

DJ3-4 实时调度

目录 3.4.1 实现实时调度的基本条件 1. 提供必要的信息 2. 系统的处理能力强 3. 采用抢占式调度机制 4. 具有快速切换机制 3.4.2 实时调度算法的分类 1. 非抢占式调度算法 2. 抢占式调度算法 3.4.3 常用的几种实时调度算法 1. 最早截止时间优先 EDF&#xff08;Ea…

拼多多按关键字搜索商品 API

一、拼多多平台优势&#xff1a; 1、独创拼团模式 拼团拼单是拼多多独创的营销模式&#xff0c;其特点是基于人脉社交的裂变传播&#xff0c;非常具有传播性。 由于本身走低价路线&#xff0c;加上拼单折扣&#xff0c;商品的分享和人群裂变效果非常明显&#xff0c;电商前期…

单向链表和双向链表的实现 (LinkedList)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

Markdown发布静态网页

解析markdown_利用 markdown 生成页面实践_灰色派的博客-CSDN博客 业务需求 1.为什么要用 markdown 来生成页面&#xff1f; 对于展示型网站&#xff0c;例如官网这种场景&#xff0c;会有很多小的页面&#xff0c;运营会根据市场时刻有增删修改类似页面的需求&#xff0c;如…

MapReduce笔记

总计&#xff1a;切片就是对一个文件按逻辑进行切片&#xff0c;默认每128m为一个切片&#xff0c;不是物理切片&#xff0c;每个切片对应着一个mapTask进行处理。而且切片是针对每一个文件进行切片的&#xff0c;即一个文件一个文件切片&#xff0c;不是把所以待处理的文件总量…

游戏运营是什么?运营专员需要具备什么能力?

游戏运营主要是负责公司游戏的运营计划制定&#xff0c;包含通过用户对游戏的反馈和数据分析找出产品所存在的问题&#xff0c;并进行优化。通过各种促销活动、节假日实行游戏付费内容来刺激消费&#xff0c;实现游戏和品牌双赢的打造过程。 游戏运营分很多种&#xff1a;用户…

python获取数据类型

之前将字面量时就将到过 数据是有类型的 python的数据类型还挺多的 而现阶段 我们主要接触了 字符串 浮点数 正整数 对应的类型 都有自己的名称 字符串 string 浮点数 float 正整数 int python提供了一个type语句 用于验证数据类型 我们打开编辑工具 编写代码如下 print(typ…

每日一问-ChapGPT-20230416-中医基础-经络

文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230416-中医基础-经络人体的经络有哪些&#xff0c;有什么规律&#xff0c;怎么记忆问诊的具体细节当日总结每日一问-ChapGPT系列起因 近来看了新闻&#xff0c;看了各种媒体&#xff0c;抖音&#xff0c;官媒&#xff0c…

FPGA与ASIC的区别

先来看张图&#xff0c;本图体现出了集成电路产业链&#xff1a;设计业、制造业、封测业。 关于制造、封装测试我们看两张图稍作了解即可&#xff1a; 数字IC ASIC设计流程及EDA工具&#xff1a; &#xff08;1&#xff09;了解数字IC设计&#xff1a;在VLSI时代&#xff…