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),
),
)