如何使用 NATS 正确传递远程父跨度?

Kok*_*zzu 4 go nats.io opentracing open-telemetry

我有一个虚拟示例我在这个仓库

我尝试将当前跨度上下文传递给远程跨度上下文,以便它可以正确显示跟踪,我所做的:

go func() {
    _, span := otel.Tracer("natsC").Start(context.Background(), "publish")
    defer span.End()

    // send current span context as header
    spanCtx := span.SpanContext()
    spanJson, _ := spanCtx.MarshalJSON()
    log.Println(string(spanJson))
    msg, err := nc.RequestMsg(&nats.Msg{
        Subject: topic1, Data: []byte("whatever"), Header: nats.Header{
            "otelTrace": []string{string(spanJson)},
        },
    }, 2*time.Second)
    if L.IsError(err, `nc.Publish`) {
        return
    }
    log.Println(`reply:`, msg)
}()
Run Code Online (Sandbox Code Playgroud)

在接收服务器上:

_, err = nc.QueueSubscribe(topic1, "my-queue", func(msg *nats.Msg) {
    // take header and deserialize back to spanContext
    rsc := msg.Header.Get(`otelTrace`)
    parentSpanCtx := trace.SpanContext{}
    err := json.Unmarshal([]byte(rsc), &parentSpanCtx)
    L.IsError(err, `json.Unmarshal`)

    // use remote context as parent context
    _, span := otel.Tracer(`natsC`).Start(trace.ContextWithRemoteSpanContext(context.Background(), parentSpanCtx), topic1)
    defer span.End()

    data := string(msg.Data)
    fmt.Println(data)
    err = msg.Respond(msg.Data)
    L.IsError(err, `msg.Respond`) // ignore error
})
Run Code Online (Sandbox Code Playgroud)

然后我使用这个命令运行它go run main.go natsC

两个跨度在 Jeager (localhost:16686) 上显示为单独的跨度,不像 http/grpc 示例中那样相关,我应该修改什么以便将其视为父跨度的子跨度?

不相关1 不相关2

等效的 http/grpc 示例:

相关的

小智 8

go.opentelemetry.io/otel/trace@v1.11.1/trace.go has the definitions for the context right, you can call MarshalJSON and have it spit something that looks useful but here's the thing. There is no equivalent unmarshalling function and the output is a string while the internal format is a fixed length byte array...

So to get it to work just dump the trace and span IDs into whatever format you like:

// Attach telemetry headers
headers := nats.Header{}
headers.Set(otelTraceID, span.SpanContext().TraceID().String())
headers.Set(otelSpanID, span.SpanContext().SpanID().String())
Run Code Online (Sandbox Code Playgroud)

Then on the receive side you have to rebuild it manually into a SpanContext:

func getParentContext(msg *nats.Msg) (spanContext trace.SpanContext, err error) {
    var traceID trace.TraceID
    traceID, err = trace.TraceIDFromHex(msg.Header.Get(otelTraceID))
    if err != nil {
        return spanContext, err
    }
    var spanID trace.SpanID
    spanID, err = trace.SpanIDFromHex(msg.Header.Get(otelSpanID))
    if err != nil {
        return spanContext, err
    }
    var spanContextConfig trace.SpanContextConfig
    spanContextConfig.TraceID = traceID
    spanContextConfig.SpanID = spanID
    spanContextConfig.TraceFlags = 01
    spanContextConfig.Remote = true
    spanContext = trace.NewSpanContext(spanContextConfig)
    return spanContext, nil
}
Run Code Online (Sandbox Code Playgroud)

Then actually use it:

remoteCtx, err := getParentContext(msg)
if err != nil {
    logrus.Fatal(err)
}

_, span := otel.Tracer(fqpn).Start(trace.ContextWithRemoteSpanContext(context.Background(), remoteCtx), msg.Subject)
defer span.End()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述