OpenTelemetry Trace API for Go provides distributed tracing capabilities with TracerProvider, Tracer, and Span interfaces for instrumentation.
TracerProvider and Tracer are the factory interfaces for creating and managing spans. TracerProvider represents the telemetry collection pipeline, while Tracer creates spans for a specific instrumentation scope.
TracerProvider provides Tracers for instrumentation code. It represents the collection destination of all spans from the tracers it creates.
type TracerProvider interface {
// Users of the interface can ignore this. This embedded type is only used
// by implementations of this interface.
embedded.TracerProvider
// Tracer returns a unique Tracer scoped to be used by instrumentation code
// to trace computational workflows. The scope and identity of that
// instrumentation code is uniquely defined by the name and options passed.
//
// The passed name needs to uniquely identify instrumentation code.
// Therefore, it is recommended that name is the Go package name of the
// library providing instrumentation (note: not the code being instrumented).
// Instrumentation libraries can have multiple versions, therefore, the
// WithInstrumentationVersion option should be used to distinguish these
// different codebases.
//
// If the same name and options are passed multiple times, the same Tracer
// will be returned (it is up to the implementation if this will be the
// same underlying instance of that Tracer or not). It is not necessary to
// call this multiple times with the same name and options to get an
// up-to-date Tracer. All implementations will ensure any TracerProvider
// configuration changes are propagated to all provided Tracers.
//
// If name is empty, then an implementation defined default name will be
// used instead.
//
// This method is safe to call concurrently.
Tracer(name string, options ...TracerOption) Tracer
}package mylib
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
const (
instrumentationName = "github.com/example/mylib"
instrumentationVersion = "v1.2.3"
)
type Library struct {
tracer trace.Tracer
}
// NewLibrary creates a new instance accepting a TracerProvider.
// If tp is nil, uses the global TracerProvider.
func NewLibrary(tp trace.TracerProvider) *Library {
if tp == nil {
tp = otel.TracerProvider()
}
return &Library{
tracer: tp.Tracer(
instrumentationName,
trace.WithInstrumentationVersion(instrumentationVersion),
),
}
}
func (l *Library) DoWork(ctx context.Context) {
ctx, span := l.tracer.Start(ctx, "DoWork")
defer span.End()
// ... work happens here
}Tracer is the creator of Spans. It is unique to the instrumentation scope.
type Tracer interface {
// Users of the interface can ignore this. This embedded type is only used
// by implementations of this interface.
embedded.Tracer
// Start creates a span and a context.Context containing the newly-created span.
//
// If the context.Context provided in `ctx` contains a Span then the newly-created
// Span will be a child of that span, otherwise it will be a root span. This behavior
// can be overridden by providing `WithNewRoot()` as a SpanOption, causing the
// newly-created Span to be a root span even if `ctx` contains a Span.
//
// When creating a Span it is recommended to provide all known span attributes using
// the `WithAttributes()` SpanOption as samplers will only have access to the
// attributes provided when a Span is created.
//
// Any Span that is created MUST also be ended. This is the responsibility of the user.
// Implementations of this API may leak memory or other resources if Spans are not ended.
Start(ctx context.Context, spanName string, opts ...SpanStartOption) (context.Context, Span)
}The Start method creates a new span and returns a context containing that span. Key behaviors:
ctx contains a span, the new span becomes its childWithNewRoot() option to create a root span regardless of parentimport (
"context"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/attribute"
)
func processRequest(ctx context.Context, tracer trace.Tracer, requestID string) error {
// Create span with initial attributes for sampling decisions
ctx, span := tracer.Start(ctx, "processRequest",
trace.WithAttributes(
attribute.String("request.id", requestID),
attribute.String("operation.type", "process"),
),
trace.WithSpanKind(trace.SpanKindServer),
)
defer span.End()
// Child spans automatically link to parent through context
if err := fetchData(ctx, tracer); err != nil {
return err
}
return saveResults(ctx, tracer)
}
func fetchData(ctx context.Context, tracer trace.Tracer) error {
// This span's parent is the processRequest span
ctx, span := tracer.Start(ctx, "fetchData")
defer span.End()
// ... fetch data
return nil
}
func saveResults(ctx context.Context, tracer trace.Tracer) error {
// This span is also a child of processRequest (sibling to fetchData)
ctx, span := tracer.Start(ctx, "saveResults")
defer span.End()
// ... save results
return nil
}TracerOption applies configuration to a Tracer when it's created.
type TracerOption interface {
// contains unexported methods
}// WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) TracerOption
// WithInstrumentationAttributes sets the instrumentation attributes.
// The passed attributes will be de-duplicated.
func WithInstrumentationAttributes(attr ...attribute.KeyValue) TracerOption
// WithSchemaURL sets the schema URL for the Tracer.
func WithSchemaURL(schemaURL string) TracerOptionimport (
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/attribute"
)
tracer := tp.Tracer(
"github.com/example/mylib",
trace.WithInstrumentationVersion("v1.2.3"),
trace.WithInstrumentationAttributes(
attribute.String("library.language", "go"),
attribute.String("library.platform", "linux"),
),
trace.WithSchemaURL("https://opentelemetry.io/schemas/1.21.0"),
)TracerConfig holds the configuration for a Tracer. It's typically used by SDK implementations.
type TracerConfig struct {
// contains unexported fields
}
// NewTracerConfig applies all the options to a returned TracerConfig.
func NewTracerConfig(options ...TracerOption) TracerConfig// InstrumentationVersion returns the version of the library providing instrumentation.
func (t *TracerConfig) InstrumentationVersion() string
// InstrumentationAttributes returns the attributes associated with the library
// providing instrumentation.
func (t *TracerConfig) InstrumentationAttributes() attribute.Set
// SchemaURL returns the Schema URL of the telemetry emitted by the Tracer.
func (t *TracerConfig) SchemaURL() stringA no-op TracerProvider is available for testing or disabling telemetry:
// NewNoopTracerProvider returns an implementation of TracerProvider that
// performs no operations. The Tracer and Spans created from the returned
// TracerProvider also perform no operations.
//
// Deprecated: Use go.opentelemetry.io/otel/trace/noop.NewTracerProvider instead.
func NewNoopTracerProvider() TracerProviderFor production use, prefer the noop package:
import "go.opentelemetry.io/otel/trace/noop"
tp := noop.NewTracerProvider()To create a root span (ignoring any parent in context):
ctx, span := tracer.Start(ctx, "rootOperation",
trace.WithNewRoot(),
)
defer span.End()This is useful when:
import "go.opentelemetry.io/otel"
func init() {
tracer = otel.Tracer("my-service")
}type Service struct {
tracer trace.Tracer
}
func NewService(tp trace.TracerProvider) *Service {
if tp == nil {
tp = otel.TracerProvider()
}
return &Service{
tracer: tp.Tracer("my-service"),
}
}Injecting the TracerProvider provides:
Install with Tessl CLI
npx tessl i tessl/golang-go-opentelemetry-io--otel--trace