SpanContext contains the immutable identifying information about a span. It includes the trace ID, span ID, trace flags, and trace state required for distributed trace propagation.
type SpanContext struct {
// contains unexported fields
}SpanContext is immutable and thread-safe. It represents the portion of a span that must be propagated to child spans and across process boundaries.
// SpanContextConfig contains mutable fields usable for constructing an
// immutable SpanContext.
type SpanContextConfig struct {
TraceID TraceID
SpanID SpanID
TraceFlags TraceFlags
TraceState TraceState
Remote bool
}
// NewSpanContext constructs a SpanContext using values from the provided
// SpanContextConfig.
func NewSpanContext(config SpanContextConfig) SpanContextimport "go.opentelemetry.io/otel/trace"
// Parse trace context from W3C headers
traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7")
sc := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
Remote: true,
})// TraceID returns the TraceID from the SpanContext.
func (sc SpanContext) TraceID() TraceID
// SpanID returns the SpanID from the SpanContext.
func (sc SpanContext) SpanID() SpanID
// TraceFlags returns the flags from the SpanContext.
func (sc SpanContext) TraceFlags() TraceFlags
// TraceState returns the TraceState from the SpanContext.
func (sc SpanContext) TraceState() TraceState
// IsValid reports whether the SpanContext is valid. A valid span context has a
// valid TraceID and SpanID.
func (sc SpanContext) IsValid() bool
// HasTraceID reports whether the SpanContext has a valid TraceID.
func (sc SpanContext) HasTraceID() bool
// HasSpanID reports whether the SpanContext has a valid SpanID.
func (sc SpanContext) HasSpanID() bool
// IsSampled reports whether the sampling bit is set in the SpanContext's
// TraceFlags.
func (sc SpanContext) IsSampled() bool
// IsRemote reports whether the SpanContext represents a remotely-created Span.
func (sc SpanContext) IsRemote() bool
// Equal reports whether two SpanContext values are equal.
func (sc SpanContext) Equal(other SpanContext) bool
// MarshalJSON implements a custom marshal function to encode a SpanContext.
func (sc SpanContext) MarshalJSON() ([]byte, error)// WithTraceID returns a new SpanContext with the TraceID replaced.
func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext
// WithSpanID returns a new SpanContext with the SpanID replaced.
func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext
// WithTraceFlags returns a new SpanContext with the TraceFlags replaced.
func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext
// WithTraceState returns a new SpanContext with the TraceState replaced.
func (sc SpanContext) WithTraceState(state TraceState) SpanContext
// WithRemote returns a copy of sc with the Remote property set to remote.
func (sc SpanContext) WithRemote(remote bool) SpanContext// SpanContextFromContext returns the current Span's SpanContext.
func SpanContextFromContext(ctx context.Context) SpanContextTraceID uniquely identifies a trace. It is a 16-byte array.
type TraceID [16]byte
// TraceIDFromHex returns a TraceID from a hex string if it is
// compliant with the W3C trace-context specification.
func TraceIDFromHex(h string) (TraceID, error)
// IsValid reports whether the trace TraceID is valid. A valid trace ID does
// not consist of zeros only.
func (t TraceID) IsValid() bool
// String returns the hex string representation form of a TraceID.
func (t TraceID) String() string
// MarshalJSON implements a custom marshal function to encode TraceID as a hex
// string.
func (t TraceID) MarshalJSON() ([]byte, error)// From hex string (W3C format: 32 hex characters)
traceID, err := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
if err != nil {
// handle error
}
// Check validity
if traceID.IsValid() {
fmt.Println("Trace ID:", traceID.String())
}
// Create from bytes
var tid trace.TraceID
copy(tid[:], []byte{0x4b, 0xf9, 0x2f, /* ... 16 bytes total */})SpanID uniquely identifies a span within a trace. It is an 8-byte array.
type SpanID [8]byte
// SpanIDFromHex returns a SpanID from a hex string if it is
// compliant with the w3c trace-context specification.
func SpanIDFromHex(h string) (SpanID, error)
// IsValid reports whether the SpanID is valid. A valid SpanID does not consist
// of zeros only.
func (s SpanID) IsValid() bool
// String returns the hex string representation form of a SpanID.
func (s SpanID) String() string
// MarshalJSON implements a custom marshal function to encode SpanID as a hex
// string.
func (s SpanID) MarshalJSON() ([]byte, error)// From hex string (W3C format: 16 hex characters)
spanID, err := trace.SpanIDFromHex("00f067aa0ba902b7")
if err != nil {
// handle error
}
// Check validity
if spanID.IsValid() {
fmt.Println("Span ID:", spanID.String())
}TraceFlags contains flags that can be set on a SpanContext. The most common flag is the sampling bit.
type TraceFlags byte
const (
// FlagsSampled is a bitmask with the sampled bit set. A SpanContext
// with the sampling bit set means the span is sampled.
FlagsSampled = TraceFlags(0x01)
)
// IsSampled reports whether the sampling bit is set in the TraceFlags.
func (tf TraceFlags) IsSampled() bool
// WithSampled sets the sampling bit in a new copy of the TraceFlags.
func (tf TraceFlags) WithSampled(sampled bool) TraceFlags
// String returns the hex string representation form of TraceFlags.
func (tf TraceFlags) String() string
// MarshalJSON implements a custom marshal function to encode TraceFlags as a
// hex string.
func (tf TraceFlags) MarshalJSON() ([]byte, error)// Check if sampled
var flags trace.TraceFlags = trace.FlagsSampled
if flags.IsSampled() {
fmt.Println("Span is sampled")
}
// Set sampling flag
flags = flags.WithSampled(true)
// Clear sampling flag
flags = flags.WithSampled(false)TraceState provides vendor-specific trace identification information across distributed tracing systems. It conforms to the W3C Trace Context specification.
type TraceState struct {
// contains unexported fields
}
// ParseTraceState attempts to decode a TraceState from the passed string.
// It returns an error if the input is invalid according to the W3C Trace
// Context specification.
func ParseTraceState(ts string) (TraceState, error)TraceState is immutable - all operations return a new TraceState. It maintains:
// Get returns the value paired with key from the corresponding TraceState
// list-member if it exists, otherwise an empty string is returned.
func (ts TraceState) Get(key string) string
// Insert adds a new list-member defined by the key/value pair to the
// TraceState. If a list-member already exists for the given key, that
// list-member's value is updated. The new or updated list-member is always
// moved to the beginning of the TraceState as specified by the W3C Trace
// Context specification.
//
// If key or value are invalid according to the W3C Trace Context specification
// an error is returned with the original TraceState.
//
// If adding a new list-member means the TraceState would have more members
// then is allowed, the new list-member will be inserted and the right-most
// list-member will be dropped in the returned TraceState.
func (ts TraceState) Insert(key, value string) (TraceState, error)
// Delete returns a copy of the TraceState with the list-member identified by
// key removed.
func (ts TraceState) Delete(key string) TraceState
// Len returns the number of list-members in the TraceState.
func (ts TraceState) Len() int
// String encodes the TraceState into a string compliant with the W3C Trace
// Context specification. The returned string will be invalid if the TraceState
// contains any invalid members.
func (ts TraceState) String() string
// Walk walks all key value pairs in the TraceState by calling f
// Iteration stops if f returns false.
func (ts TraceState) Walk(f func(key, value string) bool)
// MarshalJSON marshals the TraceState into JSON.
func (ts TraceState) MarshalJSON() ([]byte, error)// Parse from W3C header format
ts, err := trace.ParseTraceState("vendor1=value1,vendor2=value2")
if err != nil {
// handle error
}
// Get vendor-specific value
value := ts.Get("vendor1")
// Add or update vendor data
ts, err = ts.Insert("myvendor", "myvalue")
if err != nil {
// handle invalid key/value
}
// Delete vendor data
ts = ts.Delete("vendor2")
// Iterate over all members
ts.Walk(func(key, value string) bool {
fmt.Printf("%s=%s\n", key, value)
return true // continue iteration
})
// Encode to W3C format
headerValue := ts.String()
// Check size
if ts.Len() > 30 {
fmt.Println("Warning: TraceState approaching 32 member limit")
}Functions for managing SpanContext in Go's context.Context:
// ContextWithSpanContext returns a copy of parent with sc as the current Span.
// The Span implementation that wraps sc is non-recording and performs no
// operations other than to return sc as the SpanContext from the SpanContext
// method.
func ContextWithSpanContext(parent context.Context, sc SpanContext) context.Context
// ContextWithRemoteSpanContext returns a copy of parent with rsc set
// explicitly as a remote SpanContext and as the current Span. The Span
// implementation that wraps rsc is non-recording and performs no operations
// other than to return rsc as the SpanContext from the SpanContext method.
func ContextWithRemoteSpanContext(parent context.Context, rsc SpanContext) context.Contextimport (
"context"
"go.opentelemetry.io/otel/trace"
)
// Extract trace context from incoming request headers
traceID, _ := trace.TraceIDFromHex(req.Header.Get("traceparent-traceid"))
spanID, _ := trace.SpanIDFromHex(req.Header.Get("traceparent-spanid"))
flags := trace.FlagsSampled
sc := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: flags,
Remote: true,
})
// Inject into context
ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc)
// Create child span - automatically uses sc as parent
ctx, span := tracer.Start(ctx, "handleRequest")
defer span.End()package main
import (
"context"
"fmt"
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// HTTP server receiving traced request
func handleRequest(w http.ResponseWriter, r *http.Request) {
// Extract trace context from headers (simplified)
traceparent := r.Header.Get("traceparent")
sc := extractSpanContext(traceparent)
// Inject into context
ctx := trace.ContextWithRemoteSpanContext(r.Context(), sc)
// Start new span as child of remote span
tracer := otel.Tracer("http-server")
ctx, span := tracer.Start(ctx, "handleRequest",
trace.WithSpanKind(trace.SpanKindServer),
)
defer span.End()
// Process request...
processRequest(ctx)
// Get span context for response headers
sc = span.SpanContext()
w.Header().Set("traceresponse", sc.TraceID().String())
}
// HTTP client making traced request
func makeRequest(ctx context.Context, url string) error {
tracer := otel.Tracer("http-client")
ctx, span := tracer.Start(ctx, "makeRequest",
trace.WithSpanKind(trace.SpanKindClient),
)
defer span.End()
// Get span context for propagation
sc := span.SpanContext()
// Create request and inject trace context
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
injectSpanContext(req, sc)
// Make request...
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func extractSpanContext(traceparent string) trace.SpanContext {
// Parse W3C traceparent header
// Format: 00-<trace-id>-<span-id>-<flags>
// Simplified for example
return trace.SpanContext{}
}
func injectSpanContext(req *http.Request, sc trace.SpanContext) {
// Inject W3C traceparent header
traceparent := fmt.Sprintf("00-%s-%s-%s",
sc.TraceID().String(),
sc.SpanID().String(),
sc.TraceFlags().String(),
)
req.Header.Set("traceparent", traceparent)
// Inject tracestate if present
if sc.TraceState().Len() > 0 {
req.Header.Set("tracestate", sc.TraceState().String())
}
}
func processRequest(ctx context.Context) {
// Process with trace context
}Always validate trace context before using:
if !sc.IsValid() {
// Invalid context - create new trace
ctx, span := tracer.Start(ctx, "operation", trace.WithNewRoot())
defer span.End()
} else if !sc.HasTraceID() || !sc.HasSpanID() {
// Missing required IDs
return errors.New("invalid trace context")
}IsValid() before using extracted SpanContextWithRemote(true) or ContextWithRemoteSpanContext for spans from other services