obs

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package obs is Console's instrumentation seam: a small, stdlib-only API for recording counters, gauges, histograms, and spans about Console's own behavior (application performance monitoring, see docs/design/apm.md).

Instrumented code depends only on the Recorder interface. Nop is the zero-cost default used when metrics are disabled; Memory is an in-process registry that renders the Prometheus text exposition format. The concrete Recorder is chosen in internal/app (the composition root) and exposed as App.Obs, which is always non-nil.

Label cardinality

Every distinct label-value combination creates a new time series that lives for the life of the process, so label values must come from a small, bounded set. Use registered route patterns ("GET /api/flags/{key}"), HTTP methods, status classes, operation names, and provider kinds. Never use raw URL paths, flag keys, subject keys, component IDs, request IDs, error strings, or any other user-controlled or unbounded value. As a backstop, Memory caps each metric name at MaxSeriesPerMetric series; instruments requested past the cap still work but are not exported.

Histogram buckets

Histograms use the fixed DefaultBuckets (upper bounds, in seconds): 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, plus +Inf.

Index

Constants

View Source
const (
	// CheckDuration is the check latency histogram, labelled provider.
	CheckDuration = "console_check_duration_seconds"
	// CheckResults counts check outcomes, labelled provider and state.
	CheckResults = "console_check_results_total"
)

Status-provider metric names.

View Source
const (
	// StoreOpDuration is the per-operation latency histogram, labelled op.
	StoreOpDuration = "console_store_op_duration_seconds"
	// StoreErrors counts failed operations, labelled op. Not-found and
	// conflict sentinels are expected outcomes and are not counted.
	StoreErrors = "console_store_errors_total"
)

Store metric names.

View Source
const MaxSeriesPerMetric = 1000

MaxSeriesPerMetric bounds the number of label combinations Memory retains per metric name. Combinations beyond it get working but unexported instruments, so a cardinality bug cannot exhaust memory.

Variables

View Source
var DefaultBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}

DefaultBuckets are the fixed histogram upper bounds, in seconds, used by Memory: 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10. An implicit +Inf bucket is always added. Treat as read-only.

Functions

func RuntimeCollector

func RuntimeCollector(version string) func(Recorder)

RuntimeCollector returns a collector, suitable for Memory.AddCollector, that samples Go runtime health via runtime/metrics (no stop-the-world) and publishes it as gauges:

  • go_goroutines: live goroutines.
  • go_memstats_heap_alloc_bytes: bytes of allocated heap objects.
  • go_gc_pause_seconds: CUMULATIVE time spent in GC stop-the-world pauses since process start. runtime/metrics exposes pauses only as a histogram, so this is an estimate: each bucket's count is weighted by the bucket midpoint (the finite bound for open-ended buckets). It is monotonically non-decreasing; use rate() or a delta to get recent pause time.

It also sets console_build_info{version} to 1. Samples of a kind the running Go version does not provide are skipped.

func WrapProvider

func WrapProvider(next status.Provider, rec Recorder) status.Provider

WrapProvider returns a status.Provider that records check latency and result counts on rec, delegating to next. The provider label is next.Name() (the provider type, never the component ID) and the state label is the core.HealthState string. Name() is read once at wrap time. A state outside the core enum (e.g. from a plugin) is counted as unknown to keep label cardinality bounded.

func WrapStore

func WrapStore(next store.Store, rec Recorder) store.Store

WrapStore returns a store.Store that records latency and error counts for every method on rec, delegating to next. The op label is the method name. core.ErrNotFound and core.ErrConflict are not counted as errors. Instruments are resolved once here, so the per-call path does no label lookups.

Types

type Counter

type Counter interface {
	// Inc adds 1.
	Inc()
	// Add adds v. Negative values are ignored to preserve monotonicity.
	Add(v float64)
}

Counter is a monotonically increasing value.

type Gauge

type Gauge interface {
	// Set replaces the value.
	Set(v float64)
	// Add adds v (which may be negative).
	Add(v float64)
	// Inc adds 1.
	Inc()
	// Dec subtracts 1.
	Dec()
}

Gauge is a value that can go up and down.

type Histogram

type Histogram interface {
	// Observe records one observation, conventionally in seconds.
	Observe(v float64)
}

Histogram samples observations into fixed buckets.

type Label

type Label struct {
	Name  string
	Value string
}

Label is a single metric dimension. Names must be valid Prometheus label names ([a-zA-Z_][a-zA-Z0-9_]*); values are arbitrary but must be bounded (see the package documentation).

func L

func L(name, value string) Label

L is a shorthand constructor for a Label.

type Memory

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

Memory is an in-process Recorder that keeps all series in memory and renders them in the Prometheus text exposition format via Memory.WritePrometheus. Spans are no-ops that preserve the context. Create one with NewMemory.

func NewMemory

func NewMemory() *Memory

NewMemory returns an empty in-memory registry.

func (*Memory) AddCollector

func (m *Memory) AddCollector(fn func(Recorder))

AddCollector registers fn to run at the start of every Memory.WritePrometheus call, before rendering. Collectors refresh scrape-time values (for example runtime stats set on gauges) using the Recorder passed in. fn must not call WritePrometheus.

func (*Memory) Counter

func (m *Memory) Counter(name string, labels ...Label) Counter

Counter implements Recorder.

func (*Memory) Gauge

func (m *Memory) Gauge(name string, labels ...Label) Gauge

Gauge implements Recorder.

func (*Memory) Histogram

func (m *Memory) Histogram(name string, labels ...Label) Histogram

Histogram implements Recorder.

func (*Memory) StartSpan

func (m *Memory) StartSpan(ctx context.Context, _ string, _ ...Label) (context.Context, func(error))

StartSpan implements Recorder. It is a no-op: the returned context is ctx itself and the end func does nothing.

func (*Memory) WritePrometheus

func (m *Memory) WritePrometheus(w io.Writer) error

WritePrometheus runs the registered collectors, then writes every series in the Prometheus text exposition format (version 0.0.4). Output is deterministic: families sorted by name, series sorted by labels.

type Recorder

type Recorder interface {
	// Counter returns the counter for name and labels.
	Counter(name string, labels ...Label) Counter
	// Histogram returns the histogram for name and labels.
	Histogram(name string, labels ...Label) Histogram
	// Gauge returns the gauge for name and labels.
	Gauge(name string, labels ...Label) Gauge
	// StartSpan returns a derived context and a func that ends the span,
	// recording err (which may be nil) as its outcome.
	StartSpan(ctx context.Context, name string, labels ...Label) (context.Context, func(err error))
}

Recorder is the instrumentation seam. The zero-cost default is Nop. Implementations must be safe for concurrent use. Requesting the same name and labels repeatedly returns instruments backed by the same series.

var Nop Recorder = nop{}

Nop is the do-nothing Recorder used when observability is disabled. Calls without labels are allocation-free (verified by tests). Passing labels through the Recorder interface may allocate the variadic slice, so hot paths should resolve instruments once at construction and reuse them rather than looking them up, with labels, on every call.

Jump to

Keyboard shortcuts

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