trace

package
v0.1.23 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package trace produces distributed traces for messages moving through a channel, and exports them over OTLP/HTTP with the JSON encoding.

Why this exists rather than the OpenTelemetry SDK: the SDK plus an OTLP exporter pulls in roughly sixty modules and adds about as much to the binary as the whole of the rest of this program. What is actually needed here is narrow - W3C trace context propagation, a span model, head sampling, and a batched HTTP POST - and OTLP/JSON is a documented stable encoding that can be produced with the standard library alone. Same trade as choosing lib/pq over pgx: take the small thing when only part of the big thing is wanted.

The consequence worth stating: there is no auto-instrumentation and no vendor-specific exporter. Spans are the ones this program creates deliberately, and they go to any OTLP endpoint - a collector, Jaeger, Grafana Tempo, Honeycomb, Datadog's OTLP ingest.

The property that matters more than the spans

A message frequently arrives carrying a traceparent header from the system that sent it, and leaves toward a system that will record one of its own. Continuing that trace rather than starting a fresh one is what makes a single view of "the EHR sent this, the engine transformed it, the registry rejected it" possible at all. Interface problems are almost always reported as "we sent it and nothing happened", and that question is unanswerable without an identifier that survives the hop.

Tracing must never affect delivery

Every operation here is best-effort. Spans are dropped when the buffer is full, export failures are counted and never retried into the message path, and no call blocks on the network. A dropped span is an inconvenience. A message delayed or lost because the tracing backend was slow is a clinical incident, so the trade is never in question.

Index

Constants

View Source
const TraceparentHeader = "traceparent"

TraceparentHeader is the W3C header name.

Variables

This section is empty.

Functions

func Traceparent

func Traceparent(sc SpanContext) string

Traceparent formats a span context as a W3C traceparent value.

Returns "" for an invalid context rather than emitting a malformed header, because a malformed traceparent is worse than none: a compliant receiver must discard it and start a new trace, and the resulting broken link is far harder to diagnose than an absent one.

func WithRemote

func WithRemote(ctx context.Context, sc SpanContext) context.Context

WithRemote attaches an inbound span context to a context.

Separate from WithSpan because they mean different things: WithSpan carries a span this process created and is a parent, while WithRemote carries what another system told us and is a parent only if we have nothing closer. A source parses the header, and the channel decides what to do with it - which keeps header formats out of the engine and channel semantics out of the transports.

func WithSpan

func WithSpan(ctx context.Context, s *Span) context.Context

WithSpan returns a context carrying the span, so functions further down can parent their own work without the span being threaded through every signature.

Types

type Config

type Config struct {
	// Endpoint is the OTLP/HTTP base URL, for example http://localhost:4318.
	// Empty disables tracing entirely.
	Endpoint string
	// ServiceName identifies this instance in the trace backend.
	ServiceName string
	// ServiceVersion is reported as a resource attribute.
	ServiceVersion string
	// Instance distinguishes replicas. Without it, spans from three pods are
	// indistinguishable, which defeats the point of tracing a scaled deployment.
	Instance string
	// SampleRatio is the fraction of new traces recorded, 0 to 1.
	//
	// Head sampling, decided once at the root and propagated, because the
	// alternative - deciding per span - produces traces with holes in them.
	SampleRatio float64
	// Headers are added to every export request, for backends that authenticate
	// with one.
	Headers map[string]string
	// Timeout bounds a single export request.
	Timeout time.Duration
	// MaxQueue bounds spans held awaiting export.
	MaxQueue int
	// BatchSize is how many spans go in one request.
	BatchSize int
	// FlushInterval is how often a partial batch is sent anyway, so a quiet
	// system still reports promptly rather than holding spans until the batch
	// fills.
	FlushInterval time.Duration
}

Config configures tracing.

func (*Config) Validate

func (c *Config) Validate() error

Validate reports configuration that cannot work.

func (*Config) Warnings

func (c *Config) Warnings() []string

Warnings returns advice that does not prevent startup.

type Kind

type Kind int

Kind describes a span's role, using the OTLP numeric values directly so no mapping table is needed at export.

const (
	KindInternal Kind = 1
	KindServer   Kind = 2
	KindClient   Kind = 3
	KindProducer Kind = 4
	KindConsumer Kind = 5
)

type Span

type Span struct {
	// contains filtered or unexported fields
}

Span is one timed operation.

