Go

API overview for those adding instrumentation

Everyday consumers of this opentracing package really only need to worry about a couple of key abstractions: the StartSpan function, the Span interface, and binding a Tracer at main()-time. Here are code snippets demonstrating some important use cases.

Singleton initialization

The simplest starting point is ./default_tracer.go. As early as possible, call

  1. import "github.com/opentracing/opentracing-go"
  2. import ".../some_tracing_impl"
  3. func main() {
  4. opentracing.SetGlobalTracer(
  5. // tracing impl specific:
  6. some_tracing_impl.New(...),
  7. )
  8. ...
  9. }

Non-Singleton initialization

If you prefer direct control to singletons, manage ownership of the opentracing.Tracer implementation explicitly.

Creating a Span given an existing Go context.Context

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.

  1. func xyz(ctx context.Context, ...) {
  2. ...
  3. span, ctx := opentracing.StartSpanFromContext(ctx, "operation_name")
  4. defer span.Finish()
  5. span.LogFields(
  6. log.String("event", "soft error"),
  7. log.String("type", "cache timeout"),
  8. log.Int("waited.millis", 1500))
  9. ...
  10. }

Starting an empty trace by creating a “root span”

It’s always possible to create a “root” Span with no parent or other causal reference.

  1. func xyz() {
  2. ...
  3. sp := opentracing.StartSpan("operation_name")
  4. defer sp.Finish()
  5. ...
  6. }

Creating a (child) Span given an existing (parent) Span

  1. func xyz(parentSpan opentracing.Span, ...) {
  2. ...
  3. sp := opentracing.StartSpan(
  4. "operation_name",
  5. opentracing.ChildOf(parentSpan.Context()))
  6. defer sp.Finish()
  7. ...
  8. }

Serializing to the wire

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

Deserializing from the wire

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

Conditionally capture a field using log.Noop

In some situations, you may want to dynamically decide whether or not to log a field. For example, you may want to capture additional data, such as a customer ID, in non-production environments:

  1. func Customer(order *Order) log.Field {
  2. if os.Getenv("ENVIRONMENT") == "dev" {
  3. return log.String("customer", order.Customer.ID)
  4. }
  5. return log.Noop()
  6. }