What it is
The Go SDK for OpenTelemetry — vendor-neutral distributed tracing, metrics and logs that can be exported to Jaeger, Tempo, Datadog or any OTLP backend.
Configure a TracerProvider with an exporter, then create spans around meaningful operations. Context propagation carries the trace across service boundaries.
Installation
go get go.opentelemetry.io/otel go.opentelemetry.io/otel/sdkGetting started
The smallest useful thing you can do with it, and what each part means.
go
exporter, err := otlptracegrpc.New(ctx)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("library-api"),
)),
sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)), // sample 10%
)
otel.SetTracerProvider(tp)
defer tp.Shutdown(ctx) // flush buffered spans
tracer := otel.Tracer("library-api")
func getBook(ctx context.Context, id string) (*Book, error) {
ctx, span := tracer.Start(ctx, "getBook")
defer span.End()
span.SetAttributes(attribute.String("book.id", id))
book, err := store.Find(ctx, id) // child spans attach via ctx
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}
return book, nil
}Advanced usage
Where the library earns its place over a simpler alternative.
go
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
// Server: otelhttp extracts the incoming trace context.
handler := otelhttp.NewHandler(mux, "library-api")
// Client: otelhttp injects it into outgoing headers.
client := http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Traces are flat, with no child spans
- A parent context was not threaded through. Use the ctx returned by tracer.Start for all downstream calls.
- No spans reach the backend
- The batch processor buffers. Call tp.Shutdown or ForceFlush before exit, and verify the exporter endpoint.
Best practices
- Always use the context returned by tracer.Start, or your spans will not nest.
- Call defer span.End() immediately after starting a span.
- Sample in production — tracing every request is expensive at volume.
- Shut the provider down on exit so buffered spans are flushed.
Background
Why it exists, and what it was reacting to.
OpenTelemetry merged the competing OpenTracing and OpenCensus projects into one standard, so instrumentation is written once and the backend becomes a configuration choice.
