Help us shape the future of Go! Take the 2026 Go Developer Survey

observability

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 37 Imported by: 0

Documentation

Overview

Package observability owns bounded operation observations and pluggable error reporting shared by framework kernels. It starts no independent HTTP server.

Index

Constants

View Source
const (
	MaxCollectors       = 32
	MaxCollectorSamples = 1024
	MaxMetricLabels     = 16
	MaxMetricNameBytes  = 128
	MaxMetricHelpBytes  = 256
	MaxMetricLabelBytes = 256
)

Collector bounds. A collector exceeding them has its remaining samples dropped and counted in foundry_collector_failures_total.

View Source
const DefaultTraceBatchSize = 64

DefaultTraceBatchSize bounds one TraceBatchExporter call when unset.

View Source
const PrometheusContentType = "text/plain; version=0.0.4; charset=utf-8"

Variables

This section is empty.

Functions

func DurationBuckets

func DurationBuckets() []time.Duration

DurationBuckets returns a copy of duration histogram bounds.

func Observe

func Observe(ctx context.Context, operation Operation, run func(context.Context) error) (err error)

Observe runs work synchronously with the recorder attached to ctx. A missing, closed or exhausted recorder does not prevent domain work; drop counters remain available on the recorder. Existing callback isolation still belongs to the caller. The deferred end also releases ownership on panic or Goexit. The returned error alone decides the outcome: work that returned nil stays Succeeded even if its context is cancelled or expires afterwards.

func WithContext

func WithContext(ctx context.Context, recorder *Recorder) context.Context

WithContext carries only the explicit application recorder. Kernels that intentionally discard parent context values may capture this specific runtime capability before creating their fresh execution contexts. A nil recorder clears an inherited recorder without altering other context values.

Types

type Collector

type Collector func(*MetricWriter)

Collector contributes point-in-time samples to each Prometheus exposition. It runs synchronously during the scrape inside callback isolation: read in-memory statistics only, never perform I/O or wait. Retaining the writer after returning has no effect.

type Config

type Config struct {
	MaxSeries           int
	MaxRecent           int
	MaxActive           int
	ErrorQueue          int
	ReporterConcurrency int
	ReporterTimeout     time.Duration
	// SampleTraces enables local trace sampling. TraceSampleRatio then selects
	// the sampled fraction of traces, deterministically by trace ID; zero
	// selects 1 (every trace). Disable sampling with SampleTraces=false.
	SampleTraces        bool
	TraceSampleRatio    float64
	TraceExporters      []TraceExporter
	TraceBatchExporters []TraceBatchExporter
	// TraceBatchSize bounds one batch exporter call; zero selects 64.
	TraceBatchSize   int
	TraceQueue       int
	TraceConcurrency int
	TraceTimeout     time.Duration
	Maintenance      *maintenance.Gate
}

Config configures recorder budgets, sampling, exporters, and the maintenance gate.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns default metric, error reporting, and trace collection budgets.

func (Config) Validate

func (c Config) Validate() error

Validate checks observability budgets, sampling ratio, and exporter declarations.

type Entry

type Entry struct {
	Operation Operation             `json:"operation"`
	Result    Result                `json:"result"`
	Started   time.Time             `json:"started"`
	Duration  time.Duration         `json:"duration_ns"`
	TraceID   tracing.TraceID       `json:"trace_id"`
	SpanID    tracing.SpanID        `json:"span_id"`
	ParentID  tracing.SpanID        `json:"parent_id"`
	RequestID attribution.RequestID `json:"request_id,omitempty"`
	Route     attribution.Route     `json:"route,omitzero"`
}

Entry is a bounded, payload-free operation record. Correlation IDs belong in recent observations and reports, never metric labels. Vendor tracestate is deliberately absent; raw request paths, IPs, identities and error chains are not retained by the recorder.

type ErrorReport

type ErrorReport struct {
	Entry      Entry            `json:"entry"`
	Diagnostic fault.Diagnostic `json:"diagnostic,omitzero"`
}

ErrorReport describes one failed operation. Diagnostic is a redacted summary (type names, framework fault notes and attributes, contained-panic frames); it never contains error text, payloads or credentials.

type Kind

type Kind string

Kind identifies an observed operation category.

const (
	HTTP          Kind = "http"
	OutboundHTTP  Kind = "outbound_http"
	Job           Kind = "job"
	Schedule      Kind = "schedule"
	Socket        Kind = "socket"
	SocketMessage Kind = "socket_message"
	CLI           Kind = "cli"
	Provider      Kind = "provider"
	Kernel        Kind = "kernel"
	Resource      Kind = "resource"
)

type Label

type Label struct {
	Name  string
	Value string
}

Label is one Prometheus label on a collector sample. Keep values bounded operational identities (connection names, roles), never user or request data.

type LogReporterConfig

type LogReporterConfig struct {
	Level              slog.Level
	Window             time.Duration
	MaxFingerprints    int
	DontReportFaults   []fault.Code
	DontReportStatuses []int
	DontReportOutcomes []Outcome
	// Clock measures suppression windows; nil uses the system clock.
	Clock clock.Clock
}

LogReporterConfig controls the built-in structured-log error reporter. Window suppresses repeated reports sharing one fingerprint (operation, outcome, status and redacted diagnostic); zero logs every report. The first report after a window closes carries the suppressed count. DontReport* filters skip reports whose diagnostic contains one of the fault codes, whose HTTP status or whose outcome is listed.

func DefaultLogReporterConfig

func DefaultLogReporterConfig() LogReporterConfig

DefaultLogReporterConfig returns default error severity and deduplication settings.

func (LogReporterConfig) Validate

func (c LogReporterConfig) Validate() error

Validate checks error log severity, deduplication bounds, and suppression filters.

type Metric

type Metric struct {
	Operation Operation                   `json:"operation"`
	Result    Result                      `json:"result"`
	Count     uint64                      `json:"count"`
	Seconds   float64                     `json:"seconds"`
	Buckets   [len(durationBounds)]uint64 `json:"buckets"`
}

Metric contains an operation's count, total duration, and cumulative duration buckets for a result.

type MetricSample

type MetricSample struct {
	Name, Help, Kind, Labels string
	Value                    float64
}

MetricSample is a structured gauge/counter sample from the existing runtime, process and registered collectors. Labels is the bounded, escaped Prometheus label representation; consumers must not interpret it as markup.

type MetricWriter

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

MetricWriter validates and escapes collector samples. Invalid samples are dropped and counted as collector failures instead of corrupting the output.

func (*MetricWriter) Counter

func (w *MetricWriter) Counter(name, help string, value float64, labels ...Label)

Counter emits a monotonically increasing total. Names end in "_total" and values are finite and non-negative.

func (*MetricWriter) Gauge

func (w *MetricWriter) Gauge(name, help string, value float64, labels ...Label)

Gauge emits a point-in-time value. Values must be finite.

type Name

type Name string

Name identifies a declared observed operation.

type OTLPConfig

type OTLPConfig struct {
	Endpoint    string
	ServiceName string
	Headers     []OTLPHeader
	Timeout     time.Duration
	Client      *http.Client
}

OTLPConfig configures the standard-library OTLP/HTTP JSON trace exporter. Endpoint is the complete traces URL, for example https://collector.example:4318/v1/traces. Timeout caps one request (zero selects 10 seconds); the recorder's TraceTimeout also bounds each call. A nil Client uses an owned client that never follows redirects, so configured headers are sent only to Endpoint.

func (OTLPConfig) Validate

func (c OTLPConfig) Validate() error

Validate checks the OTLP endpoint, service name, timeout, and bounded transport-safe headers.

type OTLPHeader

type OTLPHeader struct {
	Name  string
	Value secret.String
}

OTLPHeader is one explicitly configured export header, typically collector authentication. Values stay redacted in formatting and logs.

type Operation

type Operation struct {
	Kind Kind `json:"kind"`
	Name Name `json:"name"`
}

Operation pairs an observed operation kind with its declared name.

func (Operation) Validate

func (o Operation) Validate() error

Validate checks the operation kind and bounded semantic name.

type Outcome

type Outcome string

Outcome identifies an observed operation result category.

