Spring Boot单体应用引入sleuth链路追踪

news2024/12/26 23:19:53

文章目录

  • 前言
  • 一、问题模拟
  • 二、引入sleuth链路跟踪
    • 1、引入sleuth的maven依赖
    • 2、添加属性配置
    • 3、logback配置
    • 4、日志信息
    • 5、通过@NewSpan注解声明新的Span
  • 三、引入Sleuth链路跟踪的好处
  • 四、Sleuth概念说明
  • 五、Logback的MDC特性


前言

最近排查生产环境的异常时发现一个问题,虽然找到了请求入参的日志信息,但是由于生产环境的请求并发量比较大,无法确定后续执行过程中打印的日志以及返回信息和入参的对应关系,无法理清请求的完整链路信息,给定位问题带来了很大的挑战。所以不止在微服务架构,单体应用下也强烈推荐引入sleuth链路跟踪。


一、问题模拟

定义日志切面,采用环绕通知记录请求入参和返回结果。

/**
 * Web层日志切面
 */
@Aspect
@Component
@Slf4j
public class WebLogAspect {
    /**
     * 定义切面
     */
    @Pointcut("execution(public * com.laowan.limit.controller..*.*(..))")
    public void webLog(){
    }
    @Around(value = "webLog()")
    public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = StopWatch.createStarted();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String params = this.getRequestParams(request,joinPoint);
        log.info("【请求信息】请求url:{},参数:{} ", request.getServletPath(), params);
        //继续下一个通知或目标方法调用,返回处理结果,如果目标方法发生异常,则 proceed 会抛异常.
        //如果在调用目标方法或者下一个切面通知前抛出异常,则不会再继续往后走.
        Object proceed = joinPoint.proceed(joinPoint.getArgs());

        stopWatch.stop();
        long watchTime = stopWatch.getTime();
        log.info("【响应结果】返回值={},耗时={} (毫秒)", proceed, watchTime);
        return proceed;
    }

    /**
     * 异常通知:目标方法发生异常的时候执行以下代码,此时返回通知@AfterReturning不会再触发
     * @param jp
     * @param ex
     */
    @AfterThrowing(pointcut = "webLog()", throwing = "ex")
    public void aspectAfterThrowing(JoinPoint jp, Exception ex) {
        String methodName = jp.getSignature().getName();
        log.error("【后置异常通知】{}方法异常:{}" ,methodName, ex.getMessage());
    }


    /**
     * 获取请求参数
     * @param request
     * @param joinPoint
     */
    private String getRequestParams(HttpServletRequest request, JoinPoint joinPoint) throws JsonProcessingException {
        StringBuilder params = new StringBuilder();
        // 获取 request parameter 中的参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap != null && !parameterMap.isEmpty()) {
            for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                params.append(entry.getKey()).append(" = ").append(Arrays.toString(entry.getValue())).append(";");
            }
        }
        // 获取非 request parameter 中的参数
        Object[] objects = joinPoint.getArgs();
        for (Object arg : objects) {
            if (arg == null) {
                break;
            }
            String className = arg.getClass().getName().toLowerCase();
            String contentType = request.getHeader("Content-Type");
            if (contentType != null && contentType.contains("application/json")){
                // json 参数
                ObjectMapper mapper = new ObjectMapper();
                mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
                mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
                params.append(mapper.writeValueAsString(arg));
            }
        }
        return params.toString();
    }
}

单次请求日志信息:
在这里插入图片描述
并发请求下日志信息:
在这里插入图片描述

问题说明:
在并发请求下,请求日志的对应关系只能借助请求的执行线程[nio-8080-exec-*]间接实现关联,但是每个执行线程都会一直处理新的HTTP请求,并且在集群环境下,线程名称很可能还会重复。这种情况下,我们很难根据日志信息理清一个请求的完整执行链路信息。

二、引入sleuth链路跟踪

链路跟踪的实现方案有很多,这里我采用的是spring-cloud-starter-sleuth
Sleuth可以单独使用,也可以整合Zipkin使用。这里我只需要生成日志的链路信息,所以单独使用Sleuth。

官网信息:Only Sleuth (log correlation)

1、引入sleuth的maven依赖

<parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>2.4.2</version>
     <relativePath/> <!-- lookup parent from repository -->
 </parent>

  <dependencies>
       <!--引入sleuth依赖-->
	   <dependency>
	          <groupId>org.springframework.cloud</groupId>
	          <artifactId>spring-cloud-starter-sleuth</artifactId>
	    </dependency>
	     ……
   </dependencies>

  <dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>2020.0.1</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
  </dependencyManagement>    

注意:
这里需要主要spring boot和spring-cloud版本之间的兼容关系。
官网的组件版本对应地址:spring cloud官网集成组件版本对应说明
在这里插入图片描述

2、添加属性配置

# 开启sleuth链路跟踪,默认为true,所以可以省略配置
spring.sleuth.enabled = true

# 配置服务名称
spring.application.name=Sleuth

3、logback配置

关于sleuth链路跟踪下logback的日志配置信息,可以参考官网的demo:
Logback配置:logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <!-- Example for logging into the build folder of your project -->
    <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>

    <!-- You can override this to have a custom pattern -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%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}"/>

    <!-- Appender to log to console -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!-- Minimum logging level to be presented in the console logs-->
            <level>DEBUG</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- Appender to log to file -->
    <appender name="flatfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
           <!-- 注意:这里不用使用控制台格式${CONSOLE_LOG_PATTERN}输出 -->
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="flatfile"/>
    </root>
</configuration>

注意:
1、文本日志输出flatfile不要采用彩色日志输出,不然在生成的日志文件中查看会看到很多的乱码信息。
2、如果要进行日志采集,推荐采用logstash将日志以json格式输出,方便字段解析。

4、日志信息

在这里插入图片描述

日志的格式为:[application name, traceId, spanId]

  • application name — 应用的名称,也就是application.properties中spring.application.name参数配置的属性。
  • traceId — 为一个请求分配的ID号,用来标识一条请求链路。
  • spanId — 表示一个基本的工作单元,一个请求可以包含多个步骤,每个步骤都拥有自己的spanId。一个请求只会有一个TraceId,但可以有多个SpanId。

5、通过@NewSpan注解声明新的Span

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @NewSpan
    @Override
    public void order(Order order) throws Exception {
        Thread.sleep(200);
        log.info("下单");
    }
}

在这里插入图片描述
注意:在同一个类中@NewSpan不生效。

三、引入Sleuth链路跟踪的好处

1、通过日志信息中的traceId,可以轻松的找出单次请求的所有相关日志信息,理清整个请求的完整链路
2、通过日志信息中的traceId、spanId和日志输出时间信息,可以得出整个请求以及各个阶段span的耗时情况

四、Sleuth概念说明

Spring Cloud Sleuth采用的是Google的开源项目Dapper的专业术语。

● Span:基本工作单元,例如,在一个新建的span中发送一个RPC等同于发送一个回应请求给RPC,span通过一个64位ID唯一标识,trace以另一个64位ID表示,span还有其他数据信息,比如摘要、时间戳事件、关键值注释(tags)、span的ID、以及进度ID(通常是IP地址)
span在不断的启动和停止,同时记录了时间信息,当你创建了一个span,你必须在未来的某个时刻停止它。
● Trace:一系列spans组成的一个树状结构,例如,如果你正在跑一个分布式大数据工程,你可能需要创建一个trace。
● Annotation:用来及时记录一个事件的存在,一些核心annotations用来定义一个请求的开始和结束

○ cs - Client Sent -客户端发起一个请求,这个annotion描述了这个span的开始
○ sr - Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络延迟
○ ss - Server Sent -注解表明请求处理的完成(当请求返回客户端),如果ss减去sr时间戳便可得到服务端需要的处理请求时间
○ cr - Client Received -表明span的结束,客户端成功接收到服务端的回复,如果cr减去cs时间戳便可得到客户端从服务端获取回复的所有所需时间

在这里插入图片描述

五、Logback的MDC特性

Logback的MDC特性,即Mapped Diagnostic Contexts (诊断上下文映射)

Logback的设计目标之一是审计和调试复杂的分布式应用程序。大多数实际的分布式系统需要同时处理来自多个客户端的请求。为了区分开每个客户端的日志,也为了能够快速定位某个请求日志来自哪个客户端,最简单地方式是,给每个客户端的每个日志请求一个唯一标记。为了对每个请求进行惟一的标记,用户将上下文信息放入MDC中。

