首页 > 其他分享 >链路追踪之Jaeger

链路追踪之Jaeger

时间:2023-09-28 11:36:57浏览次数:38  
标签:span err Jaeger Register db opentracing 链路 gorm 追踪

官方地址:https://www.jaegertracing.io/

 

[安装]

官方提供了两个安装方式, 

1. 基于二进制(https://www.jaegertracing.io/download/#binaries)

2.使用docker 

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.49

 

 

我是在windows下使用下载的二进制文件启动服务

注: 需要再终端中执行jaeger-all-in-one.exe即可启动服务

 

 

[golang接入]

实现gorm的CRUD的链路记录

 Demo文件:

gormTracing.go

package gorm_trace

import (
    "github.com/opentracing/opentracing-go"
    tracerLog "github.com/opentracing/opentracing-go/log"
    "gorm.io/gorm"
)

const (
    gormSpanKey        = "__gorm_span"
    callBackBeforeName = "opentracing:before"
    callBackAfterName  = "opentracing:after"
)

func before(db *gorm.DB) {
    // 先从父级spans生成子span
    span, _ := opentracing.StartSpanFromContext(db.Statement.Context, "gorm")
    // 利用db实例去传递span
    db.InstanceSet(gormSpanKey, span)
    return
}

func after(db *gorm.DB) {
    // 从GORM的DB实例中取出span
    _span, isExist := db.InstanceGet(gormSpanKey)
    if !isExist {
        return
    }

    // 断言进行类型转换
    span, ok := _span.(opentracing.Span)
    if !ok {
        return
    }
    defer span.Finish()

    // Error
    if db.Error != nil {
        span.LogFields(tracerLog.Error(db.Error))
    }

    // sql
    span.LogFields(tracerLog.String("sql", db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)))
    return
}

type OpentracingPlugin struct{}

func (op *OpentracingPlugin) Name() string {
    return "opentracingPlugin"
}

func (op *OpentracingPlugin) Initialize(db *gorm.DB) (err error) {
    // 开始前
    db.Callback().Create().Before("gorm:before_create").Register(callBackBeforeName, before)
    db.Callback().Query().Before("gorm:query").Register(callBackBeforeName, before)
    db.Callback().Delete().Before("gorm:before_delete").Register(callBackBeforeName, before)
    db.Callback().Update().Before("gorm:setup_reflect_value").Register(callBackBeforeName, before)
    db.Callback().Row().Before("gorm:row").Register(callBackBeforeName, before)
    db.Callback().Raw().Before("gorm:raw").Register(callBackBeforeName, before)

    // 结束后
    db.Callback().Create().After("gorm:after_create").Register(callBackAfterName, after)
    db.Callback().Query().After("gorm:after_query").Register(callBackAfterName, after)
    db.Callback().Delete().After("gorm:after_delete").Register(callBackAfterName, after)
    db.Callback().Update().After("gorm:after_update").Register(callBackAfterName, after)
    db.Callback().Row().After("gorm:row").Register(callBackAfterName, after)
    db.Callback().Raw().After("gorm:raw").Register(callBackAfterName, after)
    return
}

var _ gorm.Plugin = &OpentracingPlugin{}

测试文件:

package gorm_trace

import (
    "context"
    "fmt"
    "github.com/opentracing/opentracing-go"
    "github.com/uber/jaeger-client-go"
    "github.com/uber/jaeger-client-go/config"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "io"
    "math/rand"
    "strconv"
    "sync"
    "testing"
    "time"
)

const dsn = "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"

func initJaeger() (closer io.Closer, err error) {
    // 根据配置初始化Tracer 返回Closer
    tracer, closer, err := (&config.Configuration{
        ServiceName: "gormTracing",
        Disabled:    false,
        Sampler: &config.SamplerConfig{
            Type: jaeger.SamplerTypeConst,
            // param的值在0到1之间,设置为1则将所有的Operation输出到Reporter
            Param: 1,
        },
        Reporter: &config.ReporterConfig{
            LogSpans:           true,
            LocalAgentHostPort: "localhost:6831",
        },
    }).NewTracer()
    if err != nil {
        return
    }

    // 设置全局Tracer - 如果不设置将会导致上下文无法生成正确的Span
    opentracing.SetGlobalTracer(tracer)
    return
}

type Product struct {
    gorm.Model
    Code  string
    Price uint
}

func Test_GormTracing(t *testing.T) {
    closer, err := initJaeger()
    if err != nil {
        t.Fatal(err)
    }
    defer closer.Close()

    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        t.Fatal(err)
    }
    _ = db.Use(&OpentracingPlugin{})

    // 迁移 schema
    _ = db.AutoMigrate(&Product{})

    // 生成新的Span - 注意将span结束掉,不然无法发送对应的结果
    span := opentracing.StartSpan("gormTracing unit test")
    defer span.Finish()

    // 把生成的Root Span写入到Context上下文,获取一个子Context
    ctx := opentracing.ContextWithSpan(context.Background(), span)
    session := db.WithContext(ctx)

    // Create
    session.Create(&Product{Code: "D42", Price: 100})

    // Read
    var product Product
    session.First(&product, 1)                 // 根据整形主键查找
    session.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

    // Update - 将 product 的 price 更新为 200
    session.Model(&product).Update("Price", 200)
    // Update - 更新多个字段
    session.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
    session.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

    // Delete - 删除 product
    session.Delete(&product, 1)
}

