Springboot实现链路追踪功能

前言

在日常开发中,一个业务的实现往往会调用很多个方法,当我们去看日志的时候,各种接口的日志打印出来,看着就头疼,压根没办法去定位,而链路追踪就能很好的帮助我们去查看接口从头至尾依次调用了哪些方法。

同时我们还可以通过链路追踪的traceId去日志文件中去定位问题,比如:

grep   'c03ed014b9b34252b872009d030904f7'    log.log
或者
# 查看上下5行
grep -5   'c03ed014b9b34252b872009d030904f7'    log.log

如图所示:
在这里插入图片描述

实现代码

本案例实现是通过springboot默认日志logback实现。

第一步:导入Maven依赖

    <!--lombok配置-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>

第二步:添加整合logback,打印日志,logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
    <contextName>logback</contextName>
    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
    <springProperty scope="context" name="log.path" source="log.path"/>
 <!-- 彩色日志 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="[traceId:%X{traceId}] ${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/> 	
    <!--输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 滚动文件输出日志 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/log.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
    </appender>
 
    <root level="INFO">
        <!-- 配置日志输出的方式,可以同时输出到控制台和文件 -->
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

第三步:application.yml中需要指定logback-spring.xml

logging:
  config: classpath:logback-config.xml

第四步:自定义日志拦截器 LogInterceptor
作用:每一次接口调用时,从线程维度上添加链路ID–traceId。


import com.alibaba.druid.util.StringUtils;
import com.dinglianshuke.ipfs.service.common.utils.MDCTraceUtil;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @author o
 * @description 自定义日志拦截器
 * @date 2024/4/11
 *
 */
public class LogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 客户端可以传入链路ID,需要唯一性
        String traceId = request.getHeader(MDCTraceUtil.TRACE_ID_HEADER);
        if (!StringUtils.isEmpty(traceId)) {
            MDCTraceUtil.putTrace(request.getHeader(MDCTraceUtil.TRACE_ID_HEADER));
        } else {
            MDCTraceUtil.addTrace();
        }
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        MDCTraceUtil.removeTrace();
    }
}

这边将MDC提取出来做成了一个工具类MDCTraceUtil:

import org.slf4j.MDC;
import java.util.UUID;

public class MDCTraceUtil {
    /**
     * 追踪id的名称
     */
    public static final String KEY_TRACE_ID = "traceId";

    /**
     * 日志链路追踪id信息头
     */
    public static final String TRACE_ID_HEADER = "x-traceId-header";


    /**
     * 创建traceId并赋值MDC
     */
    public static void addTrace() {
        String traceId = createTraceId();
        // MDC(Mapped Diagnostic Context)诊断上下文映射,是@Slf4j提供的一个支持动态打印日志信息的工具。
        MDC.put(KEY_TRACE_ID, traceId);
    }

    /**
     * 赋值MDC
     */
    public static void putTrace(String traceId) {
        MDC.put(KEY_TRACE_ID, traceId);
    }

    /**
     * 获取MDC中的traceId值
     */
    public static String getTraceId() {
        return MDC.get(KEY_TRACE_ID);
    }

    /**
     * 清除MDC的值
     */
    public static void removeTrace() {
        MDC.remove(KEY_TRACE_ID);
    }

    /**
     * 创建traceId
     */
    public static String createTraceId() {
        return UUID.randomUUID().toString().replace("-", "");
    }

}

第五步:新增LogWebMvcConfig,添加拦截器

import com.dinglianshuke.ipfs.service.common.interceptor.LogInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class LogWebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器
        registry.addInterceptor(mvcInterceptor())
                // 拦截所有请求的路径
                .addPathPatterns("/**");
    }

    @Bean
    public LogInterceptor mvcInterceptor() {
        return new LogInterceptor();
    }

}

之后就是编写接口,启动测试即可。
在这里插入图片描述

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

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

相关文章

UE5 在骨骼动画模型上绘制贴图

参考&#xff1a;Unreal 5.1 - How to paint damage textures and other effects on skeletal meshes 针对模型&#xff0c;在运行状态下通过射线指定一定范围&#xff0c;添加材质效果。 核心思路 通过射线获取命中点&#xff0c;作为材质参数材质中&#xff0c;命中的世界…

护眼台灯品牌哪个好?2024五大护眼台灯排行榜分享

​护眼台灯作为家庭中常见的照明工具&#xff0c;其存在几乎成为了现代生活的标配。家长们往往会为孩子购置一台&#xff0c;供学习和阅读使用&#xff1b;同时&#xff0c;它也是学生和办公人员在夜晚工作学习的必备之物。然而&#xff0c;市面上的一些普通台灯可能存在着种种…

【XR806开发板试用】使用硬件SPI驱动TFT液晶屏显示图片

【开发背景】 在完成开发板呼吸灯效果后&#xff08;【XR806开发板试用】使用PWM模块模拟手机呼吸灯提示功能&#xff09;&#xff0c;考虑到显示界面过于单一&#xff0c;如果想要呈现更多的信息就很困难了&#xff0c;刚好之前买过一个TFT液晶屏&#xff0c;正在某个角落吃灰…

OV证书——提升企业在线身份信誉

简介 在当今的数字化时代&#xff0c;网络安全与用户信任成为企业线上运营的基石&#xff0c;而SSL/TLS证书则是确保网站数据传输安全、提升网站信誉度的关键工具之一。其中&#xff0c;组织验证&#xff08;OV&#xff09;证书作为一种特殊类型的SSL证书&#xff0c;通过深入…

Vivado抓信号——提高效率的工具化生成XDC(Python脚本)

操作目录 一、要抓取信号的txt列表二、操作流程 通常情况下&#xff0c;Vivado上板抓取信号的方法主要有两类&#xff1a; &#xff08;1&#xff09;通过在信号前添加(mark_debug“true”)&#xff0c;综合完之后点击Set Up Debug&#xff0c;将需要抓取的信号添加进去&#x…

linux学习:文件类型、文件操作、系统IO、内存映射

目录 文件类别 文件操作 系统 IO 头文件 打开文件 关闭文件 文件描述符 读写 例子 拷贝文件 偏移量 其他接口 mmap()映射 文件类别 普通文件&#xff08;regular&#xff09;&#xff1a;存在于外部存储器中&#xff0c;用于存储普通数据。目录文件&#xff08;d…

蓝桥杯,,,,,,

辗转相除求最大公约数 #include<iostream> using namespace std;int gcd(int a, int b)//求最大公约数&#xff0c;如果返回值为1&#xff0c;最大公约数只有1&#xff0c;为所求 {return b ? gcd(b, a % b) : a; } int main() {int count 0;for(int i1;i<2020;i)f…

进口PFA容量瓶高纯透明聚四氟乙烯材质耐强酸碱PFA定容瓶

PFA容量瓶&#xff0c;也叫特氟龙容量瓶&#xff0c;是用于配制标准浓度溶液的实验室器皿&#xff0c;是有着细长颈、梨形肚的耐强腐蚀平底塑料瓶&#xff0c;颈上有标线&#xff0c;可直接配置标准溶液和准确稀释溶液以及制备样品溶液。 因其有着不易碎、材质纯净、化学稳定性…

Unity Android后处理AO报错

整体流程&#xff1a; 1.添加AO效果 2.Mode 选择 Multi-scale Volumetric Occlusion 3.保证Project Settings - Player - Other Settings - Rendering - Graphic API 内包含 Vulkan 原因&#xff1a; 1.Post Processing文档&#xff1a;https://docs.unity3d.com/Packages/…

探索点云与KD-Tree配对的方法

