OpenTelemetry Go implementation providing APIs for distributed tracing, metrics, and logging to instrument applications and send telemetry data to observability platforms
The OpenTelemetry Go SDK trace implementation provides concrete implementations of the trace API interfaces, including TracerProvider, span processors, span exporters, samplers, and ID generators.
import sdktrace "go.opentelemetry.io/otel/sdk/trace"The SDK trace package provides:
The TracerProvider is the SDK implementation of the trace.TracerProvider interface.
type TracerProvider struct {
// Has unexported fields
}func NewTracerProvider(opts ...TracerProviderOption) *TracerProviderDefault Configuration:
Example:
package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
func main() {
ctx := context.Background()
// Create resource
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
),
)
if err != nil {
log.Fatal(err)
}
// Create exporter
exporter, err := otlptracehttp.New(ctx)
if err != nil {
log.Fatal(err)
}
// Create tracer provider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
// Set as global tracer provider
otel.SetTracerProvider(tp)
// Cleanup
defer func() {
if err := tp.Shutdown(ctx); err != nil {
log.Printf("Error shutting down tracer provider: %v", err)
}
}()
// Use tracer
tracer := otel.Tracer("my-tracer")
ctx, span := tracer.Start(ctx, "example-operation")
defer span.End()
// Your application logic here
}// Tracer returns a Tracer with the given name and options
func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer
// Shutdown shuts down the TracerProvider, flushing all pending telemetry
func (p *TracerProvider) Shutdown(ctx context.Context) error
// ForceFlush immediately exports all spans that have not yet been exported
func (p *TracerProvider) ForceFlush(ctx context.Context) error
// RegisterSpanProcessor adds a SpanProcessor to the TracerProvider
func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor)
// UnregisterSpanProcessor removes a SpanProcessor from the TracerProvider
func (p *TracerProvider) UnregisterSpanProcessor(sp SpanProcessor)Example Usage:
// Get tracer from provider
tracer := tp.Tracer("my-component")
// Force flush all pending spans
if err := tp.ForceFlush(context.Background()); err != nil {
log.Printf("Failed to flush: %v", err)
}
// Shutdown and cleanup
if err := tp.Shutdown(context.Background()); err != nil {
log.Printf("Failed to shutdown: %v", err)
}Options for configuring a TracerProvider.
type TracerProviderOption interface {
// Has unexported methods
}Available Options:
// WithBatcher registers an exporter with a BatchSpanProcessor
func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption
// WithSyncer registers an exporter with a SimpleSpanProcessor (not recommended for production)
func WithSyncer(e SpanExporter) TracerProviderOption
// WithSpanProcessor registers a SpanProcessor
func WithSpanProcessor(sp SpanProcessor) TracerProviderOption
// WithResource configures the Resource for the TracerProvider
func WithResource(r *resource.Resource) TracerProviderOption
// WithSampler configures the Sampler for the TracerProvider
func WithSampler(s Sampler) TracerProviderOption
// WithIDGenerator configures the IDGenerator for the TracerProvider
func WithIDGenerator(g IDGenerator) TracerProviderOption
// WithRawSpanLimits configures SpanLimits for the TracerProvider
func WithRawSpanLimits(limits SpanLimits) TracerProviderOptionSpan processors are called at the start and end of a span's lifecycle.
type SpanProcessor interface {
// OnStart is called when a span is started
OnStart(parent context.Context, s ReadWriteSpan)
// OnEnd is called when a span is finished
OnEnd(s ReadOnlySpan)
// Shutdown is called when the SDK shuts down
Shutdown(ctx context.Context) error
// ForceFlush exports all ended spans that have not yet been exported
ForceFlush(ctx context.Context) error
}BatchSpanProcessor batches spans and exports them in batches.
func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorOption) SpanProcessorDefault Configuration:
Example:
import (
"time"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func setupBatchProcessor() sdktrace.SpanProcessor {
exporter, err := stdouttrace.New()
if err != nil {
log.Fatal(err)
}
processor := sdktrace.NewBatchSpanProcessor(
exporter,
sdktrace.WithMaxQueueSize(4096),
sdktrace.WithBatchTimeout(10*time.Second),
sdktrace.WithExportTimeout(30*time.Second),
sdktrace.WithMaxExportBatchSize(1024),
)
return processor
}Options for configuring a BatchSpanProcessor.
type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions)
type BatchSpanProcessorOptions struct {
// MaxQueueSize is the maximum queue size to buffer spans
MaxQueueSize int
// BatchTimeout is the maximum duration for constructing a batch
BatchTimeout time.Duration
// ExportTimeout specifies the maximum duration for exporting spans
ExportTimeout time.Duration
// MaxExportBatchSize is the maximum number of spans to process in a single batch
MaxExportBatchSize int
// BlockOnQueueFull blocks if the queue is full
BlockOnQueueFull bool
}Available Options:
// WithMaxQueueSize sets the maximum queue size
func WithMaxQueueSize(size int) BatchSpanProcessorOption
// WithBatchTimeout sets the maximum duration for constructing a batch
func WithBatchTimeout(delay time.Duration) BatchSpanProcessorOption
// WithExportTimeout sets the maximum duration for exporting spans
func WithExportTimeout(timeout time.Duration) BatchSpanProcessorOption
// WithMaxExportBatchSize sets the maximum batch size
func WithMaxExportBatchSize(size int) BatchSpanProcessorOption
// WithBlocking sets whether to block when the queue is full
func WithBlocking() BatchSpanProcessorOptionSimpleSpanProcessor exports spans synchronously when they end. Not recommended for production use.
func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessorExample:
import (
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func setupSimpleProcessor() sdktrace.SpanProcessor {
exporter, err := stdouttrace.New()
if err != nil {
log.Fatal(err)
}
// Only use for testing/debugging
processor := sdktrace.NewSimpleSpanProcessor(exporter)
return processor
}Span exporters send span data to external systems.
type SpanExporter interface {
// ExportSpans exports a batch of spans
ExportSpans(ctx context.Context, spans []ReadOnlySpan) error
// Shutdown notifies the exporter of a pending halt to operations
Shutdown(ctx context.Context) error
}ReadOnlySpan is the read-only interface to a span.
type ReadOnlySpan interface {
Name() string
SpanContext() trace.SpanContext
Parent() trace.SpanContext
SpanKind() trace.SpanKind
StartTime() time.Time
EndTime() time.Time
Attributes() []attribute.KeyValue
Links() []trace.Link
Events() []Event
Status() Status
InstrumentationScope() instrumentation.Scope
InstrumentationLibrary() instrumentation.Library
Resource() *resource.Resource
DroppedAttributes() int
DroppedLinks() int
DroppedEvents() int
ChildSpanCount() int
TracerProvider() trace.TracerProvider
}Samplers decide whether a span should be sampled and exported.
type Sampler interface {
// ShouldSample returns a SamplingResult based on a decision
ShouldSample(parameters SamplingParameters) SamplingResult
// Description returns information describing the Sampler
Description() string
}type SamplingParameters struct {
ParentContext context.Context
TraceID trace.TraceID
Name string
Kind trace.SpanKind
Attributes []attribute.KeyValue
Links []trace.Link
}type SamplingDecision uint8
const (
// Drop will not record the span
Drop SamplingDecision = iota
// RecordOnly indicates the span is recorded but not sampled
RecordOnly
// RecordAndSample indicates the span is recorded and sampled
RecordAndSample
)
type SamplingResult struct {
Decision SamplingDecision
Attributes []attribute.KeyValue
Tracestate trace.TraceState
}Samples every trace.
func AlwaysSample() SamplerExample:
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)Samples no traces.
func NeverSample() SamplerExample:
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.NeverSample()),
)Samples a given fraction of traces.
func TraceIDRatioBased(fraction float64) SamplerExample:
// Sample 10% of traces
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)),
)Respects the parent span's sampling decision.
func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) SamplerParentBasedSamplerOption:
type ParentBasedSamplerOption interface {
// Has unexported methods
}
// WithRemoteParentSampled sets the sampler for sampled remote parent
func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption
// WithRemoteParentNotSampled sets the sampler for remote parent which is not sampled
func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption
// WithLocalParentSampled sets the sampler for sampled local parent
func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption
// WithLocalParentNotSampled sets the sampler for local parent which is not sampled
func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOptionExample:
// Default: ParentBased with AlwaysSample root
sampler := sdktrace.ParentBased(sdktrace.AlwaysSample())
// Custom configuration
sampler := sdktrace.ParentBased(
sdktrace.TraceIDRatioBased(0.5),
sdktrace.WithRemoteParentSampled(sdktrace.AlwaysSample()),
sdktrace.WithRemoteParentNotSampled(sdktrace.NeverSample()),
sdktrace.WithLocalParentSampled(sdktrace.AlwaysSample()),
sdktrace.WithLocalParentNotSampled(sdktrace.NeverSample()),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sampler),
)ID generators create trace and span IDs.
type IDGenerator interface {
// NewIDs returns a new trace and span ID
NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID)
// NewSpanID returns a new span ID for the given trace
NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID
}The default ID generator uses a random number generator.
Example:
// Custom ID generator
type customIDGenerator struct{}
func (g customIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) {
var traceID trace.TraceID
var spanID trace.SpanID
// Custom logic to generate IDs
return traceID, spanID
}
func (g customIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID {
var spanID trace.SpanID
// Custom logic to generate span ID
return spanID
}
// Use custom ID generator
tp := sdktrace.NewTracerProvider(
sdktrace.WithIDGenerator(customIDGenerator{}),
)SpanLimits configure resource limits for spans.
type SpanLimits struct {
// AttributeValueLengthLimit is the maximum allowed attribute value length
AttributeValueLengthLimit int
// AttributeCountLimit is the maximum allowed span attribute count
AttributeCountLimit int
// EventCountLimit is the maximum allowed span event count
EventCountLimit int
// LinkCountLimit is the maximum allowed span link count
LinkCountLimit int
// AttributePerEventCountLimit is the maximum allowed attributes per event
AttributePerEventCountLimit int
// AttributePerLinkCountLimit is the maximum allowed attributes per link
AttributePerLinkCountLimit int
}// NewSpanLimits returns a SpanLimits with values from environment or defaults
func NewSpanLimits() SpanLimitsDefault Values:
const (
// DefaultAttributeValueLengthLimit is the default maximum allowed attribute value length
DefaultAttributeValueLengthLimit = -1 // unlimited
// DefaultAttributeCountLimit is the default maximum allowed span attribute count
DefaultAttributeCountLimit = 128
// DefaultEventCountLimit is the default maximum allowed span event count
DefaultEventCountLimit = 128
// DefaultLinkCountLimit is the default maximum allowed span link count
DefaultLinkCountLimit = 128
// DefaultAttributePerEventCountLimit is the default maximum allowed attributes per event
DefaultAttributePerEventCountLimit = 128
// DefaultAttributePerLinkCountLimit is the default maximum allowed attributes per link
DefaultAttributePerLinkCountLimit = 128
)Example:
// Use default limits
limits := sdktrace.NewSpanLimits()
// Custom limits
limits := sdktrace.SpanLimits{
AttributeValueLengthLimit: 1000,
AttributeCountLimit: 256,
EventCountLimit: 256,
LinkCountLimit: 64,
AttributePerEventCountLimit: 64,
AttributePerLinkCountLimit: 64,
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithRawSpanLimits(limits),
)package main
import (
"context"
"log"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
func main() {
ctx := context.Background()
// Step 1: Create resource
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
semconv.DeploymentEnvironment("production"),
),
resource.WithProcess(),
resource.WithHost(),
)
if err != nil {
log.Fatalf("Failed to create resource: %v", err)
}
// Step 2: Create exporter
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpoint("localhost:4318"),
otlptracehttp.WithInsecure(),
)
if err != nil {
log.Fatalf("Failed to create exporter: %v", err)
}
// Step 3: Create span processor
processor := sdktrace.NewBatchSpanProcessor(
exporter,
sdktrace.WithMaxQueueSize(4096),
sdktrace.WithBatchTimeout(5*time.Second),
sdktrace.WithExportTimeout(30*time.Second),
sdktrace.WithMaxExportBatchSize(512),
)
// Step 4: Create sampler
sampler := sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.5))
// Step 5: Configure span limits
limits := sdktrace.SpanLimits{
AttributeValueLengthLimit: -1, // unlimited
AttributeCountLimit: 128,
EventCountLimit: 128,
LinkCountLimit: 128,
AttributePerEventCountLimit: 128,
AttributePerLinkCountLimit: 128,
}
// Step 6: Create tracer provider
tp := sdktrace.NewTracerProvider(
sdktrace.WithSpanProcessor(processor),
sdktrace.WithResource(res),
sdktrace.WithSampler(sampler),
sdktrace.WithRawSpanLimits(limits),
)
// Step 7: Set as global provider
otel.SetTracerProvider(tp)
// Step 8: Setup cleanup
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Force flush to ensure all spans are exported
if err := tp.ForceFlush(ctx); err != nil {
log.Printf("Failed to flush spans: %v", err)
}
// Shutdown provider
if err := tp.Shutdown(ctx); err != nil {
log.Printf("Failed to shutdown provider: %v", err)
}
}()
// Step 9: Use tracer
tracer := otel.Tracer("my-component")
// Create root span
ctx, rootSpan := tracer.Start(ctx, "root-operation")
rootSpan.SetAttributes(
attribute.String("user.id", "12345"),
attribute.String("action", "process"),
)
// Simulate work
processRequest(ctx, tracer)
rootSpan.End()
}
func processRequest(ctx context.Context, tracer trace.Tracer) {
ctx, span := tracer.Start(ctx, "process-request")
defer span.End()
span.SetAttributes(
attribute.Int("items.count", 10),
attribute.String("status", "processing"),
)
span.AddEvent("processing started")
// Simulate work
time.Sleep(100 * time.Millisecond)
span.AddEvent("processing completed")
}package main
import (
"context"
"net/http"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
func instrumentedHandler(w http.ResponseWriter, r *http.Request) {
// Extract context from incoming request
ctx := otel.GetTextMapPropagator().Extract(
r.Context(),
propagation.HeaderCarrier(r.Header),
)
tracer := otel.Tracer("http-server")
// Start server span
ctx, span := tracer.Start(
ctx,
r.URL.Path,
trace.WithSpanKind(trace.SpanKindServer),
trace.WithAttributes(
semconv.HTTPMethod(r.Method),
semconv.HTTPTarget(r.URL.Path),
semconv.NetHostName(r.Host),
semconv.HTTPScheme(r.URL.Scheme),
),
)
defer span.End()
// Add event
span.AddEvent("request.received")
// Process request
statusCode := processHTTPRequest(ctx, r)
// Set response attributes
span.SetAttributes(semconv.HTTPStatusCode(statusCode))
// Set status
if statusCode >= 400 {
span.SetStatus(codes.Error, "HTTP error")
} else {
span.SetStatus(codes.Ok, "success")
}
w.WriteHeader(statusCode)
}
func processHTTPRequest(ctx context.Context, r *http.Request) int {
tracer := otel.Tracer("http-server")
ctx, span := tracer.Start(ctx, "process-http-request")
defer span.End()
// Simulate processing
time.Sleep(50 * time.Millisecond)
return http.StatusOK
}The SDK respects the following environment variables:
# Batch span processor maximum queue size
OTEL_BSP_MAX_QUEUE_SIZE=2048
# Batch span processor schedule delay in milliseconds
OTEL_BSP_SCHEDULE_DELAY=5000
# Batch span processor export timeout in milliseconds
OTEL_BSP_EXPORT_TIMEOUT=30000
# Batch span processor maximum export batch size
OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512# Sampler to use (always_on, always_off, traceidratio, parentbased_always_on, etc.)
OTEL_TRACES_SAMPLER=parentbased_always_on
# Sampler argument (for traceidratio, this is the fraction)
OTEL_TRACES_SAMPLER_ARG=0.5# Maximum attribute value length (-1 for unlimited)
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT=-1
# Maximum number of attributes per span
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=128
# Maximum number of events per span
OTEL_SPAN_EVENT_COUNT_LIMIT=128
# Maximum number of links per span
OTEL_SPAN_LINK_COUNT_LIMIT=128
# Maximum number of attributes per event
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT=128
# Maximum number of attributes per link
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT=128// Good: Use batch processor
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
)
// Bad: Don't use simple processor in production
tp := sdktrace.NewTracerProvider(
sdktrace.WithSyncer(exporter), // Synchronous, slow
)tp := sdktrace.NewTracerProvider(/*...*/)
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := tp.Shutdown(ctx); err != nil {
log.Printf("Error shutting down: %v", err)
}
}()// For high-traffic services, use ratio-based sampling
sampler := sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1))
// For low-traffic services, sample everything
sampler := sdktrace.ParentBased(sdktrace.AlwaysSample())limits := sdktrace.SpanLimits{
AttributeValueLengthLimit: 1000, // Prevent huge attribute values
AttributeCountLimit: 128, // Reasonable number of attributes
EventCountLimit: 128, // Reasonable number of events
LinkCountLimit: 128, // Reasonable number of links
AttributePerEventCountLimit: 64, // Limit per event
AttributePerLinkCountLimit: 64, // Limit per link
}res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
semconv.DeploymentEnvironment("production"),
semconv.ServiceInstanceID(instanceID),
),
)Install with Tessl CLI
npx tessl i tessl/golang-go-opentelemetry-io--otel