Spring Boot 3.x Filter实战:记录请求日志

news2024/9/20 0:56:58

上一篇:Spring Boot 3.x Web单元测试最佳实践

前面我们在《Spring Boot 3.x Rest API最佳实践之统一响应结构》中学习响应的统一拦截处理,顺带完成了响应结果的记录;而对于请求内容咱们也必须进行日志记录,以确保排查问题时有据可循。为此,本小节咱们利用filter组件来实现这一需求。如果觉得对你有帮助,记得点赞收藏,关注小卷,后续更精彩!

在这里插入图片描述

文章目录

    • filter开发
    • filter配置
    • 实现logParams方法
    • 实现logRequestBody方法
    • 完善CustomErrorAttributes异常输出
    • 存在的问题:流不可重复读

filter开发

因为统一请求日志记录属于基础功能,我们在 com.juan.demo.common基础包下来创建,以便后续可以将其抽出来做成一个common子模块进行维护。

这里我们从OncePerRequestFilter继承,这样可以确保一个请求内部无论经过多少次转发,始终会被拦截一次。

package com.juan.demo.common.web.filter;

import ...

@Slf4j
public class RequestLogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("============ {}:{}", request.getMethod(), request.getRequestURL());

        filterChain.doFilter(request, response);
    }
}

这里我们先做最简单的实现:仅打印下请求方法和请求路径,然后继续将请求放行。

filter配置

com.juan.demo.common.web包下创建一个@Configuration修饰的配置类,在内部通过@Bean修饰的方法来创建相关bean,包括了filter本身的bean和用于filter bean注册和配置的FilterRegistrationBean类型的bean

package com.juan.demo.common.web;

import ...

@Configuration
public class WebConfig {

    @Bean
    public RequestLogFilter requestLogFilter() {
        // new一个filter bean实例
        return new RequestLogFilter();
    }

    @Bean
    public FilterRegistrationBean<RequestLogFilter> requestLogFilterRegistrationBean() {
        FilterRegistrationBean<RequestLogFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(requestLogFilter());
        registrationBean.setName("requestLogFilter");
        registrationBean.addUrlPatterns("/*"); // todo 暂时对所有的请求拦截
        registrationBean.setOrder(1);
        return registrationBean;
    }

}

运行先前开发的web单元测试用例,看到请求被RequestLogFilter拦截到了:

在这里插入图片描述

实现logParams方法

这里主要考察request.getParameterMap()方法的使用。注意,它返回的value是一个字符串数组,一个参数key可以对应多个value,在输出日志时,用了org.apache.commons:commons-lang3依赖中的StringUtils工具类的join(...)方法实现数组内容的拼接。

package com.juan.demo.common.web.filter;

import ...

@Slf4j
public class RequestLogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("============ {}:{}", request.getMethod(), request.getRequestURL());

        logParams(request);

        filterChain.doFilter(request, response);
    }

    private void logParams(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        int size = parameterMap.size();
        if (size == 0) return;
        StringBuilder paramBuilder = new StringBuilder();
        Set<String> paramNames = parameterMap.keySet();
        int index = 0;
        for (String paramName : paramNames) {
            paramBuilder.append(paramName).append("=").append(StringUtils.join(parameterMap.get(paramName), ","));
            if (index < size - 1) {
                paramBuilder.append(", ");
            }
            index++;
        }
        log.info("============ params:{}", paramBuilder);
    }

}

测试结果:

同样,客户端提交的formData形式的数据,也能被拦截输出:

实现logRequestBody方法

实现logRequestBody方法,逻辑是:判断请求内容类型为json格式时,就借助objectMapperreadTree方法,传入request.getInputStream()方法获取的流信息,从而读取到树形的JsonNode信息,再序列化为json信息。

package com.juan.demo.common.web.filter;

import ...

@Slf4j
public class RequestLogFilter extends OncePerRequestFilter {
    @Resource
    private ObjectMapper objectMapper;
    
    @Override
    protected void doFilterInternal(...) ... {
        ...
        logRequestBody(request);
        filterChain.doFilter(request, response);
    }
    ...
    @SneakyThrows
    private void logRequestBody(HttpServletRequest request) {
        if (checkContentType(request, MediaType.APPLICATION_JSON_VALUE)) {
            log.info("============ body:{}", objectMapper.readTree(request.getInputStream()));
        }
    }

    private boolean checkContentType(HttpServletRequest request, String contentType) {
        return StringUtils.startsWith(request.getContentType(), contentType);
    }
}

