- Go
- API overview for those adding instrumentation
- Singleton initialization
- Non-Singleton initialization
- Creating a Span given an existing Go
context.Context
- Starting an empty trace by creating a “root span”
- Creating a (child) Span given an existing (parent) Span
- Serializing to the wire
- Deserializing from the wire
- Conditionally capture a field using
log.Noop
- API overview for those adding instrumentation
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
import "github.com/opentracing/opentracing-go"
import ".../some_tracing_impl"
func main() {
opentracing.SetGlobalTracer(
// tracing impl specific:
some_tracing_impl.New(...),
)
...
}
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
.
func xyz(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))
...
}
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.
func xyz() {
...
sp := opentracing.StartSpan("operation_name")
defer sp.Finish()
...
}
Creating a (child) Span given an existing (parent) Span
func xyz(parentSpan opentracing.Span, ...) {
...
sp := opentracing.StartSpan(
"operation_name",
opentracing.ChildOf(parentSpan.Context()))
defer sp.Finish()
...
}
Serializing to the wire
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)
...
}
...
}
Deserializing from the wire
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)
...
}
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:
func Customer(order *Order) log.Field {
if os.Getenv("ENVIRONMENT") == "dev" {
return log.String("customer", order.Customer.ID)
}
return log.Noop()
}