MDC类仅包含静态方法,它使开发人员可以将信息放置在诊断上下文中,这些环境可以随后通过某些记录组件检索。MDC根据每个线程管理上下文信息。通常,在开始为新客户请求服务时,开发人员将插入相关的上下文信息,例如客户端ID,客户端的IP地址,请求参数等。LOGBACK组件(如果适当配置)将自动在每个日志条目中包含此信息。

package org.slf4j;

public class MDC {
  //Put a context value as identified by key
  //into the current thread's context map.
  public static void put(String key, String val);

  //Get the context identified by the key parameter.
  public static String get(String key);

  //Remove the context identified by the key parameter.
  public static void remove(String key);

  //Clear all entries in the MDC.
  public static void clear();
}

所以sleuth负责生成traceId和spanId信息,并将信息传入到Logback的诊断映射上下文(DMC)中,然后由Logback根据具体的日志格式进行日志输出。


Only Sleuth (log correlation)
logback的MDC特性
Spring-Cloud-Sleuth介绍
spring cloud官网集成组件版本对应说明

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

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

相关文章

Unreal 5 实现丧尸追逐攻击功能

要实现让丧尸能够智能的追逐玩家&#xff0c;我们需要用到ue封装的ai行为树来实现。基础相关的请查看&#xff1a;Unreal Engine 5.1 AI行为树基础入门&#xff0c;来学习一下如何使用ai行为树来实现一个简单的追逐功能。这一篇就是基于这个基础上进行了优化&#xff0c;实现了…

Docker专题系列之十一:基于Docker安装配置Nacos

一、查找镜像 使用如下命令查找镜像库中的资源 docker search nacos二、拉取镜像 拉取最新版本的nacos镜像 docker pull nacos/nacos-server三、查看镜像文件 docker images四、创建本地数据卷目录 mkdir /usr/rdc mkdir /usr/rdc/nacos mkdir /usr/rdc/nacos/logs五、创…

qt_多人聊天

服务器 .pro QT core gui network widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器头文件 #include <QTcpSocket> //客户端头文件 #include <QList> //客户端容器链表 #in…

【SpringMVC】SSM整合

1&#xff0c;SSM整合 前面我们已经把Mybatis、Spring和SpringMVC三个框架进行了学习&#xff0c;今天主要的内容就是把这三个框架整合在一起完成我们的业务功能开发&#xff0c;具体如何来整合&#xff0c;我们一步步来学习。 1. 流程分析 (1) 创建工程 创建一个Maven的web…

【IE】浏览器兼容问题IE

IE11浏览器 1.安装npm i babel-polyfill -S 2.main.js引入&#xff1a; import “babel-polyfill” 3.build/webpack.base.conf.js 更改 : entry: { app: [‘babel-polyfill’, ‘./src/main.js’] } 以上操作之后IE11依旧空白&#xff0c;报错 缺少标识符 axios版本V1.1.3 …

手把手教你搭建一个vue3+ts项目(超祥/必看)

目录 一、创建vite项目 二、启动vite项目 三、处理一些配置问题 四、增加工程化插件 1、安装sass 2、安装vue-router 3、安装pinia 4、安装element-plus 5、安装axios 6、设置路径别名&#xff0c;将相对路径改为绝对路径 一、创建vite项目 1、在一个文件夹下通…

React基础教程(二):React的基本使用

React基础教程(二)&#xff1a;React的基本使用 1、HelloReact 1.1 引入react基础依赖包 注意点&#xff1a;①必须要在②之前引入 <!-- 引入react核心库--><script src"../js/react.development.js"></script><!-- 引入react-dom&…

毕业论文里引用文献率为0%的解决小技巧

快要毕业了&#xff0c;带着伤感&#xff0c;大学生们已开始加紧完成论文的编写和毕业答辩&#xff0c;祝大家都可以顺利拿到学位证书和毕业证书。 前两天&#xff0c;有位同学找我&#xff0c;咨询如何解决毕业论文里&#xff0c;引用文献率为0%的问题。 我在知网上将该论文通…

flutter创建、安装扩展包、打包apk

1、创建APK项目 要在VSCode中创建一个Flutter应用程序&#xff0c;请按照以下步骤进行操作&#xff1a; 安装Flutter SDK&#xff1a;请确保你已经安装了Flutter SDK&#xff0c;并配置了Flutter的环境。你可以在Flutter的官方网站上找到安装和设置Flutter的详细说明。 安装V…

完美解决 RabbitMQ 可视化界面中 Overview 不显示图形的问题

&#x1f4a7; 记录一下今天遇到的 b u g \color{#FF1493}{记录一下今天遇到的bug} 记录一下今天遇到的bug&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 数据结构与算法…

VBA基础(宏编程)

VBA介绍&#xff1a; Visual Basic for Applications&#xff08;VBA&#xff09;是 VisualBasic 的一种宏语言&#xff0c;是微软开发出来在其桌面应用程序中执行通用的自动化(OLE)任务的编程语言。主要能用来扩展 Windows 的应用程序功能&#xff0c;特别是Microsoft Office…

DolphinDB +机器学习,预测地震波形数据

1. 地震波形数据预测业务场景说明 在地震波形数据异常检测场景中&#xff0c;通常需要使用多种工具和方法来提高检测精度和鲁棒性。其中&#xff0c;FilterPicker 是一种常用的基于模板匹配的异常检测工具&#xff0c;可以实现地震波形数据的实时异常检测和定位。FilterPicker…

为什么看了那么多测试技术帖,自己都没有提升?

作为测试新手&#xff0c;最爱莫过于看各大牛发的技术贴&#xff0c;这篇很牛叉&#xff0c;那篇也很有道理&#xff0c;似乎自己看着看着也会成为高手。然而几年后&#xff0c;发现自己对专业知识的理解乱的很&#xff0c;里面更有很多自相矛盾的地方&#xff0c;这到底是哪里…

RedisSon高并发分布式锁实战

Redis高并发分布式锁实战 1.分布式场景下的synchronized失效的问题–用redis实现分布式锁 synchronized是通过monitor实现的jvm级别的锁&#xff0c;如果是分布式系统&#xff0c;跑在不同的虚拟机上的tomcat上&#xff0c;会导致synchronized无法锁住对象 ----------- 需要分…

01分数规划 易懂+例题讲解 (c++)

01分数规划 &#xff1a;01即取还是不取&#xff0c;分数即所求型式为&#xff0c;规划就是选取最好的方案。 一般情况题目给出n个物品&#xff0c;再给出每个物品的价值以及物重&#xff0c;选取k个物品&#xff0c;问你在所有可能选取的方案中&#xff0c;最大的单位价值为多…

通过零代码ETLCloud实现马帮ERP数据自动化同步

马帮ERP介绍 马帮ERP是一款云端跨境电商管理软件。与传统的ERP系统不同&#xff0c;马帮ERP专注于跨境电商领域&#xff0c;为电商企业提供一站式管理解决方案&#xff0c;包括财务管理、采购管理、进销存管理、订单管理等功能模块。该平台针对跨境电商行业特点&#xff0c;提…

MM32F3273G8P火龙果开发板MindSDK开发教程20 - freertos + letter shell 的移植

MM32F3273G8P火龙果开发板MindSDK开发教程20 - freertos letter shell 的移植 1、freertos下载 官网传送门 2、freertos移植 1、在工程目录device下新建freertos目录&#xff0c;将下载的源码source目录下的七个.c文件copy到新建的freertos目录。 2、将source/protable/G…

Word 2021入门指南:详细解读常用功能

软件安装&#xff1a;办公神器office2021安装教程&#xff0c;让你快速上手_正经人_____的博客-CSDN博客 一、 新建文档 打开Word 2021后&#xff0c;可以看到左上角的“文件”选项&#xff0c;点击它&#xff0c;在弹出的菜单中选择“新建”选项。然后可以选择空白文档或者使…

vue3+ts+vite+element plus中使用luckysheet(预览效果)

前言&#xff1a; 这两天一个项目&#xff0c;需要在页面中以excel的形式展示大量数据&#xff0c;喜欢偷懒的我果断扒拉了一堆适用于vue3的插件&#xff0c;下面简单说说我使用的luckysheet 使用&#xff1a; 一、准备一个vue3tsviteelement plus的项目 此处省略n个字。。。…

如何用 WampServer+快解析 搭建php文件管理器

基于网络&#xff0c;资源是大家最最基本的需求&#xff0c;许多网络爱好者不求利益&#xff0c;把自己收集的一些通过一些平台共享给大家&#xff0c;这就是资源共享。 资源共享程度越高&#xff0c;代表信息发展水平越高。现实工作中&#xff0c;由于用户提供的数据可能来自…