拿来吧你——一个类帮你搞定SpringBoot中的请求日志打印

news2024/10/7 8:26:32

拿来吧你——一个类帮你搞定SpringBoot中的请求日志打印

日常开发工作中避免不了要打印请求日志,这个功能几乎在所有的项目中都需要编写一次,重复的次数多了,难免会感觉繁琐,因此打算搞一个通用类把这块功能拆出来。

废话不多说——先上代码。

我是代码

@WebFilter(filterName = "logFilter", urlPatterns = "/planet/*")
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class LogFilter implements Filter {

    private static final Set<String> IGNORE_PATHS = Collections.unmodifiableSet(new HashSet<>(
            Arrays.asList("/**/swagger-ui/**", "/**/swagger-resources/**", "/**/api-docs")));

    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 忽略文件上传
        String contentType = httpServletRequest.getHeader("content-type");
        if (contentType != null && contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        // 忽略指定url
        String path = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()).replaceAll("[/]+$", "");
        PathMatcher matcher = new AntPathMatcher();
        boolean isIgnore = IGNORE_PATHS.stream().anyMatch(ignore -> matcher.match(ignore, path));
        if (isIgnore) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);
        RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
        long before = System.currentTimeMillis();

        log.info("========================================== Request Start ==========================================");
        log.info("URL            : {}", httpServletRequest.getRequestURL().toString());

        Map<String, String> headers = new HashMap<>(32);
        Enumeration<String> names = httpServletRequest.getHeaderNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            headers.put(name, httpServletRequest.getHeader(name));
        }
        log.info("Headers            : {}", headers);
        log.info("HTTP Method    : {}", httpServletRequest.getMethod());
        log.info("IP             : {}", httpServletRequest.getRemoteAddr());
        log.info("Request Body   : {}", JSON.toJSON(requestWrapper.getBody()));
        filterChain.doFilter(requestWrapper, wrapperResponse);
        byte[] content = wrapperResponse.getContent();
        log.info("Response Status  : {}", wrapperResponse.getStatus());
        log.info("Response Content  : {}", JSON.toJSON(new String(content)));
        log.info("Time-Consuming : {} ms", System.currentTimeMillis() - before);
        log.info("=========================================== End ===========================================");
        ServletOutputStream out = httpServletResponse.getOutputStream();
        out.write(content);
        out.flush();
    }
}


class ResponseWrapper extends HttpServletResponseWrapper {

    private final ByteArrayOutputStream buffer;

    private final ServletOutputStream out;

    public ResponseWrapper(HttpServletResponse httpServletResponse) {
        super(httpServletResponse);
        buffer = new ByteArrayOutputStream();
        out = new WrapperOutputStream(buffer);
    }

    @Override
    public ServletOutputStream getOutputStream() {
        return out;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
    }

    public byte[] getContent() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    static class WrapperOutputStream extends ServletOutputStream {
        private final ByteArrayOutputStream bos;

        public WrapperOutputStream(ByteArrayOutputStream bos) {
            this.bos = bos;
        }

        @Override
        public void write(int b) {
            bos.write(b);
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener arg0) {

        }
    }

}

class RequestWrapper extends HttpServletRequestWrapper {
    private final String body;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (IOException ignored) {

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };

    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getBody() {
        return this.body;
    }

}

相关问题

这个类是干啥用的?

这个类是一个过滤器,用来在Request与Response之间打印请求与响应日志。

tip:一次编写、处处粘贴。

这个类的处理逻辑是啥?

众所周知,Servlet用来处理用户请求并且对用户请求进行响应,而Filter则是在请求达到Servlet之前,服务器响应返回到Servlet之前对数据进行的一次预处理。

里面几个内部类是干啥用的?

因为request、response都是以流的形式进行传输的,而流有一个特性就是只允许读取一次,因此需要对Request与Response进行一次扩展,使其支持多次读取,其实现方式为:在第一次读取的时候将数据缓存起来,后续读取时直接读取缓存的内容。

还有其他问题吗?

过滤器虽然功能比较强大,但是其影响面也是十分巨大的。

