The OpenTelemetry Go tracing API provides the interfaces and types for creating and managing distributed traces. It follows the OpenTelemetry specification and enables tracking of requests as they flow through distributed systems.
import "go.opentelemetry.io/otel/trace"The tracing API is based on three main concepts:
package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
)
func main() {
// Get a tracer
tracer := otel.Tracer("my-service")
// Create a context
ctx := context.Background()
// Start a span
ctx, span := tracer.Start(ctx, "operation")
defer span.End()
// Add attributes
span.SetAttributes(
attribute.String("key", "value"),
attribute.Int("count", 10),
)
// Record an event
span.AddEvent("processing started")
// Do work...
// Record completion
span.SetStatus(codes.Ok, "completed successfully")
}The TracerProvider is the entry point for creating Tracers.
type TracerProvider interface {
// 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. Additionally, instrumentation
// libraries may sometimes use traces to communicate different domains of
// workflow data (i.e. using spans to communicate workflow events only).
//
// 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
}import (
"go.opentelemetry.io/otel"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
// Set up global tracer provider
func setupTracing() {
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource),
)
otel.SetTracerProvider(tp)
}
// Access global tracer provider
func getTracerProvider() trace.TracerProvider {
return otel.GetTracerProvider()
}Options for configuring a Tracer.
type TracerOption interface {
// Has unexported methods.
}Available Options:
// WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) TracerOption
// WithSchemaURL sets the schema URL for the Tracer.
func WithSchemaURL(schemaURL string) TracerOption
// WithInstrumentationAttributes sets the instrumentation attributes.
// The passed attributes will be de-duplicated.
func WithInstrumentationAttributes(attr ...attribute.KeyValue) TracerOptionExample:
tracer := provider.Tracer(
"github.com/myorg/myapp/database",
trace.WithInstrumentationVersion("1.0.0"),
trace.WithSchemaURL("https://opentelemetry.io/schemas/1.20.0"),
trace.WithInstrumentationAttributes(
attribute.String("db.system", "postgresql"),
),
)The Tracer is responsible for creating Spans.
type Tracer interface {
// 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)
}// Basic span creation
ctx, span := tracer.Start(ctx, "operation-name")
defer span.End()
// With options
ctx, span := tracer.Start(
ctx,
"database-query",
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
attribute.String("db.system", "postgresql"),
attribute.String("db.name", "users"),
),
)
defer span.End()The Span interface represents a single operation within a trace.
type Span interface {
// End completes the Span. The Span is considered complete and ready to be
// delivered through the rest of the telemetry pipeline after this method
// is called. Therefore, updates to the Span are not allowed after this
// method has been called.
End(options ...SpanEndOption)
// AddEvent adds an event with the provided name and options.
AddEvent(name string, options ...EventOption)
// AddLink adds a link.
// Adding links at span creation using WithLinks is preferred to calling AddLink
// later, for contexts that are available during span creation, because head
// sampling decisions can only consider information present during span creation.
AddLink(link Link)
// IsRecording returns the recording state of the Span. It will return
// true if the Span is active and events can be recorded.
IsRecording() bool
// RecordError will record err as an exception span event for this span. An
// additional call to SetStatus is required if the Status of the Span should
// be set to Error, as this method does not change the Span status. If this
// span is not being recorded or err is nil then this method does nothing.
RecordError(err error, options ...EventOption)
// SpanContext returns the SpanContext of the Span. The returned SpanContext
// is usable even after the End method has been called for the Span.
SpanContext() SpanContext
// SetStatus sets the status of the Span in the form of a code and a
// description, provided the status hasn't already been set to a higher
// value before (OK > Error > Unset). The description is only included in a
// status when the code is for an error.
SetStatus(code codes.Code, description string)
// SetName sets the Span name.
SetName(name string)
// SetAttributes sets kv as attributes of the Span. If a key from kv
// already exists for an attribute of the Span it will be overwritten with
// the value contained in kv.
SetAttributes(kv ...attribute.KeyValue)
// TracerProvider returns a TracerProvider that can be used to generate
// additional Spans on the same telemetry pipeline as the current Span.
TracerProvider() TracerProvider
}import (
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
// Set individual attributes
span.SetAttributes(
attribute.String("user.id", "12345"),
attribute.Int("retry.count", 3),
attribute.Bool("cache.hit", true),
)
// Use semantic conventions
span.SetAttributes(
semconv.HTTPMethod("GET"),
semconv.HTTPRoute("/api/users/:id"),
semconv.HTTPStatusCode(200),
)// Simple event
span.AddEvent("cache miss")
// Event with attributes
span.AddEvent("processing",
trace.WithAttributes(
attribute.Int("items.processed", 100),
attribute.String("status", "success"),
),
)
// Event with timestamp
span.AddEvent("checkpoint",
trace.WithTimestamp(time.Now()),
trace.WithAttributes(
attribute.String("checkpoint.name", "validation-complete"),
),
)import "go.opentelemetry.io/otel/codes"
// Record error with default options
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
}
// Record error with stack trace
if err != nil {
span.RecordError(err,
trace.WithStackTrace(true),
trace.WithAttributes(
attribute.String("error.context", "database query"),
),
)
span.SetStatus(codes.Error, "database operation failed")
}import "go.opentelemetry.io/otel/codes"
// Success
span.SetStatus(codes.Ok, "operation completed")
// Error
span.SetStatus(codes.Error, "validation failed")
// Unset (default)
span.SetStatus(codes.Unset, "")// Initial name
ctx, span := tracer.Start(ctx, "initial-name")
defer span.End()
// Update after more information is known
if route := determineRoute(); route != "" {
span.SetName(route)
}if span.IsRecording() {
// Only compute expensive attributes if span is being recorded
expensiveData := computeExpensiveData()
span.SetAttributes(attribute.String("expensive.data", expensiveData))
}The SpanContext contains the trace and span identifiers, trace flags, and trace state.
type SpanContext struct {
// Has unexported fields.
}type SpanContextConfig struct {
TraceID TraceID
SpanID SpanID
TraceFlags TraceFlags
TraceState TraceState
Remote bool
}
func NewSpanContext(config SpanContextConfig) SpanContextExample:
traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7")
sc := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
Remote: true,
})// Validation
func (sc SpanContext) IsValid() bool
func (sc SpanContext) HasTraceID() bool
func (sc SpanContext) HasSpanID() bool
// Accessors
func (sc SpanContext) TraceID() TraceID
func (sc SpanContext) SpanID() SpanID
func (sc SpanContext) TraceFlags() TraceFlags
func (sc SpanContext) TraceState() TraceState
// Status checks
func (sc SpanContext) IsRemote() bool
func (sc SpanContext) IsSampled() bool
// Comparison
func (sc SpanContext) Equal(other SpanContext) bool
// Modification (returns new SpanContext)
func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext
func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext
func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext
func (sc SpanContext) WithTraceState(state TraceState) SpanContext
func (sc SpanContext) WithRemote(remote bool) SpanContext
// Serialization
func (sc SpanContext) MarshalJSON() ([]byte, error)Example Usage:
// Get span context from span
sc := span.SpanContext()
// Check if valid
if sc.IsValid() {
fmt.Printf("Trace ID: %s\n", sc.TraceID())
fmt.Printf("Span ID: %s\n", sc.SpanID())
fmt.Printf("Is Sampled: %v\n", sc.IsSampled())
}
// Create modified context
newSc := sc.WithTraceFlags(trace.FlagsSampled)// Get SpanContext from context.Context
func SpanContextFromContext(ctx context.Context) SpanContext
// Get Span from context.Context
func SpanFromContext(ctx context.Context) SpanExample:
// From context
sc := trace.SpanContextFromContext(ctx)
if sc.IsValid() {
fmt.Println("Found valid span context")
}
// Get span and then context
span := trace.SpanFromContext(ctx)
sc = span.SpanContext()A 16-byte identifier for a trace.
type TraceID [16]byte
// Create from hex string
func TraceIDFromHex(h string) (TraceID, error)
// Methods
func (t TraceID) IsValid() bool
func (t TraceID) String() string
func (t TraceID) MarshalJSON() ([]byte, error)Example:
// Parse from hex
traceID, err := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
if err != nil {
log.Fatal(err)
}
// Validate
if traceID.IsValid() {
fmt.Println("Valid trace ID:", traceID.String())
}An 8-byte identifier for a span.
type SpanID [8]byte
// Create from hex string
func SpanIDFromHex(h string) (SpanID, error)
// Methods
func (s SpanID) IsValid() bool
func (s SpanID) String() string
func (s SpanID) MarshalJSON() ([]byte, error)Example:
// Parse from hex
spanID, err := trace.SpanIDFromHex("00f067aa0ba902b7")
if err != nil {
log.Fatal(err)
}
// Validate
if spanID.IsValid() {
fmt.Println("Valid span ID:", spanID.String())
}Flags that can be set on a SpanContext.
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)
)
// Methods
func (tf TraceFlags) IsSampled() bool
func (tf TraceFlags) WithSampled(sampled bool) TraceFlags
func (tf TraceFlags) String() string
func (tf TraceFlags) MarshalJSON() ([]byte, error)Example:
// Check if sampled
if sc.TraceFlags().IsSampled() {
fmt.Println("Span is sampled")
}
// Set sampled flag
flags := trace.TraceFlags(0).WithSampled(true)
sc = sc.WithTraceFlags(flags)Vendor-specific trace identification information across distributed tracing systems.
type TraceState struct {
// Has unexported fields.
}
// Parse from string
func ParseTraceState(ts string) (TraceState, error)
// Methods
func (ts TraceState) Get(key string) string
func (ts TraceState) Insert(key, value string) (TraceState, error)
func (ts TraceState) Delete(key string) TraceState
func (ts TraceState) Len() int
func (ts TraceState) String() string
func (ts TraceState) Walk(f func(key, value string) bool)
func (ts TraceState) MarshalJSON() ([]byte, error)Example:
// Parse tracestate header
ts, err := trace.ParseTraceState("vendor1=value1,vendor2=value2")
if err != nil {
log.Fatal(err)
}
// Get value
value := ts.Get("vendor1")
// Insert new value
ts, err = ts.Insert("myvendor", "myvalue")
if err != nil {
log.Fatal(err)
}
// Delete value
ts = ts.Delete("vendor2")
// Walk all entries
ts.Walk(func(key, value string) bool {
fmt.Printf("%s=%s\n", key, value)
return true // continue
})The role a Span plays in a trace.
type SpanKind int
const (
// SpanKindUnspecified is an unspecified SpanKind and is not a valid
// SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal
// if it is received.
SpanKindUnspecified SpanKind = 0
// SpanKindInternal is a SpanKind for a Span that represents an internal
// operation within an application.
SpanKindInternal SpanKind = 1
// SpanKindServer is a SpanKind for a Span that represents the operation
// of handling a request from a client.
SpanKindServer SpanKind = 2
// SpanKindClient is a SpanKind for a Span that represents the operation
// of client making a request to a server.
SpanKindClient SpanKind = 3
// SpanKindProducer is a SpanKind for a Span that represents the operation
// of a producer sending a message to a message broker. Unlike
// SpanKindClient and SpanKindServer, there is often no direct
// relationship between this kind of Span and a SpanKindConsumer kind. A
// SpanKindProducer Span will end once the message is accepted by the
// message broker which might not overlap with the processing of that
// message.
SpanKindProducer SpanKind = 4
// SpanKindConsumer is a SpanKind for a Span that represents the operation
// of a consumer receiving a message from a message broker. Like
// SpanKindProducer Spans, there is often no direct relationship between
// this Span and the Span that produced the message.
SpanKindConsumer SpanKind = 5
)
func ValidateSpanKind(spanKind SpanKind) SpanKind
func (sk SpanKind) String() stringUsage:
// HTTP server
ctx, span := tracer.Start(
ctx,
"HTTP GET /users",
trace.WithSpanKind(trace.SpanKindServer),
)
// HTTP client
ctx, span := tracer.Start(
ctx,
"GET https://api.example.com",
trace.WithSpanKind(trace.SpanKindClient),
)
// Message producer
ctx, span := tracer.Start(
ctx,
"publish message",
trace.WithSpanKind(trace.SpanKindProducer),
)
// Message consumer
ctx, span := tracer.Start(
ctx,
"process message",
trace.WithSpanKind(trace.SpanKindConsumer),
)
// Internal operation
ctx, span := tracer.Start(
ctx,
"calculate-score",
trace.WithSpanKind(trace.SpanKindInternal),
)Options for span creation.
type SpanStartOption interface {
// Has unexported methods.
}Available Start Options:
// WithSpanKind sets the SpanKind of a Span.
func WithSpanKind(kind SpanKind) SpanStartOption
// WithAttributes adds the attributes related to a span life-cycle event.
func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption
// WithTimestamp sets the time of a Span or Event life-cycle moment.
func WithTimestamp(t time.Time) SpanEventOption
// WithLinks adds links to a Span. The links are added to the existing Span
// links, i.e. this does not overwrite. Links with invalid span context are
// ignored.
func WithLinks(links ...Link) SpanStartOption
// WithNewRoot specifies that the Span should be treated as a root Span.
// Any existing parent span context will be ignored when defining the Span's
// trace identifiers.
func WithNewRoot() SpanStartOptionOptions for ending a span.
type SpanEndOption interface {
// Has unexported methods.
}Available End Options:
// WithTimestamp sets the time of a Span or Event life-cycle moment.
func WithTimestamp(t time.Time) SpanEventOption
// WithStackTrace sets the flag to capture the error with stack trace.
func WithStackTrace(b bool) SpanEndEventOptionOptions for span events.
type EventOption interface {
// Has unexported methods.
}Available Event Options:
// WithAttributes adds the attributes related to a span life-cycle event.
func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption
// WithTimestamp sets the time of a Span or Event life-cycle moment.
func WithTimestamp(t time.Time) SpanEventOption
// WithStackTrace sets the flag to capture the error with stack trace.
func WithStackTrace(b bool) SpanEndEventOptionLinks represent relationships between spans.
type Link struct {
// SpanContext of the linked Span.
SpanContext SpanContext
// Attributes describe the aspects of the link.
Attributes []attribute.KeyValue
}
// Create link from context
func LinkFromContext(ctx context.Context, attrs ...attribute.KeyValue) LinkExample:
// Create link from another span
link := trace.Link{
SpanContext: otherSpan.SpanContext(),
Attributes: []attribute.KeyValue{
attribute.String("link.type", "dependency"),
},
}
// Create span with link
ctx, span := tracer.Start(
ctx,
"batch-operation",
trace.WithLinks(link),
)
// Add link during span lifetime
span.AddLink(trace.Link{
SpanContext: anotherSpan.SpanContext(),
Attributes: []attribute.KeyValue{
attribute.String("link.reason", "causedBy"),
},
})// ContextWithSpan returns a copy of parent with span set as the current Span.
func ContextWithSpan(parent context.Context, span Span) 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.ContextExample:
// Store span in context
ctx = trace.ContextWithSpan(ctx, span)
// Store remote span context
remoteSpan := trace.ContextWithRemoteSpanContext(ctx, remoteSpanContext)
// Create non-recording span with context
nonRecording := trace.ContextWithSpanContext(ctx, spanContext)import (
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
func instrumentedHandler(w http.ResponseWriter, r *http.Request) {
tracer := otel.Tracer("http-server")
// Extract context from incoming request
ctx := otel.GetTextMapPropagator().Extract(
r.Context(),
propagation.HeaderCarrier(r.Header),
)
// Start server span
ctx, span := tracer.Start(
ctx,
r.URL.Path,
trace.WithSpanKind(trace.SpanKindServer),
trace.WithAttributes(
semconv.HTTPMethod(r.Method),
semconv.HTTPScheme(r.URL.Scheme),
semconv.HTTPTarget(r.URL.Path),
semconv.NetHostName(r.Host),
),
)
defer span.End()
// Add event for request start
span.AddEvent("request.started")
// Process request
statusCode := processRequest(ctx, r)
// Set response attributes
span.SetAttributes(
semconv.HTTPStatusCode(statusCode),
)
// Set status based on response
if statusCode >= 400 && statusCode < 500 {
span.SetStatus(codes.Error, "client error")
} else if statusCode >= 500 {
span.SetStatus(codes.Error, "server error")
} else {
span.SetStatus(codes.Ok, "success")
}
w.WriteHeader(statusCode)
}func makeHTTPRequest(ctx context.Context, url string) (*http.Response, error) {
tracer := otel.Tracer("http-client")
// Start client span
ctx, span := tracer.Start(
ctx,
"HTTP GET",
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
semconv.HTTPMethod("GET"),
semconv.HTTPURL(url),
),
)
defer span.End()
// Create request with context
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "failed to create request")
return nil, err
}
// Inject context into request headers
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
// Make request
resp, err := http.DefaultClient.Do(req)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "request failed")
return nil, err
}
// Record response
span.SetAttributes(semconv.HTTPStatusCode(resp.StatusCode))
if resp.StatusCode >= 400 {
span.SetStatus(codes.Error, "HTTP error")
} else {
span.SetStatus(codes.Ok, "success")
}
return resp, nil
}func queryDatabase(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
tracer := otel.Tracer("database")
// Start database span
ctx, span := tracer.Start(
ctx,
"db.query",
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
semconv.DBSystem("postgresql"),
semconv.DBStatement(query),
semconv.DBName("mydb"),
semconv.NetPeerName("db.example.com"),
semconv.NetPeerPort(5432),
),
)
defer span.End()
// Execute query
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
span.RecordError(err, trace.WithAttributes(
attribute.String("error.type", "query_failed"),
))
span.SetStatus(codes.Error, "database query failed")
return nil, err
}
span.SetStatus(codes.Ok, "query successful")
return rows, nil
}func processBatch(ctx context.Context, items []Item) {
tracer := otel.Tracer("batch-processor")
// Create parent span
ctx, parentSpan := tracer.Start(ctx, "batch-processing")
defer parentSpan.End()
// Create links to all item spans
var links []trace.Link
for _, item := range items {
// Each item has its own span context
links = append(links, trace.Link{
SpanContext: item.SpanContext,
Attributes: []attribute.KeyValue{
attribute.String("item.id", item.ID),
},
})
}
// Create batch span with links
_, batchSpan := tracer.Start(
ctx,
"process-batch",
trace.WithLinks(links...),
trace.WithAttributes(
attribute.Int("batch.size", len(items)),
),
)
defer batchSpan.End()
// Process items
for _, item := range items {
processItem(ctx, item)
}
batchSpan.SetStatus(codes.Ok, "batch completed")
}func processUntrustedRequest(ctx context.Context) {
tracer := otel.Tracer("security")
// Create new root span, ignoring any parent context
// This is useful for security boundaries
ctx, span := tracer.Start(
ctx,
"untrusted-request",
trace.WithNewRoot(),
trace.WithAttributes(
attribute.String("security.boundary", "external"),
),
)
defer span.End()
// Process request in new trace
// ...
}The tracing API includes configuration types that group options for various operations. These are typically used internally by the SDK but are exposed in the public API for advanced use cases.
Groups options for span events.
type EventConfig struct {
// Has unexported fields.
}
// NewEventConfig creates an EventConfig from EventOptions
func NewEventConfig(options ...EventOption) EventConfig
// Accessor methods
func (cfg *EventConfig) Attributes() []attribute.KeyValue
func (cfg *EventConfig) StackTrace() bool
func (cfg *EventConfig) Timestamp() time.TimeNote: Most users don't need to create EventConfig directly. It's used internally when calling span.AddEvent() or span.RecordError().
Groups options for span creation and completion.
type SpanConfig struct {
// Has unexported fields.
}
// NewSpanStartConfig creates a SpanConfig from SpanStartOptions
func NewSpanStartConfig(options ...SpanStartOption) SpanConfig
// NewSpanEndConfig creates a SpanConfig from SpanEndOptions
func NewSpanEndConfig(options ...SpanEndOption) SpanConfig
// Accessor methods
func (cfg *SpanConfig) Attributes() []attribute.KeyValue
func (cfg *SpanConfig) Links() []Link
func (cfg *SpanConfig) NewRoot() bool
func (cfg *SpanConfig) SpanKind() SpanKind
func (cfg *SpanConfig) StackTrace() bool
func (cfg *SpanConfig) Timestamp() time.TimeNote: Most users don't need to create SpanConfig directly. It's used internally when calling tracer.Start() and span.End().
Groups options for tracer configuration.
type TracerConfig struct {
// Has unexported fields.
}
// NewTracerConfig creates a TracerConfig from TracerOptions
func NewTracerConfig(options ...TracerOption) TracerConfig
// Accessor methods
func (t *TracerConfig) InstrumentationAttributes() attribute.Set
func (t *TracerConfig) InstrumentationVersion() string
func (t *TracerConfig) SchemaURL() stringNote: Most users don't need to create TracerConfig directly. It's used internally when calling TracerProvider.Tracer().
ctx, span := tracer.Start(ctx, "operation")
defer span.End()
// Work hereif span.IsRecording() {
expensiveAttr := computeExpensiveAttribute()
span.SetAttributes(attribute.String("expensive", expensiveAttr))
}import semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
span.SetAttributes(
semconv.HTTPMethod("GET"),
semconv.HTTPRoute("/api/users/:id"),
semconv.HTTPStatusCode(200),
)if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
}func handler(ctx context.Context) {
ctx, span := tracer.Start(ctx, "handler")
defer span.End()
// Pass context to downstream calls
result := downstream(ctx)
// ...
}
func downstream(ctx context.Context) Result {
ctx, span := tracer.Start(ctx, "downstream")
defer span.End()
// Work here
return result
}// Server handling requests
trace.WithSpanKind(trace.SpanKindServer)
// Client making requests
trace.WithSpanKind(trace.SpanKindClient)
// Producer sending messages
trace.WithSpanKind(trace.SpanKindProducer)
// Consumer receiving messages
trace.WithSpanKind(trace.SpanKindConsumer)
// Internal operations
trace.WithSpanKind(trace.SpanKindInternal)// Good: Describes the operation
tracer.Start(ctx, "GET /api/users/:id")
tracer.Start(ctx, "database.query")
tracer.Start(ctx, "cache.get")
// Bad: Too generic
tracer.Start(ctx, "operation")
tracer.Start(ctx, "span")span.AddEvent("validation.started")
// validation logic
span.AddEvent("validation.completed")
span.AddEvent("cache.miss")
// fetch from database
span.AddEvent("data.fetched")For testing or when telemetry is disabled:
import "go.opentelemetry.io/otel/trace/noop"
// Create no-op tracer provider
tp := noop.NewTracerProvider()
// Use as normal - all operations are no-ops
tracer := tp.Tracer("test")
ctx, span := tracer.Start(ctx, "test")
defer span.End()