This topic explains how to add connected spans to create traces. Before you can do that, you need to add instrumentation that references the OpenTracing API and LightStep tracer, and you need to create a global tracer for your app. Read the Quick Start guide before continuing with this topic. For API documentation and advice on instrumentation in general, see the OpenTracing godocs and the OpenTracing website.

Now that you’ve begun instrumentation of your app, you can create more spans and connect them together.

Read Instrument Your Code and Understand Distributed Tracing for some conceputal information.

  1. Start adding code to create a span for your service’s operation. Start with an operation at an edge (and that calls an operation or operations in response). This will be your root span. For the operation name, use a unique name that clearly represents the function. This is what will display in LightStep, so understandable operation names are important.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
     	func xyz() {
         ...
         sp := opentracing.StartSpan("operation_name")
         defer sp.Finish()
    
         // Sleep for 1 second before returning to give the tracer
         // buffer time to flush the span to LightStep
         time.Sleep(1 * time.Second)
         ...
     }
    
  2. You then connect the root span to subsequent operations’ spans to create a full trace. Don’t forget to call Finish.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
     // If you use context.Context in your application, OpenTracing's Go library will happily rely on it for Span propagation. To start a new (blocking child) Span, you can use StartSpanFromContext.
    
      func abc(ctx context.Context, ...) {
         ...
         span, ctx := opentracing.StartSpanFromContext(ctx, "operation_name")
         defer span.Finish()
         span.LogFields(
             log.String("event", "soft error"),
             log.String("type", "cache timeout"),
             log.Int("waited.millis", 1500))
         ...
     }
    
     // OR create a child Span given an existing parent Span.
    
     func abc(parentSpan opentracing.Span, ...) {
         ...
         sp := opentracing.StartSpan(
             "operation_name",
             opentracing.ChildOf(parentSpan.Context()))
         defer sp.Finish()
         ...
     }
    
  3. If the trace needs to continue over the wire, you use GlobalTracer to inject the TraceContext into HTTP headers.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
     func makeSomeRequest(ctx context.Context) ... {
         if span := opentracing.SpanFromContext(ctx); span != nil {
             httpClient := &http.Client{}
             httpReq, _ := http.NewRequest("GET", "http://myservice/", nil)
    
             // Transmit the span's TraceContext as HTTP headers on our
             // outbound request.
             opentracing.GlobalTracer().Inject(
                 span.Context(),
                 opentracing.HTTPHeaders,
                 opentracing.HTTPHeadersCarrier(httpReq.Header))
    
             resp, err := httpClient.Do(httpReq)
             ...
         }
         ...
     }
    
  4. And then deserialize using extract like this.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
         var serverSpan opentracing.Span
         appSpecificOperationName := ...
         wireContext, err := opentracing.GlobalTracer().Extract(
             opentracing.HTTPHeaders,
             opentracing.HTTPHeadersCarrier(req.Header))
         if err != nil {
             // Optionally record something about err here
         }
    
         // Create the span referring to the RPC client if available.
         // If wireContext == nil, a root span will be created.
         serverSpan = opentracing.StartSpan(
             appSpecificOperationName,
             ext.RPCServerOption(wireContext))
    
         defer serverSpan.Finish()
    
         ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
         ...
     }
    

You’ll find details about working with the OpenTracing API here: