• 【GO】LGTM_Grafana_gin_trace中间件(3)_代码实操


    最近在尝试用 LGTM 来实现 Go 微服务的可观测性,就顺便整理一下文档。

    Tempo 会分为 4 篇文章:

    1. Tempo 的架构
    2. 官网测试实操跑通
    3. gin 框架发送 trace 数据到 tempo
    4. go-zero 微服务框架使用发送数据到 tempo

    本文就是写一下如何在 gin server 里面加入 trace 的逻辑

    代码地址:lgtm/example/gin-lgtm at main · zxmfke/lgtm (github.com)


    gin server 接入 trace,是比较简单的,只需要封装一个 tracer 的初始化和中间件就可以了。直接看代码。

    Tracer Init

    初始化一个 tracerProvider,并把这个 tracerProvider 设置为全局的 provider。

    func InitTracer(c Config) error {
    	var err error
    
    	err = tracerProvider(c)
    	...
    	otel.SetTracerProvider(tp)
    
    	return nil
    }
    
    func createExporter(c Config) (tracesdk.SpanExporter, error) {
    	switch c.Batcher {
    	case kindZipKin:
    		return zipkin.New(c.Endpoint)
    	case kindOtlphttp:
    		opts := []otlptracehttp.Option{
    			otlptracehttp.WithInsecure(),
    			otlptracehttp.WithEndpoint(c.Endpoint),
    		}
    		return otlptracehttp.New(context.Background(), opts...)
    
    	case kindOtlpgrpc:
    
    		opts := []otlptracegrpc.Option{
    			otlptracegrpc.WithInsecure(),
    			otlptracegrpc.WithEndpoint(c.Endpoint),
    		}
    		return otlptracegrpc.New(context.Background(), opts...)
    
    	default:
    		return nil, fmt.Errorf("unknown exporter: %s", c.Batcher)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    Tracer Middleware

    利用 gin 路由的中间件模式,添加一个 tracer 的中间件,把要 trace 的路由的信息,在请求前记录并在请求结束后发送到 tempo。

    func Trace(ctx *gin.Context) {
    
    	newSpan := Extract(ctx.Request, "trace-middleware")
    
    	defer newSpan.End()
    
    	ctx.Set(HeaderTraceContextKey, newSpan.spanCtx)
    	ctx.Set(HeaderTraceIDKey, newSpan.SpanID())
    
    	newSpan.Inject(ctx.Request)
    
    	ctx.Next()
    
    	newSpan.SetIntTag("http.status_code", ctx.Writer.Status())
    	if ctx.Writer.Status() == http.StatusOK {
    		newSpan.SetStatus(codes.Ok, "well done")
    		return
    	}
    	newSpan.SetStatus(codes.Error, "something goes wrong")
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    Main

    在代码仓库的 main.go 逻辑就是初始化,并把 tracer middleware 加入到 router 里面。

    func main() {
    
    	if err := tracer.InitTracer(tracer.Config{
    		ServiceName: "tracer-demo",
    		Endpoint:    "127.0.0.1:4318",
    		Sampler:     1.0,
    		Batcher:     "otlphttp",
    	}); err != nil {
    		fmt.Println(fmt.Errorf("%s", err.Error()))
    		return
    	}
    
    	r := gin.Default()
    
    	r.Use(tracer.Trace)
    	r.GET("/ping", func(ctx *gin.Context) {
    		ctx.JSON(http.StatusOK, gin.H{
    			"message": "pong",
    		})
    	})
    
    	r.POST("/test", func(ctx *gin.Context) {
    
    		Sub(ctx)
    
    		ctx.JSON(http.StatusOK, gin.H{
    			"code": 0,
    			"msg":  "success",
    		})
    
    	})
    
    	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    请求接口后,可以通过 Grafana Tempo 里面查看

    在这里插入图片描述

  • 相关阅读:
    python基础语法(2)
    redis
    C++插入排序
    从实践的角度谈谈主成分分析
    【学习日志】2022.11.10 C++11常量表达式、用户定义字面量、原生字符串字面值、类的改进、Leetcode疼迅优选50提T1
    重返90年代!火爆INS的AI照片编辑器,荣登App Store AI应用榜首
    [创业-40]:-优秀人与普通人的区别
    WebAssembly入门笔记[2]:利用Memory传递字节数据
    docker启动容器报错
    Eureka快速入门
  • 原文地址:https://blog.csdn.net/weixin_40242845/article/details/133152584