const (
	Succeeded Outcome = "succeeded"
	Failed    Outcome = "failed"
	Rejected  Outcome = "rejected"
	Cancelled Outcome = "cancelled"
	TimedOut  Outcome = "timed_out"
	Panicked  Outcome = "panicked"
)

func OutcomeFor

func OutcomeFor(err error) Outcome

OutcomeFor inspects errors inside owned isolation because custom Is/Unwrap methods can panic or call Goexit. Traversal is bounded: cyclic or excessively large/deep graphs report Failed. Methods must return and Is must perform a shallow comparison, as required by errors.Is. No error text is formatted.

type QuantileEstimate

type QuantileEstimate struct {
	Seconds             float64
	Available, Overflow bool
}

QuantileEstimate interpolates within the framework's cumulative histogram. Overflow means the estimate exceeds the largest finite bound. No samples means Available=false, never a fabricated zero duration.

func EstimateQuantile

func EstimateQuantile(buckets []uint64, count uint64, q float64) (QuantileEstimate, error)

EstimateQuantile interpolates a quantile from cumulative duration buckets. Empty observations are unavailable; values beyond the final bound report overflow.

type Recorder

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

Recorder is application-owned. New performs no I/O and starts no goroutines. Run owns reporter workers; Close rejects new spans and drains accepted spans and reporter callbacks. No global logger/provider is modified. Do not copy it. Span admission and completion use atomics and per-series shards; they never wait for snapshot formatting, collectors or exporter callbacks.

func FromContext

func FromContext(ctx context.Context) *Recorder

FromContext returns the context's recorder, or nil if absent or the context is nil.

func New

func New(config Config, reporters ...Reporter) (*Recorder, error)

New validates configuration and copies reporter and exporter slices into a new recorder.

func (*Recorder) Claim

func (r *Recorder) Claim() error

Claim reserves a fresh recorder for one application builder without starting resources. Failed/abandoned builders still consume this reservation; create a fresh recorder for another builder. This prevents accidental cross-app shutdown of the same recorder. Direct standalone Run needs no reservation.

func (*Recorder) Close

func (r *Recorder) Close(ctx context.Context) error

Close rejects new spans immediately. A deadline bounds the caller's wait; Done remains open while any span or accepted reporter callback still runs.

func (*Recorder) CollectSamples

func (r *Recorder) CollectSamples() []MetricSample

CollectSamples reuses the existing collectors without parsing text exposition or retaining operation/correlation history. Every result is owned by the caller.

func (*Recorder) Done

func (r *Recorder) Done() <-chan struct{}

Done returns the recorder's completion channel.

func (*Recorder) Gate

func (r *Recorder) Gate() *maintenance.Gate

Gate returns the recorder's maintenance gate, or nil for a nil recorder.

func (*Recorder) Ready

func (r *Recorder) Ready(ctx context.Context) error

Ready waits until reporter admission has started or the recorder has closed. It reports runtime startup, not application or dependency readiness.

func (*Recorder) RegisterCollector

func (r *Recorder) RegisterCollector(name Name, collector Collector) error

RegisterCollector adds a named collector to later expositions. Names are declared semantic identifiers and unique per recorder; at most MaxCollectors may be registered and registration closes when the recorder closes.

func (*Recorder) Run

func (r *Recorder) Run(ctx context.Context) error

Run owns reporter workers once. Parent cancellation seals admission and drains accepted spans/reports; deadlines remain cooperative and cannot abandon live callbacks. Run returns only after reporter work actually exits.

func (*Recorder) Snapshot

func (r *Recorder) Snapshot() Snapshot

Snapshot collects synchronized metric and recent-entry snapshots, sorting recent entries oldest first.

func (*Recorder) Start

func (r *Recorder) Start(ctx context.Context, operation Operation) (context.Context, *Span, error)

Start captures one operation and derives a trace child. A nil optional recorder preserves the input context and returns a nil span. Resource limits return an error and increment dropped-span accounting; instrumentation callers may continue their business operation without a span. An already-cancelled context is still observed: cancellation is the operation's outcome to report.

func (*Recorder) WritePrometheus

func (r *Recorder) WritePrometheus(writer io.Writer) error

