官方地址: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  即可查看

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

链路追踪之Jaeger_mysql

 

 

 

参考: