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

observability

package
v0.5.8 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: 21 Imported by: 0

Documentation

Overview

Package observability wires structured logging, Prometheus metrics, and OpenTelemetry tracing for the Dexaflow control plane (ADR 0010).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewLogger

func NewLogger(level, format string) *slog.Logger

NewLogger builds a slog.Logger writing to stdout at the given level. A format of "text" uses the human-readable handler; anything else (default "json") uses the JSON handler required for production (ADR 0010).

func WithLegacyNames

func WithLegacyNames(g prometheus.Gatherer) prometheus.Gatherer

WithLegacyNames wraps g so each dexaflow_* metric family is also exposed as leoflow_*, with identical help, type, labels and values. Dashboards, alerts and recording rules written before the rename keep working against the same scrape; other families pass through once.

Types

type Config

type Config struct {
	ServiceName  string
	LogLevel     string
	LogFormat    string
	OTelEnabled  bool
	OTelEndpoint string
	// SampleRatio is the share of new root traces kept, in [0, 1].
	SampleRatio float64
	// SkipProbeSpans drops spans for /healthz, /readyz and /static/*.
	SkipProbeSpans bool
}

Config configures observability setup.

type Metrics

type Metrics struct {
	// Scheduler
	SchedulerLoopDuration prometheus.Histogram
	ScheduleDelay         prometheus.Histogram
	SchedulerWokenTicks   prometheus.Counter
	SchedulerDecisions    *prometheus.CounterVec
	TasksUndispatchable   *prometheus.CounterVec
	SchedulerStepDowns    *prometheus.CounterVec // #311 leader churn observability
	SchedulerReacquire    prometheus.Histogram   // #311 step-down → re-acquire latency
	AlertsDispatched      *prometheus.CounterVec // #424 on-failure alerts, by outcome

	// Task lifecycle
	TaskStateTransitions    *prometheus.CounterVec
	TaskDuration            *prometheus.HistogramVec
	TaskRetries             *prometheus.CounterVec
	TaskPodCreationDuration prometheus.Histogram
	TaskColdStart           *prometheus.HistogramVec

	// XCom
	XComSize     *prometheus.HistogramVec
	XComPush     *prometheus.CounterVec
	XComPull     *prometheus.CounterVec
	XComRejected *prometheus.CounterVec

	// API
	HTTPRequests        *prometheus.CounterVec
	HTTPRequestDuration *prometheus.HistogramVec
	AuthFailures        *prometheus.CounterVec

	// Agent credentials: task tokens authenticated without an attempt_epoch
	// claim, accepted under the epoch-0 rule (ADR 0051 amendment).
	AgentLegacyAttemptTokens prometheus.Counter
	// AgentTokenRejections counts agent RPCs refused for their bearer token, by
	// reason (#1125).
	AgentTokenRejections *prometheus.CounterVec
	// Reconciler: durable SUCCESS records settled over a reaper's provisional
	// infra mark, by the mark overridden (ADR 0052 amendment).
	ReconcileInfraOverrides *prometheus.CounterVec

	// Executor (Kubernetes)
	PodsCreated        *prometheus.CounterVec
	PodPendingDuration prometheus.Histogram
	KubernetesAPICalls *prometheus.CounterVec

	// Dispatch pool (#127, ADR 0031): depth gauge, capacity-saturation counter,
	// per-dispatch latency, inner-dispatcher error counter.
	DispatchQueueDepth  prometheus.Gauge
	DispatchAtCapacity  prometheus.Counter
	DispatchLatency     prometheus.Histogram
	DispatchInnerErrors prometheus.Counter
	// UnitMisfits counts tasks let through under executor.unit.enforce=warn
	// although they do not fit their size, by stage (register, dispatch;
	// ADR 0066 §3).
	UnitMisfits *prometheus.CounterVec
	// UnpinnedImages counts images not pinned by digest that
	// deploy.require_digest warned about or refused, by stage (register,
	// set_current) and outcome (warned, refused) (#1703).
	UnpinnedImages *prometheus.CounterVec
	// CIOIDCExchanges counts CI OIDC token exchanges by issuer entry
	// ("unknown" when the token named none) and outcome (ADR 0092).
	CIOIDCExchanges *prometheus.CounterVec

	// Redis observability — port of the #311 step-down pattern for Redis (Pro
	// only; Lite uses Postgres + in-process tailer per ADR 0026, so these
	// gauges stay at 0). Operators alert on the per-reason rate, not on log
	// content: a sudden rate spike on RedisCommandFailures{reason="timeout"}
	// or RedisDialFailures{reason="tls_handshake"} catches an outage before
	// the surrounding components (XCom, log tailer) start surfacing tail
	// errors to users.
	RedisCommandFailures *prometheus.CounterVec
	RedisDialFailures    *prometheus.CounterVec
	RedisDialDuration    prometheus.Histogram
	RedisPoolActive      prometheus.Gauge
	RedisPoolIdle        prometheus.Gauge
	RedisPoolTotalConns  prometheus.Gauge
	RedisPoolTimeouts    prometheus.Counter

	// Retention janitor (D5): rows deleted per table, rows a dry run found
	// eligible per table, and the duration of each cycle.
	RetentionRowsDeleted   *prometheus.CounterVec
	RetentionRowsEligible  *prometheus.GaugeVec
	RetentionCycleDuration prometheus.Histogram

	// UsageEventsLag is the age of the oldest usage event (ADR 0096) the
	// service API cannot serve yet because an older transaction is still
	// open, set by the leader; 0 while recording is off.
	UsageEventsLag prometheus.Gauge
	// TaskAttempts and TaskSlotSeconds are the per-tenant usage counters,
	// advanced by the leader from the usage events when
	// observability.usage_metrics is on. Their cardinality is the number of
	// tenants, so they stay empty by default.
	TaskAttempts    *prometheus.CounterVec
	TaskSlotSeconds *prometheus.CounterVec

	// TLSReloadFailures counts certificate or key files that changed on disk
	// but failed to load, by listener (api, agent_grpc). The listener keeps
	// serving the last good pair, so a non-zero rate means a rotation is stuck
	// and the served certificate will expire unless the files are fixed (#637).
	TLSReloadFailures *prometheus.CounterVec

	// LogTailReplayBytes is what the on_demand live-tail gates hold for replay
	// together, and LogTailReplayEvictions the rings logs.tail.replay_budget_bytes
	// dropped to stay under it (#1621).
	LogTailReplayBytes     prometheus.Gauge
	LogTailReplayEvictions prometheus.Counter

	// ResilienceBackstopDisabled is 1 for each resilience backstop a boot
	// setting removed and 0 for each one in place, set once at boot (#941).
	ResilienceBackstopDisabled *prometheus.GaugeVec
	// contains filtered or unexported fields
}

