如何在 gRPC 服务器存根中从 ctx 创建 OpenTelemetry span

Posted

技术标签:

【中文标题】如何在 gRPC 服务器存根中从 ctx 创建 OpenTelemetry span【英文标题】:How to create an OpenTelemetry span from ctx in gRPC server stub 【发布时间】:2021-08-04 08:33:30 【问题描述】:

我的 Go gRPC 服务器配备了

    Google 跟踪跨度导出器:

    import texporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
    ...
    gcp, err := texporter.NewExporter()
    ...
    tracer := trace.NewTracerProvider(trace.WithSyncer(traceExporter),
        trace.WithSampler(trace.AlwaysSample()))
    otel.SetTracerProvider(tracer)
    

    在 gRPC 服务器上注册的 otelgrpc 拦截器。

    unaryInterceptors := grpc_middleware.WithUnaryServerChain(
        otelgrpc.UnaryServerInterceptor(),
    )
    streamInterceptors := grpc_middleware.WithStreamServerChain(
        otelgrpc.StreamServerInterceptor(),
    )
    

现在我尝试在 RPC 实现中创建一个跟踪跨度,以便我可以为该方法创建子跨度,例如:

func (s *srv) Test(ctx context.Context, req *pb.Request) (*pb.TestResponse, error) 
    // create a span here
    span1 := [??????].Start()
    doWork1()
    span1.End()

    span2 := [??????].Start()
    doWork2()
    span2.End()
    ...

但目前还不清楚from the OpenTelemetry docs 是如何做到这一点的。

我得到的最接近的是otel.GetTracerProvider().Tracer("some string here???"),提供了Start(ctx)(ctx,Span)。但我不清楚这里要提供什么字符串(我的出口商没有文档指出的 url),这似乎很不方便。

我在想某处有类似otelgrpc.SpanFromCtx(ctx) 的方法可以拉动示踪剂+使用我没有找到的rpc ctx 创建一个跨度。遗憾的是,OT+gRPC+Go 的文档非常缺乏。

【问题讨论】:

【参考方案1】:

由于您使用的是github.com/GoogleCloudPlatform/opentelemetry-operations-go,因此您应该使用它自己来创建 span 而不是 opentelemetry。

你可以像这样创建一个新的跨度:

    // Create custom span.
    tracer := otel.TraceProvider().Tracer("example.com/trace")
    err = func(ctx context.Context) error 
        ctx, span := tracer.Start(ctx, "foo")
        defer span.End()

        // Do some work.

        return nil
    (ctx)

更多详情可以参考OpenTelemetry Google Cloud Trace Exporter

【讨论】:

是的,我在文档中看到了这一点,但我怀疑这是个好主意。您在各处(例如,在每个 gRPC 服务类型中,或者更糟糕的是,在每个 RPC 中)一次又一次地初始化跟踪器,并且可能使用不同的名称。我认为最好的方法是在 gRPC 服务类型中携带 trace.TraceProvider 实例并使用它来初始化。您的建议对于在代码中导入的“库”(例如 redis-go)非常有意义,因此无法访问 Tracer,因此它可以将自己的初始化为单例。

以上是关于如何在 gRPC 服务器存根中从 ctx 创建 OpenTelemetry span的主要内容,如果未能解决你的问题,请参考以下文章

如何在 gRPC 中从服务器到客户端进行广播?

gRPC 中的通道/存根是线程安全的吗

检查 gRPC 流何时为空或不是流式传输数据

protoc 不生成服务存根文件

csharp中的grpc通道/存根线程是不是安全

Python 客户端在 GRPC 存根上返回 CERTIFICATE_VERIFY_FAILED