英文:
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?
equivalent http/grpc example:
答案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()
英文:
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()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论