Want to use OpenTelemetry instead? Read these docs to get started!

This Quick Start will have you configure your tracer to communicate with the Lightstep Satellites and create a single span on your service. You install both the OpenTracing API and Lightstep tracer and then use the OpenTracing and Lightstep APIs to instrument your code. The Lightstep tracer also collects and reports on infrastructure metrics, communicating directly with the Lightstep Engine.

To ensure you can access all Lightstep functionality, including infrastructure metrics reporting, update your tracer to the latest release. To update, simply follow the instructions for installing the tracer. No code changes are needed.

While Lightstep offers tracers and APIs specific to its tracing software, you will still use the OpenTracing API to fully instrument your code. Be sure you have read and are familiar with both Lightstep tracers and the OpenTracing specification in your application’s language.

Golang Instrumentation for OpenTracing

  1. Find your access token in Lightstep. You’ll need this to configure your Lightstep tracer.
    • Click the Project Settings button.
    • In the Access Tokens table, click the Copy icon to copy your access token to the clipboard.
  2. Install the OpenTracing and Lightstep tracer packages.

    1
    2
    
    go get 'github.com/lightstep/lightstep-tracer-go'
    go get 'github.com/opentracing/opentracing-go'
    
  3. In your application code, add a reference to the Lightstep Tracer and to the OpenTracing API.

    1
    2
    3
    4
    
    import (
      "github.com/opentracing/opentracing-go"
      "github.com/lightstep/lightstep-tracer-go"
    )
    
  4. Early in your application’s initialization, configure the Lightstep tracer and register it as the OpenTracing Global Tracer. As part of the configuration, you need to add your access token and add a tag to hold the value of your service’s name.

    Use the right code for your environment!
    When initializing the tracer, you pass in properties to the Lightstep Satellites that collect the span data. Be sure to use the code for your Lightstep Satellite environment - On-Premise, Lightstep Public Satellite pool, or Developer Mode Satellite.

     

    on-premise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
     package main. . .
     func main() {
       ...
       lightStepTracer := lightstep.NewTracer(lightstep.Options{
         Collector: lightstep.Endpoint{
           Host:"{your_load_balancer_DNS_name_or_IP_address}",
           Plaintext: true,
         },
         AccessToken: "YOUR_ACCESS_TOKEN",
         Tags: map[string]interface{}{
           lightstep.ComponentNameKey: "YOUR_SERVICE_NAME",
         },
       })
       opentracing.SetGlobalTracer(lightStepTracer)
       ...
     }
    

    public

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     package main
     . . .
     func main() {
       ...
       lightStepTracer := lightstep.NewTracer(lightstep.Options{
         AccessToken: "YOUR_ACCESS_TOKEN",
         Tags: map[string]interface{}{
           lightstep.ComponentNameKey: "YOUR_SERVICE_NAME",
         },
       })
       opentracing.SetGlobalTracer(lightStepTracer)
       ...
     }
    

    developer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     package main
     . . .
     func main() {
     ...
     lightStepTracer := lightstep.NewTracer(lightstep.Options{
       Collector: lightstep.Endpoint{
         Host:      "localhost",
         Port:      8360,
         Plaintext: true,
       },
       AccessToken: "developer",
       Tags: map[string]interface{}{
         lightstep.ComponentNameKey: "YOUR_SERVICE_NAME",
       },
     })
     opentracing.SetGlobalTracer(lightStepTracer)
     ...
      }
    ​​​​​​​
    

 

  1. Test that everything is connected by sending a test span.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    tracer := opentracing.GlobalTracer()
    span := tracer.StartSpan("my-first-span")
    // for tag value, use either "client" or "server" depending on whether
    // this service receives or creates requests
    span.SetTag("kind", "client")
    span.LogKV("message", "what a lovely day")
    span.Finish()
    
    // remember to close the tracer in order to ensure spans are sent
    lightStepTracer.Close(context.Background()))
    
  2. Before shutdown, be sure to close the tracer so that any remaining spans will be sent.

    1
    
       lightStepTracer.Close(context.Background())
    
  3. Run the app.

  4. Open Lightstep. You should see your service in the Service directory list.

It may take a few moments for your service to display in the Service Directory. To see data immediately, click the Explorer tab to view data in the histogram.

Configure Metrics Reporting

Along with the tracer collecting telemetry data to report on performance issues and other contextual information to help you with root cause analysis, Lightstep can also ingest and report on infrastructure metrics for a service to further assist in your investigations.

To ensure you can access all Lightstep functionality, including infrastructure metrics reporting, update your tracer to the latest release. To update, simply follow the instructions for installing the tracer. No code changes are needed.

The following infrastructure metrics are sent to Lightstep:

  • CPU Usage (%)
  • Memory (%)
  • Network (bytes)

  • Garbage Collection (GC) Cycles (count)
  • Heap Allocation
  • Number of Goroutines

These metrics are displayed when you compare performance of a service over two different time periods.

Machine metrics are enabled by default. However, if you use a proxy, you need to configure the endpoint on the SystemMetrics option to point to the Lightstep endpoint.

 

on-premise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main. . .
func main() {
  ...
  lightStepTracer := lightstep.NewTracer(lightstep.Options{
    Collector: lightstep.Endpoint{
      Host:"{your_load_balancer_DNS_name_or_IP_address}",
      Plaintext: true,
    },
    AccessToken: "YOUR_ACCESS_TOKEN",
    SystemMetrics: lightstep.SystemMetricsOptions{
			Disabled: false,
			Endpoint: lightstep.Endpoint{Host: "ingest.lightstep.com", Port: 443, Plaintext: false},
		},
    Tags: map[string]interface{}{
      lightstep.ComponentNameKey: "YOUR_SERVICE_NAME",
    },
  })
  opentracing.SetGlobalTracer(lightStepTracer)
  ...
}

public

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
. . .
func main() {
  ...
  lightStepTracer := lightstep.NewTracer(lightstep.Options{
    AccessToken: "YOUR_ACCESS_TOKEN",
    SystemMetrics: lightstep.SystemMetricsOptions{
      Disabled: false,
      Endpoint: lightstep.Endpoint{Host: "ingest.lightstep.com", Port: 443, Plaintext: false},
    },
    Tags: map[string]interface{}{
      lightstep.ComponentNameKey: "YOUR_SERVICE_NAME",
    },
  })
  opentracing.SetGlobalTracer(lightStepTracer)
  ...
}

developer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
. . .
func main() {
...
lightStepTracer := lightstep.NewTracer(lightstep.Options{
  Collector: lightstep.Endpoint{
    Host:      "localhost",
    Port:      8360,
    Plaintext: true,
  },
  AccessToken: "developer",
  SystemMetrics: lightstep.SystemMetricsOptions{
    Disabled: false,
    Endpoint: lightstep.Endpoint{Host: "ingest.lightstep.com", Port: 443, Plaintext: false},
  },
  Tags: map[string]interface{}{
    lightstep.ComponentNameKey: "YOUR_SERVICE_NAME",
  },
})
opentracing.SetGlobalTracer(lightStepTracer)
...
}
​​​​​​​

 

If you don’t want metrics reported, you can configure the tracer to turn it off.

To turn metrics off:

Set the SystemMetrics Disabled option to true.

1
2
3
4
SystemMetrics: lightstep.SystemMetricsOptions{
  Disabled: true,
  . . .},
},

Add More Spans

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

  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: