Home > database >  Manually extracting OpenTelemetry context from golang into a string?
Manually extracting OpenTelemetry context from golang into a string?

Time:04-17

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?

CodePudding user response:

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