为全局请求添加 TraceId ,看日志再也不懵逼

如何为服务自定义链路追踪(traceID) 链路追踪是一种追踪,监控服务请求的技术概称。主要是为了帮助开发者了解一个请求在服务间的流转路径,执行时间,延迟以及问题追踪定位。特点:可视化请求流:展示一个请求的完整调用路径。性能分析:识别请求在哪个环节出现延迟,优化系统性能。问题排查:快速定位错误发生的位置,提高故障恢复效率。调用关系分析:帮助理解微服务之间的依赖关系。链路追踪的工作流程请求进入系统:用户发起 HTTP 请求,链路追踪系统生成一个Trace ID。请求传播Trace ID。 阅读详情

不知道大家有没有一堆日志就是定位不到那块是异常部分,接口错误无法复现,也找不到报错信息等比较棘手的问题。

其实解决上面的问题很简单,只要我们为每一个请求都分配一个唯一的 RequestId 或者叫 TraceId ,一旦出了问题,只需要拿着 Id 去日志里一搜,妖魔鬼怪立马原形毕露。

对于分布式链路追踪,有很多开源中间件,本文主要通过 logback 的 MDC 实现。

请求拦截器

@Component
public class TraceIdInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String traceId = String.format("%s - %s",request.getRequestURI(), UUID.fastUUID().toString(true));
        MDC.put("traceId", traceId);
        return true;
 }

注册拦截器

@Component
@RequiredArgsConstructor
public class GlobalWebMvcConfigurer implements WebMvcConfigurer {
​
    private final TraceIdInterceptor traceIdInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(traceIdInterceptor);
    }
}

统一返回值

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResponse<T> implements Serializable {
​
    /** 错误码 */
    private Integer code;
​
    /** 错误消息 */
    private String message;
​
    /** 泛型响应数据 */
    private T Data;
​
    private String traceId;
​
    public CommonResponse(Integer code, String message) {
​
        this.code = code;
        this.message = message;
        this.traceId = MDC.get("traceId");
    }
​
}

日志配置

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATTERN"
              value="%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%highlight(%-5level)] [%boldYellow(%X{traceId})] [%boldYellow(%thread)] %boldGreen(%logger{36} %F.%L) %msg%n">
    </property>
​
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <!-- 控制台打印INFO及以上级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
    </appender>
​
    <root>
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

测试

接口返回值

{
  "code": 0,
  "message": "",
  "traceId": "/commerce-user/user/findById - 73e470120ef24d57a111de6b671d030b",
  "data": {
    "id": 1,
    "username": "test1",
    "password": "test",
    "extraInfo": "{}",
    "createTime": "2022-06-20T09:23:18.000+00:00",
    "updateTime": "2022-06-20T09:23:18.000+00:00",
    "balance": 0
  }
}

日志

如此,如果线上有接口出现问题,拿着 traceId 到日志文件搜索,就能检索到该请求的日志调用链路,处理问题就变得简单了。

异步调用配置

因为是 ThreadLocal ,异步任务必然是获取不到 traceId 的,需要再线程池配置中手动添加。

线程池配置

@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Bean
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
​
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(20);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("user-async-");   
​
        // 等待所有任务结果候再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        // 定义拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //设置线程装饰器
        executor.setTaskDecorator(runnable -> ThreadMdcUtils.wrapAsync(runnable, MDC.getCopyOfContextMap()));
        // 初始化线程池, 初始化 core 线程
        executor.initialize();
​
        return executor;
    }
}

public class ThreadMdcUtils {
    public static Runnable wrapAsync(Runnable task, Map<String,String> context){
        return () -> {
            if(context==null){
                MDC.clear();
            }else {
                MDC.setContextMap(context);
            }
            if(MDC.get("traceId")==null){
                MDC.put("traceId", UUID.fastUUID().toString(true));
            }
            try {
                task.run();
            }finally {
                MDC.clear();
            }
        };
    }
}

再次测试

traceId跟踪请求全流程日志 一个系统被拆分成N多个模块,这些模块负责不同的功能,组合成一套系统,最终可以提供丰富的功能。在这种分布式架构中,一次请求往往需要涉及到多个服务,如下图:服务之间的调用错综复杂,对于维护的成本成倍增加,势必存在以下几个问题:服务之间的依赖与被依赖的关系如何能够清晰的看到?出现异常时如何能够快速定位到异常服务?出现性能瓶颈时如何能够迅速定位哪个服务影响的?为了能够在分布式架构中快速定位问题,分布式链路追踪应运而生。 阅读详情

相关推荐

SpringBoot项目注入 traceId 来追踪整个请求的日志链路

SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排查问题的时候,可以迅速根据 traceId 查找到相关请求的日志,特别是在生产环境的时候,用户可能只提供一个错误截图,我们作为开发人员想要更详细的日志,这时候我们就可以直接根据用户提供的 traceId 来查找用户这次请求的整个日志,会方便很多。即使是在单体应用中,为请求添加一个唯一的traceId以追踪整个请求链路的日志也是完全可行的。关于更具体的日志输出配置,可以参考我的这篇文章《