因为过滤器会在所有的请求前后做一些预处理操作,如果预处理操作比较耗时则会降低部分系统性能(QPS、TPS)。

Ctrl+C之前,记得先点个赞哦。

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

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

相关文章

虹科方案|使用 HK-TRUENAS支持媒体和娱乐工作流程-1

一、摘要 开发和交付能够随时随地触及受众的媒体内容变得越来越重要和复杂。 在当今高度互联、娱乐驱动的世界中&#xff0c;媒体和娱乐 (M&E) 公司需要保持竞争力才能取得成功。 这些组织需要制作各种不同格式的信息和娱乐内容&#xff0c;以便在移动设备、台式机、工作站…

MySQL---基本操作DDL(SQL特点,数据类型,对数据库的操作,对表的操作)

1. SQL的特点 具有综合统一性&#xff0c;不同数据库的支持的SQL稍有不同 非过程化语言 语言简捷&#xff0c;用户容易接受 以一种语法结构提供两种使用方式 2. 对数据库的常用操作 功能 SQL 查看所有的数据库 show databases&#xff1b; 创建数据库 create databa…

设备驱动模型:总线-设备-驱动

1 设备驱动模型简介 参考 以下内容&#xff1a; Linux 笔记&#xff1a; https://xuesong.blog.csdn.net/article/details/109522945?spm1001.2014.3001.5502正点原子-左盟主 驱动开发网络资料&#xff1a;https://www.cnblogs.com/lizhuming/category/1859545.html 1.1 概…

Github的加速访问

文章目录 概述Steam的下载Steam的安装使用 概述 GitHub打开访问速度比较慢&#xff0c;这儿介绍一种加速访问的方式&#xff0c;是正规的方式&#xff0c;采用 Steam 来加速。 Steam的下载 浏览器输入框输入Watt Toolkit进行搜索&#xff0c; 选择官网进入&#xff0c;网址 …

蚂蚁安全科技 Nydus 与 Dragonfly 镜像加速实践 | 龙蜥技术

编者按&#xff1a;本文详细介绍蚂蚁安全科技使用龙蜥社区技术进行镜像加速的实践过程&#xff0c;可以让您了解如何基于龙蜥社区推出的容器镜像&#xff0c;Nydus 与 Dragonfly 镜像加速技术和 LifseaOS 为容器的启动加速。文章转自金融级分布式架构&#xff0c;以下为全文。 …

计算材料学有哪些SCI期刊推荐? - 易智编译EaseEditing

以下是一些计算材料学领域的SCI期刊推荐&#xff1a; Computational Materials Science&#xff1a; 该期刊发表计算材料科学的理论、计算和实验研究&#xff0c;包括材料结构、热力学、物理和化学性质以及材料的设计、制备和性能等方面的内容。 Materials Horizons&#xff1…

JavaWeb——HTML中的常用标签详解

目录 一、HTML 1、HTML标签结构 2、HTML文件结构 &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、标签层次结构 二、HTML常见标签 1、注释标签 2、标题标签 3、段落标签 4、换行标签 5、格式化标签 6、图片标签 &#xff08;1&#xff09;、定义 &a…

4。计算机组成原理(3)指令系统

嵌入式软件开发&#xff0c;非科班专业必须掌握的基本计算机知识 核心知识点&#xff1a;数据表示和运算、存储系统、指令系统、总线系统、中央处理器、输入输出系统 指令系统&#xff08;Instruction Set&#xff09;是计算机体系结构的关键组成部分之一&#xff0c;它定义了处…

Java面试题复习(1)

目录 1.mysql使用innodb引擎&#xff0c;请简述mysql索引的最左前缀&#xff0c;如何优化order by语句 2.在JVM内存模型中&#xff0c;为什么要区分新生去和老年代&#xff0c;对于新生代为什么要区分eden区和survial区&#xff1f; 3.常见的远程调用有几种 4.对于外部衔接的…

ChatGPT总是答非所问?如何使用chatgpt定义角色