Metrics holds every Prometheus collector required by ADR 0010. Construct it once with NewMetrics and pass it to the components that emit values.

func NewMetrics

func NewMetrics(reg prometheus.Registerer) *Metrics

NewMetrics registers every ADR 0010 collector with reg and returns the set.

func (*Metrics) ObservePodCreation added in v0.5.4

func (m *Metrics) ObservePodCreation(dagID, result string, d time.Duration)

ObservePodCreation counts one task pod create by dag and result ("success" or "error", ADR 0010) and observes the API call's latency. It satisfies executor.PodCreationRecorder.

func (*Metrics) ObserveRedisDialDuration

func (m *Metrics) ObserveRedisDialDuration(d time.Duration)

ObserveRedisDialDuration records one TCP/TLS dial latency sample.

func (*Metrics) ObserveRetentionCycle added in v0.5.4

func (m *Metrics) ObserveRetentionCycle(d time.Duration)

ObserveRetentionCycle observes the duration of one retention cycle.

func (*Metrics) ObserveScheduleDelay added in v0.5.8

func (m *Metrics) ObserveScheduleDelay(d time.Duration)

ObserveScheduleDelay records how late a scheduled run was created, from its slot to its creation (#2175). It satisfies scheduler.Recorder.

func (*Metrics) ObserveSchedulerLoop added in v0.5.4

func (m *Metrics) ObserveSchedulerLoop(d time.Duration)

ObserveSchedulerLoop records the wall time of one leader scheduler tick, the SLI dexaflow_scheduler_loop_duration_seconds exists for (#1186). It satisfies scheduler.Recorder.

func (*Metrics) ObserveSchedulerReacquire

func (m *Metrics) ObserveSchedulerReacquire(d time.Duration)

ObserveSchedulerReacquire records the wall-clock duration of a step-down → re-acquire cycle (#311). Use the histogram's P99 in alerts: a P99 growing past a second indicates churn that is starting to delay scheduling.

func (*Metrics) RecordAgentTokenRejection added in v0.5.5

func (m *Metrics) RecordAgentTokenRejection(reason string)

RecordAgentTokenRejection counts one agent RPC refused for its bearer token. It satisfies agentrpc.TokenRejectionRecorder.

func (*Metrics) RecordAlert

func (m *Metrics) RecordAlert(dagID, channelType, result string)

RecordAlert counts one on-failure alert dispatch (#424) by dag, channel type, and outcome ("sent" or "failed"). Satisfies failurealert.Recorder.

func (*Metrics) RecordCIOIDCExchange added in v0.5.5

func (m *Metrics) RecordCIOIDCExchange(issuer, outcome string)

RecordCIOIDCExchange counts one CI OIDC token exchange. It satisfies api.CIOIDCExchangeRecorder.

func (*Metrics) RecordDispatchAtCapacity

func (m *Metrics) RecordDispatchAtCapacity()

RecordDispatchAtCapacity counts one rejection because the buffered dispatch queue was full. Each rejection means the scheduler will retry on the next tick — a rising rate signals the pool needs more workers or a deeper queue.

func (*Metrics) RecordDispatchInnerError

func (m *Metrics) RecordDispatchInnerError()

RecordDispatchInnerError counts one error returned by the inner dispatcher inside a worker — typically a Kubernetes API failure or pod-create rejection.

func (*Metrics) RecordDispatchLatencySeconds

func (m *Metrics) RecordDispatchLatencySeconds(seconds float64)

RecordDispatchLatencySeconds observes one end-to-end dispatch latency, from enqueue to the worker's inner-dispatcher return, for every buffered dispatch (successful or not).

func (*Metrics) RecordDispatchQueueDepth

func (m *Metrics) RecordDispatchQueueDepth(depth int)

RecordDispatchQueueDepth sets the current depth of the buffered dispatch queue (#127). Sampled on every successful enqueue.

func (*Metrics) RecordHTTPRequest

func (m *Metrics) RecordHTTPRequest(method, path string, status int, dur time.Duration)

RecordHTTPRequest records a completed HTTP request (count + duration).

func (*Metrics) RecordInfraOverride added in v0.5.2

func (m *Metrics) RecordInfraOverride(mark string)

RecordInfraOverride counts one durable SUCCESS settled over an infra mark. It satisfies executor.InfraOverrideRecorder.

func (*Metrics) RecordLegacyAttemptToken added in v0.5.1

func (m *Metrics) RecordLegacyAttemptToken()

RecordLegacyAttemptToken counts one agent RPC authenticated by a task token without an attempt_epoch claim (ADR 0051 amendment). It satisfies agentrpc.LegacyTokenRecorder.

func (*Metrics) RecordLogTailReplayEviction added in v0.5.8

func (m *Metrics) RecordLogTailReplayEviction()

RecordLogTailReplayEviction counts a replay ring the budget dropped. No-op on a nil receiver.

func (*Metrics) RecordRedisCommandFailure

func (m *Metrics) RecordRedisCommandFailure(reason string)

RecordRedisCommandFailure increments the per-reason command failure counter (#312 sibling, paralleling #311's step-down reason). Caller is the go-redis hook's ProcessHook — it classifies the error before incrementing so the label cardinality stays bounded.

func (*Metrics) RecordRedisDialFailure

func (m *Metrics) RecordRedisDialFailure(reason string)

RecordRedisDialFailure increments the per-reason dial failure counter. Separated from command failures because a dial failure means we never even reached the Redis instance — the cause is upstream (DNS, TLS, network), not Redis itself.

func (*Metrics) RecordRedisPoolTimeout

func (m *Metrics) RecordRedisPoolTimeout()

RecordRedisPoolTimeout increments the pool-checkout timeout counter. The go-redis hook fires this when a caller waited PoolTimeout for an idle connection — the leading indicator that the pool is too small for the command rate.

func (*Metrics) RecordResilienceBackstop added in v0.5.8

func (m *Metrics) RecordResilienceBackstop(key string, disabled bool)

RecordResilienceBackstop sets the boot posture of the backstop of key: disabled or in place. No-op on a nil receiver.

func (*Metrics) RecordRetentionDeleted added in v0.5.4

func (m *Metrics) RecordRetentionDeleted(table string, n int64)

RecordRetentionDeleted counts n rows the retention janitor deleted from table.

func (*Metrics) RecordRetentionEligible added in v0.5.4

func (m *Metrics) RecordRetentionEligible(table string, n int64)

RecordRetentionEligible sets the rows a retention dry run found eligible in table.

func (*Metrics) RecordSchedulerDecision

func (m *Metrics) RecordSchedulerDecision(decisionType string)

RecordSchedulerDecision records one scheduler decision by type.

func (*Metrics) RecordSchedulerStepDown

func (m *Metrics) RecordSchedulerStepDown(reason string)

RecordSchedulerStepDown increments the leader step-down counter (#311). The reason label lets operators distinguish a transient lock_released (pgx blip) from a check_timeout (the lock-check failed repeatedly) or a clean shutdown, so they can alert on the rate of the noisy reasons without paging on expected shutdowns.

func (*Metrics) RecordSchedulerWokenTick added in v0.5.4

func (m *Metrics) RecordSchedulerWokenTick()

RecordSchedulerWokenTick counts one scheduler tick started early by a task completion (scheduler.eager_promotion).

func (*Metrics) RecordSchemaState added in v0.5.6

func (m *Metrics) RecordSchemaState(reason string)

RecordSchemaState records the schema verdict: the reason the schema is not ready, or "" once it is. One series is kept, so a dashboard reads the current reason, not every reason ever seen. No-op on a nil receiver.

func (*Metrics) RecordTLSReloadFailure added in v0.5.5

func (m *Metrics) RecordTLSReloadFailure(listener string)

RecordTLSReloadFailure counts one rotated certificate pair that failed to load on the given listener. It is a no-op on a nil receiver so a caller without metrics wired needs no guard.

func (*Metrics) RecordTaskAttempt added in v0.5.8

func (m *Metrics) RecordTaskAttempt(tenant, state string, slotSeconds float64)

RecordTaskAttempt counts one finished attempt of tenant in state and adds its slot seconds.

func (*Metrics) RecordTaskDuration

func (m *Metrics) RecordTaskDuration(dagID, taskID, taskType string, seconds float64)

RecordTaskDuration records how long a task took to execute, in seconds.

func (*Metrics) RecordTaskRetry added in v0.5.4

func (m *Metrics) RecordTaskRetry(dagID, taskID string)

RecordTaskRetry counts one retry the scheduler released for a task (its up_for_retry reset to none with a new try number). It satisfies scheduler.Recorder.

func (*Metrics) RecordTaskTransition

func (m *Metrics) RecordTaskTransition(from, to, dagID string)

RecordTaskTransition records a task instance state transition.

func (*Metrics) RecordUndispatchable

func (m *Metrics) RecordUndispatchable(reason string)

RecordUndispatchable records a task that became queued but has no executor to launch it (e.g. pod dispatch disabled), so an operator can distinguish a resource/config gap from an actual bug.

func (*Metrics) RecordUnitMisfit added in v0.5.2

func (m *Metrics) RecordUnitMisfit(stage string)

RecordUnitMisfit counts one task let through under executor.unit.enforce=warn although it does not fit its size. It satisfies dispatch.UnitMisfitRecorder and api.UnitMisfitRecorder.

func (*Metrics) RecordUnpinnedImage added in v0.5.4

func (m *Metrics) RecordUnpinnedImage(stage, outcome string)

RecordUnpinnedImage counts one image not pinned by digest that deploy.require_digest warned about or refused at stage. It satisfies api.UnpinnedImageRecorder.

func (*Metrics) SetLogTailReplayBytes added in v0.5.8

func (m *Metrics) SetLogTailReplayBytes(n int64)

SetLogTailReplayBytes reports what the live-tail gates hold for replay. No-op on a nil receiver.

func (*Metrics) SetUsageEventsLag added in v0.5.8

func (m *Metrics) SetUsageEventsLag(d time.Duration)

SetUsageEventsLag sets the age of the oldest usage event held back.

func (*Metrics) UpdateRedisPoolStats

func (m *Metrics) UpdateRedisPoolStats(active, idle, total uint32)

UpdateRedisPoolStats refreshes the three pool gauges. The cmd/dexaflow-server goroutine that calls this scrapes go-redis's PoolStats every N seconds.

type Telemetry

type Telemetry struct {
	Logger   *slog.Logger
	Metrics  *Metrics
	Registry *prometheus.Registry
	Tracer   trace.Tracer
}

Telemetry bundles the configured observability primitives.

func Setup

func Setup(ctx context.Context, cfg Config) (*Telemetry, func(), error)

Setup builds logging, metrics, and tracing from cfg and returns the telemetry bundle plus a shutdown function the caller must defer.

Jump to

Keyboard shortcuts

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