WritePrometheus writes one owned snapshot in Prometheus 0.0.4 text format: operation metrics, recorder totals, Go runtime and process metrics, then samples from registered collectors. Metric labels are declared operation metadata; trace/request IDs, vendor state and payloads never become labels. Counts and cumulative histogram buckets come from the same snapshot. Registry and collector bounds cap the output size.

type Reporter

type Reporter func(context.Context, ErrorReport) error

Reporter receives owned metadata in a bounded worker. It must honor context cancellation and return only after its work ends. Recorder shutdown retains ownership of callbacks that ignore the deadline. Panics/Goexit are isolated.

func LogReporter

func LogReporter(logger *slog.Logger, config LogReporterConfig) (Reporter, error)

LogReporter logs each accepted error report once as a structured record with the redacted diagnostic and correlation IDs. It never logs error text or payloads. Duplicate suppression retains at most MaxFingerprints entries; the least recently reported fingerprint is forgotten first.

type Result

type Result struct {
	Outcome Outcome `json:"outcome"`
	Status  int     `json:"status,omitempty"`
}

Result contains operational metadata only. Status is an optional HTTP status; zero Outcome derives success/rejection/failure from it. Never put error text, URLs, payloads or user identifiers into an operation's declared metric name.

func (Result) Normalized

func (r Result) Normalized(kind Kind) (Result, error)

Normalized validates a result and derives an omitted outcome from status. Transport observers use the same classification as the shared recorder.

type Snapshot

type Snapshot struct {
	Closing             bool             `json:"closing"`
	Mode                maintenance.Mode `json:"mode"`
	Active              int              `json:"active"`
	Completed           uint64           `json:"completed"`
	Failures            uint64           `json:"failures"`
	DroppedSeries       uint64           `json:"dropped_series"`
	DroppedSpans        uint64           `json:"dropped_spans"`
	DroppedReports      uint64           `json:"dropped_reports"`
	ReporterFailures    uint64           `json:"reporter_failures"`
	PendingReports      int              `json:"pending_reports"`
	DroppedTraces       uint64           `json:"dropped_traces"`
	TraceExportFailures uint64           `json:"trace_export_failures"`
	PendingTraces       int              `json:"pending_traces"`
	CollectorFailures   uint64           `json:"collector_failures"`
	Metrics             []Metric         `json:"metrics"`
	Recent              []Entry          `json:"recent"`
}

Snapshot contains recorder lifecycle, queue, failure, metric, and recent operation data.

type Span

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

Span retains one recorder admission until End. End is concurrency-safe and idempotent; forgetting it retains shutdown ownership. Nil spans are harmless. Do not copy a Span.

func (*Span) End

func (s *Span) End(result Result)

End ends the span with the supplied result and an empty diagnostic.

func (*Span) EndWithDiagnostic

func (s *Span) EndWithDiagnostic(result Result, diagnostic fault.Diagnostic)

EndWithDiagnostic completes the span and attaches a redacted failure summary to the error report produced for a failed, panicked or timed-out result.

func (*Span) EndWithRoute

func (s *Span) EndWithRoute(result Result, diagnostic fault.Diagnostic, route attribution.Route)

EndWithRoute adds a validated declared route at completion, when HTTP matching has finished. Invalid metadata is omitted; it never changes the operation.

type TraceBatchExporter

type TraceBatchExporter func(context.Context, []Entry) error

TraceBatchExporter receives up to Config.TraceBatchSize sampled spans that were already queued together, in queue order. A trace worker never waits to fill a batch, so light traffic exports promptly in small batches. The slice is owned by the callback. One failed call counts one export failure.

func NewOTLPExporter

func NewOTLPExporter(config OTLPConfig) (TraceBatchExporter, error)

NewOTLPExporter returns a batch exporter posting sampled spans as OTLP/HTTP JSON. Spans carry operation metadata, typed trace/span/request IDs and the outcome only; no payloads, error text or vendor tracestate. Each call makes one attempt without retry; a non-2xx response is an export failure.

type TraceExporter

type TraceExporter func(context.Context, Entry) error

TraceExporter receives sampled completed spans without payloads or vendor state. Exporters use their own bounded queue so trace traffic cannot evict error reports. Each callback must own its I/O until it returns.

Jump to

Keyboard shortcuts

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