Gin集成jaeger「OpenTracing」实现Trace

564 阅读2分钟

基本概念

Trace标准

OpenTracing是当前主流,其中golang中OpenTracing的实现有jaeger

google下一代可观测标准是OpenTelemetry「整合日志、监控告警、APM」

jaeger 初始化


package jaeger

import (
   "context"
   "fmt"
   "github.com/opentracing/opentracing-go"
   "github.com/opentracing/opentracing-go/ext"
   "github.com/uber/jaeger-client-go"
   "github.com/uber/jaeger-client-go/config"
   "io"
   "net/http"
)

const (
   LocalAgentHostPort = "x:6831"
   TraceIDKey         = "Hermes-Trace-ID"
)

func GetDefaultConfig() *config.Configuration {
   cfg := &config.Configuration{
      Sampler: &config.SamplerConfig{
         Type:  "const",
         Param: 1,
      },
      Reporter: &config.ReporterConfig{
         LogSpans:           true,
         LocalAgentHostPort: LocalAgentHostPort,
      },
   }
   return cfg
}

func init() {
   jaegerConfig := GetDefaultConfig()
   InitJaeger("go-framework-demo", jaegerConfig)
}


/*
*
初始化
*/
func InitJaeger(service string, cfg *config.Configuration) (opentracing.Tracer, io.Closer) {
   cfg.ServiceName = service
   tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
   if err != nil {
      panic(fmt.Sprintf("Error: connot init Jaeger: %v\n", err))
   }
   opentracing.SetGlobalTracer(tracer)
   return tracer, closer
}

func StartSpan(tracer opentracing.Tracer, name string) opentracing.Span {
   //设置顶级span
   span := tracer.StartSpan(name)
   return span
}

func WithSpan(ctx context.Context, name string) (opentracing.Span, context.Context) {
   span, ctx := opentracing.StartSpanFromContext(ctx, name)
   return span, ctx
}

// 透传上下文
func GetCarrier(span opentracing.Span) (opentracing.HTTPHeadersCarrier, error) {
   carrier := opentracing.HTTPHeadersCarrier{}
   err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, carrier)
   if err != nil {
      return nil, err
   }
   return carrier, nil
}

func GetParentSpan(spanName string, traceId string, header http.Header) (opentracing.Span, error) {
   carrier := opentracing.HTTPHeadersCarrier{}
   carrier.Set(TraceIDKey, traceId)
   tracer := opentracing.GlobalTracer()
   wireContext, err := tracer.Extract(
      opentracing.HTTPHeaders,
      opentracing.HTTPHeadersCarrier(header),
   )
   parentSpan := opentracing.StartSpan(
      spanName,
      ext.RPCServerOption(wireContext))
   if err != nil {
      return nil, err
   }
   return parentSpan, err
}

Gin中间件

package middleware

import (
    "github.com/gin-gonic/gin"
    "github.com/opentracing/opentracing-go"
    "x.com/infra/hermes/jaeger"
    j "github.com/uber/jaeger-client-go"
)

var (
    SpanCTX    = "span-ctx"
)

func TraceMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        traceId := c.GetHeader(TraceIDKey)

        var span opentracing.Span
        if traceId != "" {
            var err error
            span, err = jaeger.GetParentSpan(c.FullPath(), traceId, c.Request.Header)
            if err != nil {
                return
            }
        } else {
            span = jaeger.StartSpan(opentracing.GlobalTracer(), c.FullPath())
        }
        defer span.Finish()
        c.Set(SpanCTX, opentracing.ContextWithSpan(c, span))
        
        c.Set(TraceIDKey,span.Context().(j.SpanContext).TraceID().String())
        
        c.Next()
    }
}

routergroup注册中间件

	apiCostV3 := r.Group("/api/cost/v3",middleware.GenCacheQueryKeyMiddleware(),middleware.TraceMiddleware(),)
        
	bill.RegisterCostComparisonRouter(apiCostV3)

Handler处理

func CostComparisonHandlerParameter(c *gin.Context) (*dto.AliyunProductPretaxAmountInCycleRequest, error) {

    ...
    ` 重点 `
    if value, exists := c.Get(SpanCTX); exists {
        ppa.OpenTracingContextWithSpan = value.(context.Context)
    }
    ...
    return &ppa, nil
}

封装创建Span

func CreateChildSpanContext(spanCtxInterface context.Context, spanName string) {

    var spanCtx context.Context

    spanCtx = spanCtxInterface.(context.Context)

    //创建子span
    span, _ := jaeger.WithSpan(spanCtx, spanName)

    defer span.Finish() //结束后调用完成

调用Span创建

注入接口一「子Span」

import (
	rhandler "acmp-service/handler/response"
)
        
func (_c *CostService) GetAYProductPretaxAmountInCycle(ppa *dto.AYProductPretaxAmountInCycleRequest) (any, error) {

	rhandler.CreateChildSpanContext(ppa.OpenTracingContextWithSpan, "GetAYroductPretaxAmountInCycle")

注入接口二「子Span」

确保传递给CreateChildSpanContext类型为

中间件中Key:SpanCTX、Value:context.Context

func Page(overview Res, c *gin.Context) Res {

	span,_ := c.Get(SpanCTX)
	CreateChildSpanContext(span.(context.Context), "Page")
}

Gin返回携带TraceID

返回TraceID


type PageResponse struct {
   Result     int         `json:"result"`
   Message    string      `json:"message"`
   Success    bool        `json:"success"`
   TotalCount int         `json:"totalCount"`
   Data       interface{} `json:"data"`
   PageNum    int         `json:"pageNum"`
   PageSize   int         `json:"pageSize"`
   TraceID    string      `json:"traceID"`
}

func (g *HGin) traceID() string {
    traceID, e := g.C.Get(TraceIDKey)
    if e && traceID != nil {
        return traceID.(string)
    }
    return ""
}

func (g *HGin) PageOK(data any, count int, pageNum int, PageSize int) {
    g.C.JSON(http.StatusOK, PageResponse{
        Result:     http.StatusOK,
        Message:    "success",
        Success:    true,
        TotalCount: count,
        Data:       data,
        PageNum:    pageNum,
        PageSize:   PageSize,
        TraceID:    g.traceID(),
    })
    return
}

效果

若前端提交TraceID,则以前端为准;

若未提交,后端自动返回TraceID

注入接口一 image.png

image.png

注入接口二

image.png

image.png

Q&A

封装ChildSpan V2

封装ChildSpan V2



func CreateChildSpanContext(spanCtxInterface context.Context, spanName string) opentracing.Span{

	var spanCtx context.Context

	spanCtx = spanCtxInterface.(context.Context)

	//创建子span
	span, _ := jaeger.WithSpan(spanCtx, spanName)

	return span
}

调用ChildSpan V2

span := response.CreateChildSpanContext(c, "getCertificates")
	span.SetTag("domainName", domainName)
	defer span.Finish()

效果

image.png

链接

blog.csdn.net/ss810540895…

www.cnblogs.com/xingxia/p/j…

developer.aliyun.com/article/110…

www.cnblogs.com/gaonac/p/15…

zhuanlan.zhihu.com/p/268740698

blog.csdn.net/windeal3203…