测试下添加购物车,发现报了400http状态码,并提示错误信息:

再看看日志,并没有抛出异常堆栈信息:

在这里插入图片描述

完善CustomErrorAttributes异常输出

CustomErrorAttributes中加一段逻辑:

再测试就发现控制台抛出的错误了:

在这里插入图片描述

body信息确实打印到日志了,但是spring web模块在controller层进行请求信息转换成对象时,框架内部会再次调用request.getInputStream()就报出这样的错误了。

存在的问题:流不可重复读

很显然,这里无法两次调用request.getInputStream(),存在着流不可重复读的问题。下一小节我们将巧妙的通过流信息缓存来解决这个问题,大家加油!

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

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

相关文章

又一苹果经典产品宣布停产,老用户满满的回忆

苹果公司的SuperDrive&#xff0c;作为一项曾经引领潮流的外置光驱技术&#xff0c;自2008年伴随着革命性的MacBook Air轻盈登场以来&#xff0c;便成为了苹果电脑产品线中一道亮丽的风景线&#xff0c;象征着科技与美学的完美结合。 这款光驱以其超薄的设计、高效的读写速度以…

leetcode787. K 站中转内最便宜的航班——优先队列优化的Dijkstra算法+剪枝

题目 leetcode787. K 站中转内最便宜的航班 题目分析 给定一个城市图&#xff0c;每个城市通过航班与其他城市相连。每个航班都有一个起点、终点和价格。你需要找到从起点城市 src 到终点城市 dst 的最便宜路径&#xff0c;但这条路径最多只能经过 k 个中转站。你需要返回这…

构建智能生态,视频监控/安防监控EasyCVR视频汇聚流媒体技术在智能分析领域的应用

随着5G、AI、物联网&#xff08;IoT&#xff09;、云计算等技术的快速发展&#xff0c;万物互联的时代已经到来&#xff0c;全新的行业生态AIoT正在引领一场深刻的变革。在这场变革中&#xff0c;EasyCVR视频流媒体技术以其强大的视频处理、汇聚与融合能力&#xff0c;在智能分…

2024年计算机类学术会议有哪些

随着科技的飞速发展&#xff0c;计算机科学与技术领域正以前所未有的速度进步&#xff0c;各类学术会议成为了交流最新研究成果、探讨前沿技术趋势的重要平台。2024年&#xff0c;全球范围内将举办多场计算机类学术会议&#xff0c;这些会议不仅汇聚了顶尖的专家学者&#xff0…

创客匠人老蒋:你缺的不是客户,缺的是单个客户的营销和变现能力

老蒋创客圈第58期对话标杆直播连麦&#xff0c;我们邀请到【拾才易人】平台创始人侯邦辉老师。侯老师与创客匠人合作3年之久&#xff0c;实现了线上线下高转化&#xff0c;并实现家庭教育、心理疗愈、国学传承与营销运营一站式学习平台。 上篇文章&#xff0c;我们主要梳理了连…

Gemma Scope 帮助理解 AI 模型的内部工作原理

Gemma Scope 工具如何帮助理解 AI 模型&#xff08;具体来说是 Gemma 模型&#xff09;的内部工作原理&#xff0c;通过观察模型的“特征”来理解 AI 是如何“思考”的 1 特征&#xff08;features&#xff09; 了解AI模型在思考什么。Gemma Scope 将 Gemma 模型的大脑分解成…

延时队列与redis and rabbitmq

延时队列是什么 延时队列&#xff08;Delay Queue&#xff09;是一种特殊的消息队列&#xff0c;它允许你在添加消息时设置一个延时时间&#xff0c;消息只有在延时时间到达后才能被消费。这种机制在分布式系统中非常有用&#xff0c;常用于处理需要在指定时间后执行的任务&am…

光耦合器知识概述

光耦合器&#xff0c;又称光电耦合器&#xff0c;是一种通过光信号来实现电信号隔离的电子元件。它在确保电路安全和信号完整性方面起着关键作用&#xff0c;广泛应用于电源管理、工业自动化、消费电子等领域。本文将深入探讨光耦合器的工作原理、技术参数、应用场景、选型指南…

【JAVA入门】Day19 - BigInteger 和 BigDecimal

【JAVA入门】Day19 - BigInteger 和 BigDecimal 文章目录 【JAVA入门】Day19 - BigInteger 和 BigDecimal一、BigInteger1.1 BigInteger 构造方法1.2 BigInteger 内部常见方法 二、BigDecimal2.1 BigDecimal 的作用2.2 BigDecimal 对象的获取2.3 BigDecimal 中常见的成员方法2.…

