李恒道 发表于 2023-8-8 17:15:39

opentracing-gorm SQL追踪源码分析

在https://github.com/go-programming-tour-book/blog-service/blob/fc62a2672eb7ac8c5eb08ef1710e46a74bb19d90/internal/service/service.go

可以看到每次初始化代码之后都会调用otgorm.WithContext

```go
type Service struct {
        ctx context.Context
        dao *dao.Dao
}

func New(ctx context.Context) Service {
        svc := Service{ctx: ctx}
        svc.dao = dao.New(otgorm.WithContext(svc.ctx, global.DBEngine))
        return svc
}
```

我们看看otgorm.WithContext做了什么

代码在https://github.com/eddycjy/opentracing-gorm/blob/master/otgorm.go

这里调用了opentracing.SpanFromContext从context中读取parentSpan,判断是否存在,如果存在则设置到dao数据库实例上

```go
const (
        parentSpanGormKey = "opentracing:parent.span"
        spanGormKey       = "opentracing:span"
)

// SetSpanToGorm sets span to gorm settings, returns cloned DB
func WithContext(ctx context.Context, db *gorm.DB) *gorm.DB {
        if ctx == nil {
                return db
        }
        parentSpan := opentracing.SpanFromContext(ctx)
        if parentSpan == nil {
                return db
        }
        return db.Set(parentSpanGormKey, parentSpan)
}
```

SpanFromContext是openTracing的函数

https://github.com/opentracing/opentracing-go/blob/10b1cf09e00bdc84234b8c7a4b4d4e4afe64de87/gocontext.go#L26

主要调用ctx.Value通过activeSpanKeyjian

```go

func SpanFromContext(ctx context.Context) Span {
        val := ctx.Value(activeSpanKey)
        if sp, ok := val.(Span); ok {
                return sp
        }
        return nil
}
```

这里基本的逻辑已经清晰了,每次初始化链接的时候都会把ctx中的span注入到对应的dao实例上

那倒是在哪里注入的,一般我们会在中间的拦截器层调用StartSpanFromContextWithTracer

```go
span, newCtx = opentracing.StartSpanFromContextWithTracer(
                                c.Request.Context(),
                                global.Tracer,
                                c.Request.URL.Path,
                                opentracing.ChildOf(spanCtx),
                                opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
                        )
```

会返回一个新的newCtx,然后通过withContext设置上下文关系

我们可以看看StartSpanFromContextWithTracer的源码

```go
func StartSpanFromContextWithTracer(ctx context.Context, tracer Tracer, operationName string, opts ...StartSpanOption) (Span, context.Context) {
        if parentSpan := SpanFromContext(ctx); parentSpan != nil {
                opts = append(opts, ChildOf(parentSpan.Context()))
        }
        span := tracer.StartSpan(operationName, opts...)
        return span, ContextWithSpan(ctx, span)
}
```

这里主要调用了ContextWithSpan做了一层封装,我们再去看ContextWithSpan

发现通过withValue设置进去了对应的span

```go
var activeSpanKey = contextKey{}

// ContextWithSpan returns a new `context.Context` that holds a reference to
// the span. If span is nil, a new context without an active span is returned.
func ContextWithSpan(ctx context.Context, span Span) context.Context {
        if span != nil {
                if tracerWithHook, ok := span.Tracer().(TracerContextWithSpanExtension); ok {
                        ctx = tracerWithHook.ContextWithSpanHook(ctx, span)
                }
        }
        return context.WithValue(ctx, activeSpanKey, span)
}
```

到这里我们就了解了全部过程

拦截器中通过StartSpanFromContextWithTracer创建的时候会自动注入到ctx
然后在初始化dao层的时候ctx中的span并注入
每次调用gorm都会触发钩子
钩子再对其sql进行进一步处理实现sql跟踪效果

王一之 发表于 2023-8-9 09:38:15

哥哥都看啥去了。。。。知道这是干啥的东西么

李恒道 发表于 2023-8-9 15:05:20

王一之 发表于 2023-8-9 09:38
哥哥都看啥去了。。。。知道这是干啥的东西么

jaeger链路追踪,grpc用的

王一之 发表于 2023-8-9 15:07:04

李恒道 发表于 2023-8-9 15:05
jaeger链路追踪,grpc用的

链路追踪干啥的,这个和grpc也没啥关系
页: [1]
查看完整版本: opentracing-gorm SQL追踪源码分析