func Test_GormTracing2(t *testing.T) {
    closer, err := initJaeger()
    if err != nil {
        t.Fatal(err)
    }
    defer closer.Close()

    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        t.Fatal(err)
    }
    _ = db.Use(&OpentracingPlugin{})

    rand.Seed(time.Now().UnixNano())

    //num, wg := 1<<10, &sync.WaitGroup{}
    num, wg := 110, &sync.WaitGroup{}

    wg.Add(num)

    for i := 0; i < num; i++ {
        go func(t int) {
            span := opentracing.StartSpan(fmt.Sprintf("gormTracing unit test %d", t))
            defer span.Finish()

            ctx := opentracing.ContextWithSpan(context.Background(), span)
            session := db.WithContext(ctx)

            p := &Product{Code: strconv.Itoa(t), Price: uint(rand.Intn(1 << 10))}

            session.Create(p)

            session.First(p, p.ID)

            session.Delete(p, p.ID)

            wg.Done()
        }(i)
    }

    wg.Wait()
}

 

 

[使用]

打开浏览器: http://localhost:16686  即可查看

进行筛选,查看上报的追踪信息

 

 

 

参考:

  • https://github.com/avtion/gormTracing

 

标签:span,err,Jaeger,Register,db,opentracing,链路,gorm,追踪
From: https://www.cnblogs.com/xingxia/p/jaeger.html

相关文章

  • 如何实现简单的分布式链路功能?
    为什么需要链路跟踪为什么需要链路跟踪?微服务环境下,服务之间相互调用,可能存在A->B->C->D->C这种复杂的服务交互,那么需要一种方法可以将一次请求链路完整记录下来,否则排查问题不好下手、请求日志也无法完整串起来。如何实现链路跟踪假设我们从用户请求接口开始,每次请求需要有......
  • 流量治理的基石——基于字节码增强的全链路流量标签透传
     作者:李来   华为云高级软件工程师一、全链路流量标签透传在微服务架构中,流量标签用于对流量进行标记和分类,能够在微服务之间实现更精细的路由、负载均衡和流控等流量治理能力。以HTTP报文为例,每一条header都可以是一条流量标签,比如x-sermant-version:v1表示通过Sermant流......
  • 一文读懂分布式追踪的历史发展点滴
    【摘要】本文介绍了可观测生态领域相关的技术——DistributedTracing(分布式追踪)【作者】李杰,专注于Java虚拟机技术、云原生技术领域的探索与研究。什么是“DistributedTracing-分布式追踪”?DistributedTracing(分布式追踪)是一种用于监测和分析分布式应用程序的技术和方法......
  • 静态路由与BFD联动实现快速检测链路状态使路由表快速收敛
     实现目的:1.默认情况,PC1和PC2之间的通信使用AR1-AR9-AR10-AR2链路通信(主链路),当AR1-AR9-AR10-AR2链路中任何一点的链路断开后,路由会以毫秒级速度切换到AR1-AR3-AR2(备链路),实现网络的连通性。2.当主链路恢复后,路由又切回到主线路。 实验前的配置基础:1.配置PC和路由器端口IP;2......
  • 使用mtrace追踪JVM堆外内存泄露
    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,非公众号转载保留此声明。简介在上篇文章中,介绍了使用tcmalloc或jemalloc定位native内存泄露的方法,但使用这个方法相当于更换了原生内存分配器,以至于使用时会有一些顾虑。经过一些摸索,发现glibc自带的ptmalloc2分配器,也提供有追踪内......
  • 16 链路聚合实验
    实验理解链路聚合的作用和配置拓扑此时存在拓扑存在环路,配置STP生成树,STP生成树通过阻塞端口破环,这样两条链路将不会工作,在此场景下有更好的方式解决,链路聚合,将GE0/0/1-3绑定为一根逻辑的链路,在传输流量时,以负载均衡的方式分担给三条链路,因为是逻辑上的一条链路,所以没有环,还......
  • 用于异构无线传感器网络的多聚合器多链路由协议(Matlab代码实现)
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • SpringBoot + MDC 实现全链路调用日志跟踪
    简介:MDC(MappedDiagnosticContext,映射调试上下文)是log4j、logback及log4j2提供的一种方便在多线程条件下记录日志的功能。MDC可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线......
  • 路由器PLC接入和多链路组网的区别?
    路由器PLC接入和多链路组网都是现代网络技术中的重要概念,但它们在实现方式和应用场景上存在明显的区别。路由器PLC接入:路由器PLC接入是一种通过电力线通信(PLC)技术实现的网络接入方式。它将PLC模块与路由器连接,使得电力线不仅能够传输电力,还可以作为数据传输的通道。这种方式利用......
  • 王道408--CN---数据链路层
    一、历年选择题考点二、数据链路层的主要功能数据链路层的最基本的功能是向该层用户提供透明的和可靠的数据传送基本服务。数据链路层是对物理层传输原始比特流的功能的加强,将物理层提供的可能出错的物理连接改造成为逻辑上无差错的数据链路,使之对网络层表现为一无差错的线路......