z284747的博客 2665

链路追踪的traceid原理梳理

链路追踪的traceid原理梳理 欠

当我谈编程时我谈些什么 2万+

全链路日志追踪traceId(http、dubbo、mq)

一、概述 1、目的 目前许多系统项目之间的调用,有基于微服务的,有通过HTTP请求的,还有通过mq的。那么在处理一次请求的时候,可能会调用多个服务或者调用多个其他系统功能,这样就会产生很多的日志,此时,如果想查看这一次调用的完整的请求链路的日志时,就会变得比较困难,虽然我们有一些集中的日志收集工具比如ELK,我们需要把这一些日志串联起来,这个问题很关键,因为如果没有串联起来,排查日志就是一件很困难的事情。 2、解决办法 一般来说,系统之间的调用不外乎两种:http和dubbo。当然还有通过中间.

promisessh的博客 1万+

链路日志中追踪traceId

mdc就是基于Threadlocal进行的一个流程周转的标志物的传递,就是根据这种标志,可以追踪到日志的整体请求记录,便于进行定位到问题所在,而且对于用户的影响极小。

sun_fubao的博客 9976

springboot中记录链路日志traceId

从上面的代码实现,还可以看到,如果我们使用原生的 InheritableThreadLocal类则在子线程中修改变量,可能会影响到父线程的变量值,及其他子线程的值。该类扩展了 ThreadLocal,为子线程提供从父线程那里继承的值:在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的;但是,ThreadLocal有一个问题,就是它只保证在同一个线程间共享变量,也就是说如果这个线程起了一个新线程,那么新线程是不会得到父线程的变量信息的。

swimming_in_IT_的博客 5067

微服务框架TraceId方案

MDC(Mapped Diagnostic Context,映射诊断上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的机制。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容(只有子线程创建的时候初始化一次并不适用于线程池)。在项目中随着项目的业务越来越复杂,以及项目的微服务化等,导致平常的项目中出现如:同一次操作日志散乱等现象,对通过查询日志的方式排查问题造成极大困扰。因此迫切需要一种可以追溯当前链路操作日志的手段。

雨后是冰雹 4460

通过MDC+traceId实现全局请求追踪并快速定位日志

需要在大量日志堆栈中快速定位到某次请求或者方法调用的日志信息。

wuhao2343的博客 2108

别再让C盘爆红了!手把手教你将Anaconda虚拟环境迁移到其他盘(附权限问题解决)

本文详细指导如何将Anaconda虚拟环境从C盘迁移到其他驱动器,解决C盘空间不足问题。通过conda config命令和直接修改.condarc配置文件两种方法,确保环境迁移顺利进行,同时提供权限问题解决方案和长期维护技巧,帮助用户高效管理虚拟环境。

weixin_42525289的博客 423

Log4j实现全局日志traceId详解

【摘要】分布式系统中实现全局traceId的完整方案:通过MDC机制在请求入口生成唯一traceId(如UUID),并配置log4j2日志模板输出%X{traceId}。需注意三点核心实现:1)Filter拦截请求设置/清除ThreadContext;2)服务间通过HTTP头传递traceId;3)异步场景使用TransmittableThreadLocal传递MDC上下文。该方案能完整追踪请求链路,配合ELK等工具可显著提升排障效率,需特别注意线程复用时的traceId清理。

onlymscn的博客 1321

YOLOv8 ultralytics使用笔记----labelme标记数据,训练模型、追踪目标。

Ultralytics是一个Python库,它是为执行高级计算机视觉任务而创建的,如对象检测、实例分割、图像分类等。Ultralytics为YOLO模型发布了一个全新的存储库,它被构建为用于机器视觉统一框架。这里先利用,并激活。

qq_48050838的博客 1万+

YOLO11改进策略【独家融合改进】| RCSOSA + SPD + WFU

SPD-Conv是一种新的 CNN 构建模块,用于替代传统 CNN 架构中使用的步长卷积(strided convolution)和池化(pooling)层,它由空间到深度(Space-to-depth,SPD)层和非步长卷积(non - strided convolution)层组成。WFU模块通过小波变换的频率分解与跨尺度融合机制,解决了传统上采样过程中的混叠和细节丢失问题,实现了高效、高保真的面部细节重建。

Limiiiing的博客 453

几行代码轻松实现跨系统传递 traceId,再也不用担心对不上日志了!

前言 新项目查日志太麻烦,多台机器之间查来查去,还不知道是不是同一个请求的。打印日志时使用 MDC 在日志上添加一个 traceId,那这个 traceId 如何跨系统传递呢? 公众号:liuzhihangs,记录工作学习中的技术、开发及源码笔记;时不时分享一些生活中的见闻感悟。欢迎大佬来指导! 背景 同样是新项目开发的笔记,因为使用的是分布式架构,涉及到各个系统之间的交互 这时候就会遇到一个很常见的问题: 单个系统是集群部署,日志分布在多台服务器上; 多个系统的日志在多台机器,但是一次请求,查.

