springweb添加traceId

上面要求添加 traceId,需求很简单。
唯一的难点是,jakarta.servlet.http.HttpServletRequest 不支持直接 put 请求头。
所以需要创建一个可修改的对象,包装请求。
另外,由于我们应用还使用了 openFeign ,需要将 traceId 传递进去。
PS: 老大非要把appName用做traceId前缀,无语~

过滤器

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 追踪id过滤器
 *
 */
@WebFilter(urlPatterns = "/*")
@Component
@Slf4j
public class TraceIdFilter extends HttpFilter {

    @Value("${spring.application.name:appName}")
    private String appName;

    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
        try {
            // 添加 Header
            String traceId = request.getHeader(HConst.TRACE_ID);
            // 已存在则放行
            if (StringUtils.isNotBlank(traceId)) {
                // 放入日志
                MDC.put(HConst.TRACE_ID, traceId);
                response.setHeader(HConst.TRACE_ID,traceId);
                chain.doFilter(request, response);
            } else {
                // 不存在则自生成
                traceId = appName + "-" + UUIDGenerator.getUUID();
                log.info("appGenerator traceId : {}",traceId);
                // 推入请求
                MutableHttpServletRequest muRequest = new MutableHttpServletRequest(request);
                response.setHeader(HConst.TRACE_ID,traceId);
                muRequest.putHeader(HConst.TRACE_ID, traceId);
                // 放入日志
                MDC.put(HConst.TRACE_ID, traceId);
                chain.doFilter(muRequest, response);
            }
        } catch (Exception e) {
            log.error("TRACE_id异常:", e);
            throw new RuntimeException("TRACE_id异常");
        }
    }
}

可修改的请求适配器

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;

import java.util.*;

/**
 * 可修改的请求对象
 *
 */
public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
    /**
     * 保持自定义头、值
     */
    private final Map<String, String> customHeaders;

    public MutableHttpServletRequest(HttpServletRequest request){
        super(request);
        this.customHeaders = new HashMap<>();
    }

    public void putHeader(String name, String value){
        this.customHeaders.put(name, value);
    }

    @Override
    public String getHeader(String name) {
        // 检查
        String headerValue = customHeaders.get(name);
        if (headerValue != null){
            return headerValue;
        }
        // 返回原头
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        // 创建自定义头容器
        Set<String> set = new HashSet<String>(customHeaders.keySet());
        // 添加头
        @SuppressWarnings("unchecked")
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            // 复制到新容器
            String n = e.nextElement();
            set.add(n);
        }
        // 重新构建并返回
        return Collections.enumeration(set);
    }

}

调用了 openFeign,将 traceId 传入代码

@Slf4j
@Component
public class CipherInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        try {
            // 添加traceId
            addTraceId(template);
            byte[] body = template.body();
            String bodyStr = new String(body, StandardCharsets.UTF_8);
            // 打印请求消息
            log.info("feign-request-path: {}", template.path());
            log.info("feign-request-bodyStr : {}", bodyStr);
        } catch (Exception e) {
            log.error("CipherInterceptor error :", e);
        }
    }

    /**
     * 添加请求头
     * @param template 请求对象
     */
    private static void addTraceId(RequestTemplate template) {
        Map<String, Collection<String>> headers = template.headers();
        String traceId = ServletUtil.getHeader(HConst.TRACE_ID);
        if (StringUtils.isBlank(traceId)) {
            traceId = "feign-" + UUIDGenerator.getUUID();
        }
        headers.put(HConst.TRACE_ID, Collections.singletonList(traceId));
        log.info("feign-request-headers: {}", headers);
    }

}

其它代码