A nil *Span is valid and every method on it is a no-op, so instrumentation can be written without checking whether tracing is enabled. That matters because the alternative is an `if tracer != nil` around every call site, which is where somebody eventually forgets and panics on a message.

func FromContext

func FromContext(ctx context.Context) *Span

FromContext returns the span in the context, or nil.

nil is a working span, so callers do not need to check.

func (*Span) Context

func (s *Span) Context() SpanContext

Context returns the span's own context, for propagation.

func (*Span) Duration

func (s *Span) Duration() time.Duration

Duration reports how long the span took, or how long it has been open.

func (*Span) End

func (s *Span) End()

End closes the span and hands it to the exporter.

Safe to call twice; the second call does nothing. That matters because the natural way to write this is a defer plus an explicit End on an early return, and a double-End should not produce a duplicate span.

func (*Span) SetBool

func (s *Span) SetBool(key string, value bool)

SetBool records a boolean attribute.

func (*Span) SetError

func (s *Span) SetError(err error)

SetError marks the span failed and records the message.

The error text is clipped and comes from this program's own errors, not from message content.

func (*Span) SetInt

func (s *Span) SetInt(key string, value int64)

SetInt records an integer attribute.

func (*Span) SetOK

func (s *Span) SetOK()

SetOK marks the span explicitly successful.

func (*Span) SetString

func (s *Span) SetString(key, value string)

SetString records a string attribute.

Deliberately no method taking arbitrary values: an attribute set from message content is how patient data ends up in a third-party observability platform that has no agreement covering it. Callers must pass something they have decided is safe, and the type signature is the reminder.

type SpanContext

type SpanContext struct {
	TraceID TraceID
	SpanID  SpanID
	// Sampled carries the sampling decision onward. Once a trace is sampled every
	// participant should sample it, or the resulting trace has holes exactly
	// where the interesting hop was.
	Sampled bool
	// Remote marks a context parsed from an inbound header rather than created
	// here, which is worth knowing when deciding whether to trust its sampling
	// decision.
	Remote bool
}

SpanContext is the part of a span that crosses process boundaries.

func ParseTraceparent

func ParseTraceparent(v string) SpanContext

ParseTraceparent reads a W3C traceparent value.

Unparseable input yields an invalid context and no error, deliberately. A caller's only sensible response to a malformed inbound header is to start a fresh trace, and making that an error invites somebody to reject the message over a header problem.

func RemoteFromContext

func RemoteFromContext(ctx context.Context) SpanContext

RemoteFromContext returns the inbound span context, if a source recorded one.

func (SpanContext) Valid

func (sc SpanContext) Valid() bool

Valid reports whether the context can parent a span.

type SpanID

type SpanID [8]byte

SpanID is an 8-byte identifier for a single span.

func (SpanID) IsZero

func (s SpanID) IsZero() bool

func (SpanID) String

func (s SpanID) String() string

type Stats

type Stats struct {
	Exported int64 `json:"exported"`
	Dropped  int64 `json:"dropped"`
	Failures int64 `json:"failures"`
	Queued   int   `json:"queued"`
}

Stats reports export counters, so tracing's own health is visible. A tracing system that silently stops working is worse than none, because decisions get made on the assumption that an absent span means an absent operation.

type TraceID

type TraceID [16]byte

TraceID is a 16-byte identifier shared by every span in one trace.

func (TraceID) IsZero

func (t TraceID) IsZero() bool

IsZero reports whether the ID is unset. An all-zero ID is invalid per the W3C specification, which is what makes it usable as a sentinel.

func (TraceID) String

func (t TraceID) String() string

type Tracer

type Tracer struct {
	// contains filtered or unexported fields
}

Tracer creates spans and exports them.

A nil *Tracer is usable and produces nil spans, so a caller never needs to know whether tracing is configured.

func New

func New(cfg Config, log *slog.Logger) (*Tracer, error)

New starts a tracer. A Config with no Endpoint returns nil, which is a working no-op tracer.

func (*Tracer) Close

func (t *Tracer) Close(ctx context.Context) error

Close flushes what it can and stops.

func (*Tracer) Start

func (t *Tracer) Start(ctx context.Context, name string, kind Kind, remote SpanContext) (context.Context, *Span)

Start begins a span.

The parent is taken from the context when one is present. Otherwise a new trace begins, unless remote carries a valid inbound context - which is the case that makes a trace span two systems.

func (*Tracer) Stats

func (t *Tracer) Stats() Stats

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL