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

huangapple go评论91阅读模式
英文:

How to pass remote parent span properly using NATS?

问题

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

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

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

    // 将当前跨度上下文作为标头发送
    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)
}()

在接收服务器上:

_, err = nc.QueueSubscribe(topic1, "my-queue", func(msg *nats.Msg) {
    // 获取标头并反序列化为 spanContext
    rsc := msg.Header.Get("otelTrace")
    parentSpanCtx := trace.SpanContext{}
    err := json.Unmarshal([]byte(rsc), &parentSpanCtx)
    L.IsError(err, "json.Unmarshal")

    // 将远程上下文用作父上下文
    _, 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") // 忽略错误
})

然后我使用以下命令运行它:go run main.go natsC

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

英文:

I have a dummy example on this repo

I tried to pass current span context to a remote one so it would show the trace properly, what I've done:

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)
}()

On the receiver server:

_, 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
})

then I run it using this command go run main.go natsC.

both span shown on Jeager (localhost:16686) as separate spans, not correlated like in http/grpc example, what should I modify so it would considered as child span of the parent?

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

equivalent http/grpc example:

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

答案1

得分: 5

go.opentelemetry.io/otel/trace@v1.11.1/trace.go中定义了上下文的内容,你可以调用MarshalJSON函数并获得一个看起来有用的输出,但是有一点需要注意。它没有对应的反序列化函数,而输出是一个字符串,而内部格式是一个固定长度的字节数组...

因此,为了使其正常工作,只需将跟踪和跨度ID转储到任何你喜欢的格式中:

// 附加遥测标头
headers := nats.Header{}
headers.Set(otelTraceID, span.SpanContext().TraceID().String())
headers.Set(otelSpanID, span.SpanContext().SpanID().String())

然后在接收端,你必须手动重新构建为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
}

然后实际使用它:

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()

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

英文:

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())

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
}

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()

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

huangapple
  • 本文由 发表于 2022年10月3日 19:51:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/73935038.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定