程序员小航 6121

K8s(二十一):在 Kubernetes 集群中部署 MySQL8.0 高可用集群(1主2从)

🔴 K8s(二十一):在 Kubernetes 集群中部署 MySQL8.0 高可用集群(1主2从)

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 3万+

SpringBoot增加全局traceId日志追踪

*** @description requst包装类,构建可重复读取inputStream的request*/@Override@Override@Override@Override@Override@Override@Override/*** @Description 全局请求增加traceId日志追踪,保存到ThreadLocal的MDC中*/@Override@Overridetry {if(!}else {

Lenjor 的博客 1261

snmp MIB详解

每个 MIB 对象都由一个对象标识符(OID)和对象类型组成。OID 是一个由多个数字组成的路径(类似于 IP 地址),它标识了 MIB 中对象的层次结构。

dlz0836的博客 3724

工业级向量检索核心技术:IVF-PQ原理与全流程解析

在大规模高维向量检索场景中,IVF-PQ(倒排索引结合乘积量化)凭借“粗筛范围+细算距离”的双重加速策略,成为核心解决方案。本文将深入解析其原理与全流程,助力开发者掌握这一关键技术。

进一步有进一步的欢喜~ 1199

SpringBoot中使用TraceId进行日志追踪

用于标识某一次具体的请求ID。当用户的请求进入系统后,会在RPC调用网络的第一层生成一个全局唯一的traceId,并且会随着每一层的RPC调用,不断往后传递,这样的话通过traceId就可以把一次用户请求在系统中调用的路径串联起来。在分布式系统中,一个请求可能会涉及多个服务和组件的调用,而traceId可以帮助我们追踪和查看整个请求的流程和调用链。

冷脸萌萌的技术实践 1938

点云语义分割:使用Cylinder3D训练SemanticKITTI数据集

将该路径修改到config/semanticktti.yaml文件中的data_path节点下。下载semanticKITTI数据集的点云数据和标签,解压到一起。系统:Ubuntu18。

zhaoguanhua的博客 2604

【日志革新】在ThinkPHP5中实现高效TraceId集成,打造可靠的日志追踪系统

相比之下,如果将 traceId 存储在请求体的 header 中,获取代码则需要写成 (Request::instance()->header())[‘traceId’]?在每次请求的响应中都添加 traceId 到响应头中,以便客户端收到响应后可以通过 traceId 与请求对应起来。对于异步请求,需要在发送请求时将 traceId 包含在请求头中,以便日志也能够与对应的原始请求进行关联。在异步请求中,确保在发送请求时将 traceId 包含在请求头中。,以便在日志中准确追踪请求的全链路过程。

Mason Hu 1602

05、CAN 总线拓扑结构详解,线型拓扑搭建要点

CAN总线拓扑与信号完整性优化 本文通过工程实践经验,深入剖析了CAN总线系统中信号完整性的关键要素。主要内容包括: 终端电阻的核心作用:必须严格在总线两端配置120Ω电阻,形成60Ω等效阻抗以匹配双绞线特性阻抗,避免信号反射导致的波形畸变。 阻抗匹配原理:解释了信号能量管理机制,指出阻抗不连续会导致信号反射,并通过示波器波形特征(如振铃现象)判断阻抗失配问题。 信号完整性多维要求:强调信号质量不仅涉及波形存在与否,还包括边沿斜率、共模电压范围等关键参数,这些直接影响通信可靠性。 工程实践建议:提供了终端电

2401_88858637的博客 180

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

链路日志traceId

  项目查日志太麻烦,多台机器之间查来查去,还不知道是不是同一个请求的。打印日志时使用 MDC 在日志上添加一个 traceId,使用 traceId 跨系统传递   1 背景 同样是新项目开发的笔记,因为使用的是分布式架构,涉及到各个系统之间的交互       这时候就会遇到一个很常见的问题:   单个系统是集群部署,日志分...

t194978的博客 6438

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

python - FastApi 在日志中如何实现全局打印 traceId demo

本文介绍了在FastAPI中实现全局打印traceId的方法。通过自定义中间件RequestLogMiddleware,实现请求级隔离的traceId管理:从前端请求头获取或自动生成traceId,并注入到日志记录中。相比Java的ThreadLocal线程隔离,FastAPI的请求隔离机制更适用于异步场景。核心代码展示了如何创建带traceId的日志记录器,并通过中间件在请求处理前后记录日志,同时在响应头返回traceId以便问题排查。该方法利用了FastAPI的请求实例隔离特性,确保在多线程和异步环境下

杨杨杨~~的博客 604

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: 下载超过 28762W 次的 Java面试题库(附答案)
下一篇: 深入分析JVM执行引擎
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值