手动将OpenTelemetry上下文从Golang提取为字符串?

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

Manually extracting OpenTelemetry context from golang into a string?

问题

我正在构建一个简单的客户端服务器应用程序,我希望能够跟踪从客户端执行到调用第二个服务器微服务的服务器微服务。

简单来说,它的复杂性不超过 CLI -> ServiceA -> ServiceB。

我遇到的挑战是如何序列化上下文 - 我查阅的大部分文档似乎都是进行某种形式的自动化 HTTP 标头注入(例如 https://opentelemetry.lightstep.com/core-concepts/context-propagation/),但我无法访问它。我需要在客户端序列化(我认为是)跟踪/跨度的上下文,并将其推送到服务器,在那里我将重新激活它。(请注意,我希望这个过程更简单,但我无法弄清楚)。

因此,对象的结构如下(称为"job"):

args := &types.SubmitArgs{
	SerializedOtelContext: serializedOtelContext,
}

job := &types.Job{}

tracer := otel.GetTracerProvider().Tracer("myservice.org")
_, span := tracer.Start(ctx, "Submitting Job to RPC")
err := system.JsonRpcMethod(rpcHost, rpcPort, "Submit", args, job)

提交到 JsonRpcMethod 的函数在这里:

func JsonRpcMethod(
	host string,
	port int,
	method string,
	req, res interface{},
) error {
	client, err := rpc.DialHTTP("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return fmt.Errorf("Error in dialing. %s", err)
	}
	return client.Call(fmt.Sprintf("JobServer.%s", method), req, res)
}

接收它的函数在这里:

func (server *JobServer) Submit(args *types.SubmitArgs, reply *types.Job) error {
	//nolint
	job, err := server.RequesterNode.Scheduler.SubmitJob(args.Spec, args.Deal)
	if err != nil {
		return err
	}
	*reply = *job
	return nil
}

我的问题是,在接收函数(上面的"Submit")中,如何从发送方提取跟踪/跨度?

英文:

I'm building a simple client server app which I want to trace across the client execution to a server microservice that calls a second server microservice.

Simply speaking, it's not more complicated than CLI -> ServiceA -> ServiceB.

The challenge I'm having is how to serialize the context - most of the docs I've looked at appear to do some form of automated HTTP header injection (e.g. https://opentelemetry.lightstep.com/core-concepts/context-propagation/) , but I do not have access to that. I need to serialize (I think) the context of the trace/span in the client and push it to the server, where I'll rehydrate it. (Mind you, I'd love this to be simpler, but I cannot figure it out).

So the object looks like this (called "job"):

	args := &types.SubmitArgs{
		SerializedOtelContext: serializedOtelContext,
	}

	job := &types.Job{}

	tracer := otel.GetTracerProvider().Tracer("myservice.org")
	_, span := tracer.Start(ctx, "Submitting Job to RPC")
	err := system.JsonRpcMethod(rpcHost, rpcPort, "Submit", args, job)

The function to submit to JsonRpcMethod is here:

func JsonRpcMethod(
	host string,
	port int,
	method string,
	req, res interface{},
) error {
	client, err := rpc.DialHTTP("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return fmt.Errorf("Error in dialing. %s", err)
	}
	return client.Call(fmt.Sprintf("JobServer.%s", method), req, res)
}

And the function that receives it is here:

func (server *JobServer) Submit(args *types.SubmitArgs, reply *types.Job) error {
	//nolint
	job, err := server.RequesterNode.Scheduler.SubmitJob(args.Spec, args.Deal)
	if err != nil {
		return err
	}
	*reply = *job
	return nil
}

My question is how do I, in the receiving function ("Submit" above) extract the trace/span from the sender?

答案1

得分: 5

这是一个小程序,用于说明用法。希望这能让它变得清晰明了。

package main

import (
	"context"
	"fmt"

	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/propagation"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {

	// 公共初始化
	// 你也可以将它们设置为全局变量
	exp, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())
	bsp := sdktrace.NewSimpleSpanProcessor(exp) // 在生产环境中应使用批处理跨度处理器
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithSpanProcessor(bsp),
	)

	propgator := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})

	ctx, span := tp.Tracer("foo").Start(context.Background(), "parent-span-name")
	defer span.End()

	// 将上下文序列化到载体中
	carrier := propagation.MapCarrier{}
	propgator.Inject(ctx, carrier)
	// 这个载体被发送到进程之间
	fmt.Println(carrier)

	// 提取上下文并作为子级开始新的跨度
	// 在你的接收函数中
	parentCtx := propgator.Extract(context.Background(), carrier)
	_, childSpan := tp.Tracer("foo").Start(parentCtx, "child-span-name")
	childSpan.AddEvent("some-dummy-event")
	childSpan.End()
}
英文:

Here is a small program to illustrate the usage. Hope this makes it clear.

package main

import (
	"context"
	"fmt"

	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/propagation"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {

	// common init
	// You may also want to set them as globals
	exp, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())
	bsp := sdktrace.NewSimpleSpanProcessor(exp) // You should use batch span processor in prod
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithSpanProcessor(bsp),
	)

	propgator := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})

	ctx, span := tp.Tracer("foo").Start(context.Background(), "parent-span-name")
	defer span.End()

	// Serialize the context into carrier
	carrier := propagation.MapCarrier{}
	propgator.Inject(ctx, carrier)
	// This carrier is sent accros the process
	fmt.Println(carrier)

	// Extract the context and start new span as child
	// In your receiving function
	parentCtx := propgator.Extract(context.Background(), carrier)
	_, childSpan := tp.Tracer("foo").Start(parentCtx, "child-span-name")
	childSpan.AddEvent("some-dummy-event")
	childSpan.End()
}

huangapple
  • 本文由 发表于 2022年4月17日 01:13:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/71895937.html
匿名

发表评论

匿名网友

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

确定