自动驾驶计算芯片企业“流血”上市,小米、腾讯等曾投7亿美元

"自动驾驶市场何时迎来爆发&#xff1f;" 作者 | 魏 强 编辑 | 卢旭成 今天&#xff0c;自动驾驶计算芯片企业Black Sesame International HoldingLimited(黑芝麻智能)在港交所上市&#xff0c;发行价28港元&#xff0c;募资总额10.36亿港元。 黑芝麻智能号称国…

牛客周赛 Round 54 (个人题解)(待补全)

前言&#xff1a; 如今已经回到返校回家&#xff0c;在家中的学习热情明显下降&#xff0c;在加上练车、和朋友亲戚聚一聚&#xff0c;学习的时间明显下降&#xff0c;希望自己能更加努力一点吧&#xff0c;之后想通过发博客来监督自己在暑假家中的努力&#xff0c;希望自己能做…

剖析算法内部结构----------贪心算法

什么是贪心算法&#xff1f; 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在问题求解过程中&#xff0c;每一步都采取当前状态下最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致最终的全局最优解的算法策略。 贪心算法的核心思想是做选择时&…

Elasticsearch 查询规则现已正式发布 - query rules

作者&#xff1a;来自 Elastic Kathleen DeRusso 查询规则&#xff08;query rules&#xff09;允许使用细粒度、上下文特定的解决方案来更改特定查询或搜索用例的搜索结果。这对于需要将品牌或赞助结果固定在特定关键字的搜索结果列表顶部的广告系列很有帮助&#xff0c;但对于…

B2 双电机系列挂轨巡检机器人:解决巡检难题,提升工业效能

随着工业自动化的不断发展&#xff0c;传统的人工巡检方式已经难以满足现代工业对安全、效率和精度的要求。旗晟机器人推出的B2双电机系列挂轨巡检机器人&#xff0c;以其独特的优势&#xff0c;为工业巡检领域带来了革命性的变化。 一、产品亮点 B2双电机系列挂轨巡检机器人以…

数据结构初阶最终讲:排序

数据结构初阶最终讲&#xff1a;排序 1.排序的概念及其运用1.1什么是排序1.2排序的运用1.3常见排序算法 2.冒泡排序3.直接插入排序4.堆排序5.测试代码&#xff1a;排序性能对比5.1直接插入排序时间复杂度分析 6.希尔排序6.1希尔排序时间复杂度分析 7.选择排序7.1初步思路7.2选择…

【Python系列】异步编程在 Python 中的应用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

WinCC7.5零基础教学:多功能VB/C脚本基础框架模板程序详解!报表、配方、面板类型、菜单栏切换画、IO控制模板一应俱全,0基础小白值得拥有!

了解基础框架完整功能请观看视频&#xff01; 观看完整教学视频点击这里&#xff08;腾讯视频可放心观看&#xff09; 以下是wincc多功能脚本基础框架项目功能简介&#xff1a; 功能一&#xff1a;多功能标题窗口模块 模块主要功能包括实时报警窗口信息、人员登录登出、报警消…

24暑假算法刷题 | Day30 | 贪心算法 IV | 452. 用最少数量的箭引爆气球,435. 无重叠区间,763. 划分字母区间

目录 452. 用最少数量的箭引爆气球题目描述题解 435. 无重叠区间题目描述题解 763. 划分字母区间题目描述题解 452. 用最少数量的箭引爆气球 点此跳转题目链接 题目描述 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中…

印刷企业实施数字工厂管理系统后能提升哪些效益

在当今这个数字化转型浪潮席卷全球的时代&#xff0c;印刷企业作为传统制造业的重要组成部分&#xff0c;正面临着前所未有的挑战与机遇。为了提升生产效率、降低成本、增强市场竞争力&#xff0c;越来越多的印刷企业开始引入数字工厂管理系统&#xff0c;这一举措不仅重塑了企…

Inno Setup根据系统的不同(32位/64位)安装不同的exe

注意事项 Inno Setup6.0及以上版本生成的可执行文件&#xff0c;可以运行在Windows7及以上系统&#xff0c;不支持WindowsXP系统。 如果要运行在WindowsXP系统上&#xff0c;需要下载Inno Setup6.0之前的版本。 Inno Setup 汉化版5.3.4下载链接&#xff1a; https://download…