比较点云是处理和分析点云数据的关键步骤。然而,由于各个扫描之间固有的差异,无法进行逐点比较。因此,点云分析的第一步也是主要步骤是将点配对以进行有意义的比较。 配对点是区分表面变形和运动分析的关键任务。这个过程不仅为变形分析提供了见解,还使我们能够通过比较不…

如何用 Readwise Reader 定制提示词 AI 自动辅助处理信息?

抵御「信息过载」&#xff0c;你需要这样的利器。 痛点 知识工作者的痛点是非常明显的——如果你是一名老师、学生&#xff0c;或是平时需要跟许多资料打交道的人&#xff0c;想必你会经历过信息过载。 信息过载有时候不仅是数量问题&#xff0c;还是一个类型问题。很多不同的信…

【话题】AI技术创业有那些机会,简单探讨下

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 背景机会一、引言二、AI技术的创业机遇1.智能服务行业的兴起2.数据驱动的业务模式创新3.AI与产业融合的创新发展 三、AI技术创业的挑战1.技术门槛高2.法规政策的不确定性…

奎芯科技:智能时代的芯片上游企业如何突破?

半导体IP&#xff08;Intellectual Property&#xff0c;知识产权&#xff09;&#xff0c;通常也称作IP核&#xff08;IP core&#xff09;&#xff0c;指芯片设计中预先设计、验证好的功能模块&#xff0c;主要服务于芯片设计&#xff0c;因部分通用功能模块在芯片中被反复使…

03-JAVA设计模式-享元模式

享元模式 什么是享元模式 享元模式&#xff08;Flyweight Pattern&#xff09;是一种对象结构型设计模式&#xff0c;用于减少创建对象的数量&#xff0c;以减少内存占用和提高系统性能。它通过共享已经存在的对象来避免创建大量相似的对象&#xff0c;从而降低内存消耗。 在…

SAP 计划策略82简介

前面的文章中我们已经测试了很多才策略,10、11、40、50、70、60、63 80策略。 本文将重点说明ATO模式下82策略的使用场景,计划策略82是SAP提供的另一种基于按单生产思想的计划策略,由客户的需求来直接驱动直接生产,是一个按单生产的场景。 1、首先我们先看下系统后台82策略…

为什么都在做白银投资?略谈现货白银的投资优势

在各种主要的投资产品中&#xff0c;现货白银可以说是比较具有优势的一种。近期现货白银价格不断攀升&#xff0c;已经突破了28.00大关&#xff0c;这更是引起了很多朋友对现货白银投资的兴趣。下面我们就来讨论一下&#xff0c;现货白银的投资优势有哪些。 交易灵活。投资现货…

嵌入式岗位“面试失败”的宝贵经验分享

面试失败&#xff0c;在所难免。从中汲取教训和经验才最关键。面试并不只是用人单位挑选应聘者&#xff0c;同样也是应聘者从面试中获取相关工作信息的好渠道。 1.每面完一次试&#xff0c;认真回顾整个面试 很多人面试一出来&#xff0c;就像考完一场试一样&#xff0c;把…

专访安霸CEO王奉民:怎么帮助OEM在智驾上和特斯拉竞争

采访| 德新 撰文| 苗岭 整个2023年&#xff0c;安霸CEO王奉民两次到访中国&#xff0c;一次是参加上海车展&#xff1b;另一次&#xff0c;他拜访了所有能约上的主机厂及Tier 1客户。 王奉民积极地出现在国内&#xff0c;跟安霸当前押注汽车领域有关。 这家成立于2004年的芯…

鸿蒙内核源码分析 (物理内存篇) | 怎么管理物理内存

如何初始化物理内存&#xff1f; 鸿蒙内核物理内存采用了段页式管理&#xff0c;先看两个主要结构体。结构体的每个成员变量的含义都已经注解出来&#xff0c;请结合源码理解. #define VM_LIST_ORDER_MAX 9 //伙伴算法分组数量&#xff0c;从 2^0&#xff0c;2^1&#…