一、&#x1f4dd; 定义角色&#xff1a;ChatGPT 的角色设定 背景信息&#xff1a;提供详细、准确的背景信息和前提条件&#xff0c;以便 ChatGPT 提供有针对性的回答和建议 任务目标&#xff1a;清晰地描述希望 ChatGPT 完成的任务 输出要求&#xff1a;告知 ChatGPT 如何完…

chatgpt应用市场

简介:利用ChatGPT的模型能力可以开发出垂直领域的应用市场。例如,可以使用ChatGPT来构建一个智能医疗助手,该助手可以回答患者关于疾病、症状、治疗方案等方面的问题。另外,也可以使用ChatGPT来构建一个智能金融助手,该助手可以回答用户关于投资、理财、贷款等方面的问题。…

MySQL的两个原则,两个优化,一个bug

背景&#xff1a;因为间隙锁在可重复读隔离级别下才有效&#xff0c;所以本篇文章接下来的描述&#xff0c;若没有特殊说明&#xff0c;默认 是可重复读隔离级别。 1.加锁规则里面&#xff0c;包含了两个“原则”、两个“优化”和一个“bug”。 (1). 原则1:加锁的基本单位是n…

家用洗地机有什么推荐的吗?家用洗地机分享

洗地机是解决清洁问题的智能设备。多年的市场验证已证明&#xff0c;它比传统清洁方法更加经济、高效和环保。相比传统方式&#xff0c;洗地机可以以更小的成本清洁更大的空间。同时&#xff0c;它的清洁效果也更好&#xff0c;采用先进的技术和设备&#xff0c;包括高压喷洒、…

第二十一章 备忘录模式

文章目录 前言一、备忘录模式基本介绍二、备忘录模式原理代码实现备忘录对象 Memento发起者 Originator管理备忘录类 CaretakerClint 测试类 三、游戏角色恢复状态实例备忘录对象游戏角色管理备忘录对象测试 Clint 四、备忘录模式的注意事项和细节 前言 一、备忘录模式基本介绍…

顺序表(二)

文章目录 前言一.什么是链表二.线性表的链表结构2.1链表的初始2.2 链表的分类2.3 单链表2.4 双链表 三.java里面的LinkedList API3.1 基础操作3.2 链表的遍历操作 四.模拟实现LinkedList的相关操作创建一个链表头插法尾插法任意位置插入,第一个数据节点为0号下标查找是否包含关…

【网络安全】记一次杀猪盘渗透实战

看起来非常假的网站&#xff0c;这个网站是没有 cdn 的用的是 thinkphpk 框架搭建的。 先打一波 poc 没有效果 访问一下后台直接在 url 后面加/admin。 一个开源的 cms 还没有验证码尝试用 burp 进行爆破&#xff0c;首先在火狐上设置代理 ip 为 127.0.0.1 代理端口为 8081。 B…

性能测试----负载测试、压力测试、并发测试

性能测试&#xff1a;检测一个软件的性能。 性能测试的指标&#xff1a; 响应时间&#xff1a;用户从请求到服务器响应的时间 吞吐量&#xff1a;单位时间内成功地传送数据的数量 并发数&#xff1a;在线并且在操作的用户数 负载测试&#xff1a;加负载&#xff0c;找到让系…

内网:定位域管理员

在域渗透中&#xff0c;获取域内的一个支点后&#xff0c;需要获取管理员权限。在一个域中&#xff0c;当计算机加入域后&#xff0c;会默认给域管理员组赋予本地系统管理员权限。当计算机被添加到域中&#xff0c;成为域的成员主机时&#xff0c;系统会自动将域管理员组添加到…

( “ 图 “ 之 拓扑排序 ) 207. 课程表 ——【Leetcode每日一题】

❓207. 课程表 难度&#xff1a;中等 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课…

C#_委托简述

一.委托的分类 1.delegate关键字 直接通过delegate关键字定义委托类&#xff0c;定义示例如下&#xff1a; delegate void Calculate(int x, int y, int z); 此时可将返回值类型为void、参数类型为(int, int, int)的函数委托给Calculate&#xff0c;示例&#xff1a; Calcu…