从请求上下文中获取请求头方法,生成 UUID
    public static String getHeader(String name){
        // 获取请求头信息
        ServletRequestAttributes requestAttributes =
            (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = null;
        if (requestAttributes != null) {
            request = requestAttributes.getRequest();
            return request.getHeader(name);
    }
        return null;
    }

    public static String getUUID() {
        String s = UUID.randomUUID().toString();
        return s.replaceAll("-", "");
    }

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

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

相关文章

DoIP-1 简介

1. 概述 DoIP-Diagnostic Over Internet Protocol &#xff0c;基于TCPIP协议族的诊断传输协议 DoIP国际标准定义为ISO 13400&#xff0c;总共由五部分组成&#xff1a;  ISO13400-1DoIP的综述  ISO13400-2DoIP的传输层和网络层服务&#xff08;主体部分&#xff09;  I…

JavaSe系列二十七: Java正则表达式

正则表达式 为什么要学习正则表达式再提几个问题解决之道-正则表达式正则表达式基本介绍介绍 正则表达式底层实现实例分析 正则表达式语法基本介绍元字符-转义号 \\\\元字符-字符匹配符元字符-选择匹配符元字符-限定符元字符-定位符分组非贪婪匹配 应用实例对字符串进行如下验证…

【c++刷题笔记-数组】day29:452. 用最少数量的箭引爆气球、 435. 无重叠区间 、 763.划分字母区间

452. 用最少数量的箭引爆气球 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;先按照左边界排序&#xff0c;当前的左边界大于前一个的右边界的时候&#xff0c;表示没有覆盖所以需要一根箭&#xff0c;反之则要更新为最小的右边界 重点&#xff1a;是区间覆盖问题…

webrtc gcc详解

webrtc的gcc算法(Google Congestion Control)&#xff0c;貌似国内很多文章都没有细讲&#xff0c;原理是怎么样的&#xff0c;具体怎么进行计算的。这里详解一下gcc。 gcc算法&#xff0c;主要涉及到&#xff1a; 拥塞控制的关键信息和公式 卡曼滤波算法 gcc如何使用卡曼滤…

JavaScript 原型链那些事

在讲原型之前我们先来了解一下函数。 在JS中&#xff0c;函数的本质就是对象&#xff0c;它与其他对象不同的是&#xff0c;创建它的构造函数与创建其他对象的构造函数不一样。那产生函数对象的构造函数是什么呢&#xff1f;是一个叫做Function的特殊函数&#xff0c;通过newFu…

Python从入门到放弃——深入研究Print函数

深入浅出Print函数 第一个代码“Hello World” 在正常配置了PyCharm或者Thonny等编辑器之后&#xff0c;我们开始写第一个代码。正常的情况下学习一门编程语言&#xff0c;一般第一个代码都是输出Hello World。那么如何打印Hello World呢&#xff1f; print("Hello Wor…

第六十八回 东平府误陷九纹龙 宋公明义释双枪将-文心大模型ernie-speed免费使用方法

宋江和卢俊义抓阄儿&#xff0c;宋江打东平府&#xff0c;卢俊义打东昌府&#xff0c;谁先打下谁做梁山泊主。宋江带领林冲、花荣、刘唐等二十八人&#xff0c;卢俊义带领吴用、公孙胜、关胜等二十八人。 宋江等人到了东平府外安山镇&#xff0c;郁保四和王定六自告奋勇去下战…

io流 多线程

目录 一、io流 1.什么是io流 2.流的方向 i.输入流 ii.输出流 3.操作文件的类型 i.字节流 1.拷贝 ii.字符流 ​3.字符流输出流出数据 4.字节流和字符流的使用场景 5.练习 6.缓冲流 1.字节缓冲流拷贝文件 2.字符缓冲流特有的方法 1.方法 2.总结 7.转换流基本用法…

掌握MySQL基础命令:数据更新操作详细操作(数据的增删改)

MySQL数据修改是指使用SQL语句&#xff08;如UPDATE、INSERT、DELETE&#xff09;对数据库表中的数据进行更改、添加或删除的操作&#xff0c;常见的操作包括更新表中的记录、插入新记录以及删除现有记录 。 一、数据插入 1插入完整的数据记录 2插入非完整的数据记录 3插入多…

Vulkan 学习(1)---- Vulkan 基本概念和发展历史

目录 Vulkan及其演化史Vulkan 基本概念基本术语 Vulkan 的原理Vulkan应用程序Vulkan的编程模型硬件初始化窗口展示表面资源设置流水线设置描述符和描述符缓冲池基于SPIR-V的着色器流水线管理指令的记录队列的提交 Vulkan及其演化史 目前主流的图形渲染API有OpenGL、OpenGL ES、…

应急响应--网站(web)入侵篡改指南

免责声明:本文... 目录 被入侵常见现象: 首要任务&#xff1a; 分析思路&#xff1a; 演示案例: IIS&.NET-注入-基于时间配合日志分析 Apache&PHP-漏洞-基于漏洞配合日志分析 Tomcat&JSP-弱口令-基于后门配合日志分析 (推荐) Webshell 查杀-常规后门&…

ThinkPHP定时任务是怎样实现的?

接到一个需求&#xff1a;定时检查设备信息&#xff0c;2分钟没有心跳的机器&#xff0c;推送消息给相关人员&#xff0c;用thinkphp5框架&#xff0c;利用框架自带的任务功能与crontab配合来完成定时任务。 第一步&#xff1a;分析需求 先写获取设备信息&#xff0c;2分钟之…

Winform中使用HttpClient实现调用http的post接口并设置传参content-type为application/json示例

场景 Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类&#xff1a; Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类_winform解析json-CSDN博客 上面使用HttpClient调用post接口时使用的HttpCon…

卷积神经网络基础篇

文章目录 1、卷积层1.1、激活函数1.3、sigmoid1.4、Tanh1.5、ReLU1.6、Leaky ReLU1.7、误差计算 2、池化层3、全连接层4、CNN训练 参考链接1 参考链接2 1、卷积层 卷积层&#xff08;Convolutional layer&#xff09;&#xff0c;这一层就是卷积神经网络最重要的一个层次&…

spRAG框架学习小结

spRAG是什么 spRAG是一个针对非结构化数据的检索引擎。它特别擅长处理对密集文本的复杂查询&#xff0c;比如财务报告、法律文件和学术论文。有两种关键方法用于提高性能&#xff0c;超越了普通的RAG系统&#xff1a; 自动上下文&#xff08;AutoContext&#xff09;&#xff…

几款电脑端能够运行的AI大模型聊天客户端

Ollama Ollama 是一个用于在本地运行和管理大型语言模型的工具。它支持多种流行模型的下载和本地运行&#xff0c;包括 LLaMA-2、CodeLLaMA、Falcon 和 Mistral 。Ollama 提供了一个简单、轻量级和可扩展的解决方案&#xff0c;使得用户可以以最简单快速的方式在本地运行大模型…

中霖教育:二级建造师未注册还需要继续教育吗?

关键词&#xff1a;中霖教育怎么样&#xff0c;中霖教育口碑 如果通过了二级建造师考试但是没有注册&#xff0c;还用继续教育吗? 1. 未注册的二级建造师 二级建造师在其证书获取后三年内没有进行注册时&#xff0c;在申请初始注册之前必须完成规定的本专业继续教育课程。 …

计算样本之间的相似度

文章目录 前言一、距离度量1.1 欧几里得距离&#xff08;Euclidean Distance&#xff09;1.2 曼哈顿距离&#xff08;Manhattan Distance&#xff09;1.3 切比雪夫距离&#xff08;Chebyshev Distance&#xff09;1.4 闵可夫斯基距离&#xff08;Minkowski Distance&#xff09…

Java里的Arrary详解

DK 中提供了一个专门用于操作数组的工具类&#xff0c;即Arrays 类&#xff0c;位于java.util 包中。该类提供了一些列方法来操作数组&#xff0c;如排序、复制、比较、填充等&#xff0c;用户直接调用这些方法即可不需要自己编码实现&#xff0c;降低了开发难度。 java.util.…

时序预测 | Matlab实现TCN-Transformer的时间序列预测

时序预测 | Matlab实现TCN-Transformer的时间序列预测 目录 时序预测 | Matlab实现TCN-Transformer的时间序列预测效果一览基本介绍程序设计 效果一览 基本介绍 基于TCN-Transformer模型的时间序列预测&#xff0c;可以用于做光伏发电功率预测&#xff0c;风速预测&#xff0c;…