metrics

package
v0.13.3 Latest Latest
Warning

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

Go to latest
Published: May 9, 2026 License: GPL-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package metrics exposes graywolf's Prometheus metrics and a helper to fold Rust-side StatusUpdate messages into them.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Metrics

type Metrics struct {
	Registry *prometheus.Registry

	RxFrames       *prometheus.CounterVec
	DcdTransitions *prometheus.CounterVec
	DcdDropped     prometheus.Counter
	ChildRestarts  prometheus.Counter
	AudioLevel     *prometheus.GaugeVec
	DcdActive      *prometheus.GaugeVec
	ChildUp        prometheus.Gauge

	// Phase 2: protocol + tx governor metrics.
	KissClientsActive *prometheus.GaugeVec // per interface name
	KissDecodeErrors  prometheus.Counter
	AgwClientsActive  prometheus.Gauge
	AgwDecodeErrors   *prometheus.CounterVec // label: stage ("initial" | "fallback")
	TxFrames          *prometheus.CounterVec // per channel
	TxRateLimited     prometheus.Counter
	TxDeduped         prometheus.Counter
	TxQueueDropped    prometheus.Counter
	AprsOutDropped    prometheus.Counter

	// digipeater, packet log, and beacon metrics.
	DigipeaterPackets  prometheus.Counter
	DigipeaterDeduped  prometheus.Counter
	PacketlogEntries   prometheus.Gauge
	BeaconPackets      *prometheus.CounterVec // label: "type"
	BeaconFired        *prometheus.CounterVec // labels: "beacon_name", "result" ("sent" | "skipped_busy")
	BeaconEncodeErrors *prometheus.CounterVec // label: "beacon_name"
	BeaconSubmitErrors *prometheus.CounterVec // labels: "beacon_name", "reason"
	SmartBeaconRate    *prometheus.GaugeVec   // label: "channel"
	GpsParseErrors     *prometheus.CounterVec // label: "source" ("gpsd" | "nmea")

	// KISS modem/TNC-mode observability. Phase 5 of the KISS modem/TNC
	// plan.
	KissIngressFrames       *prometheus.CounterVec // labels: "interface_id", "mode"
	KissTncRxDispatched     *prometheus.CounterVec // label: "interface_id"
	KissTncIngressDropped   *prometheus.CounterVec // labels: "interface_id", "reason" ("rate_limit" | "queue_full")
	KissBroadcastSuppressed *prometheus.CounterVec // labels: "interface_id", "reason" ("self_loop")
	RxFanoutDropped         *prometheus.CounterVec // label: "producer" ("kiss_tnc"; "modem" is a blocking producer and never drops at the fanout)

	// Phase 3 (KISS TCP-client / channel-backing plan): TX backend
	// dispatcher observability. Labels:
	//   TxBackendSubmits: channel, backend ("modem" | "kiss"),
	//                     instance (e.g. "modem", "kiss-3"),
	//                     outcome ("ok" | "err" | "backend_busy" | "backend_down")
	//   TxNoBackend:      channel
	//   TxBackendDuration: channel, backend
	//   KissInstanceTxQueueDepth: interface_id (gauge)
	TxBackendSubmits         *prometheus.CounterVec
	TxNoBackend              *prometheus.CounterVec
	TxBackendDuration        *prometheus.HistogramVec
	KissInstanceTxQueueDepth *prometheus.GaugeVec

	// Phase 4: KISS tcp-client supervisor observability.
	//   KissClientConnected: gauge per interface (1 = connected, 0 = not)
	//   KissClientReconnects: counter per interface (cumulative dials since process start)
	//   KissClientBackoffSeconds: gauge per interface (current backoff delay in seconds)
	//   KissClientTxDrops: counter per interface × reason ("busy" | "down")
	KissClientConnected      *prometheus.GaugeVec
	KissClientReconnects     *prometheus.CounterVec
	KissClientBackoffSeconds *prometheus.GaugeVec
	KissClientTxDrops        *prometheus.CounterVec

	// IgateFilterRecompositions counts each successful recompose-and-apply
	// cycle in the iGate reload path — i.e. a tactical mutation (or
	// iGate config save) produced a composed filter that differed from
	// the last-applied value and was pushed into the running client.
	// Cycles that compose the same filter as before do not increment
	// (no reconnect triggered).
	IgateFilterRecompositions prometheus.Counter
	// contains filtered or unexported fields
}

Metrics owns a Prometheus registry and the graywolf metric vectors.

func New

func New() *Metrics

New builds a Metrics with a private registry.

func (*Metrics) Handler

func (m *Metrics) Handler() http.Handler

Handler returns an http.Handler serving /metrics from this registry.

func (*Metrics) ObserveKissBroadcastSuppressed

func (m *Metrics) ObserveKissBroadcastSuppressed(ifaceID uint32)

ObserveKissBroadcastSuppressed increments when a KISS broadcast skips the originating TNC interface (self-loop guard).

func (*Metrics) ObserveKissClientReconnect

func (m *Metrics) ObserveKissClientReconnect(ifaceID uint32)

ObserveKissClientReconnect increments the per-interface tcp-client reconnect counter. Called once per successful dial by the wiring layer's OnReload handler (which observes supervisor state transitions).

func (*Metrics) ObserveKissClientTxDrop

func (m *Metrics) ObserveKissClientTxDrop(ifaceID uint32, reason string)

ObserveKissClientTxDrop increments the per-interface tx-drop counter. reason is "busy" (queue full) or "down" (supervisor in backoff / not connected). Wired via kiss.Manager.OnTxQueueDrop — tcp-client instances share the same queue plumbing as server-listen instances, so this counter is a strict superset of the Phase 3 queue's drop events.

func (*Metrics) ObserveKissIngressFrame

func (m *Metrics) ObserveKissIngressFrame(ifaceID uint32, mode string)

ObserveKissIngressFrame increments the per-interface/per-mode ingress frame counter. Called for every inbound KISS data frame that successfully AX.25-decodes, regardless of whether the interface is in Modem or TNC mode.

func (*Metrics) ObserveKissTncRxDispatched

func (m *Metrics) ObserveKissTncRxDispatched(ifaceID uint32)

ObserveKissTncRxDispatched increments when a TNC-mode frame survives rate-limit + queue and is enqueued onto the shared RX fanout.

func (*Metrics) ObserveReceivedFrame

func (m *Metrics) ObserveReceivedFrame(channel uint32)

ObserveReceivedFrame bumps the rx-frames counter for a channel. Called from the modembridge frame forwarder so individual frame arrivals are reflected immediately without waiting for the next StatusUpdate.

func (*Metrics) ObserveTxBackendSubmit

func (m *Metrics) ObserveTxBackendSubmit(channel uint32, backend, instance, outcome string, d time.Duration)

ObserveTxBackendSubmit records one per-instance fan-out outcome from the Phase 3 TX dispatcher. d is the Submit duration used by the companion histogram.

func (*Metrics) ObserveTxFrame

func (m *Metrics) ObserveTxFrame(channel uint32)

ObserveTxFrame increments the tx counter for a channel.

func (*Metrics) ObserveTxNoBackend

func (m *Metrics) ObserveTxNoBackend(channel uint32)

ObserveTxNoBackend increments when the dispatcher drops a frame because the channel has no registered backend.

func (*Metrics) SetAgwClients

func (m *Metrics) SetAgwClients(n int)

SetAgwClients sets the AGW client gauge.

func (*Metrics) SetChildUp

func (m *Metrics) SetChildUp(up bool)

SetChildUp records whether the Rust child is running.

func (*Metrics) SetKissClientBackoffSeconds

func (m *Metrics) SetKissClientBackoffSeconds(ifaceID uint32, secs uint32)

SetKissClientBackoffSeconds sets the current backoff delay gauge. Consumed by the UI via /api/kiss, but also surfaced to Prometheus so alerting can catch stuck-in-backoff supervisors independently.

func (*Metrics) SetKissClientConnected

func (m *Metrics) SetKissClientConnected(ifaceID uint32, name string, connected bool)

SetKissClientConnected sets the per-interface tcp-client connected gauge. 1 means "live dialed connection"; 0 means "not connected" (includes backoff, connecting, disconnected, stopped).

func (*Metrics) SetKissClients

func (m *Metrics) SetKissClients(iface string, n int)

SetKissClients sets the gauge for a KISS interface name.

func (*Metrics) SetKissInstanceTxQueueDepth

func (m *Metrics) SetKissInstanceTxQueueDepth(ifaceID uint32, depth int32)

SetKissInstanceTxQueueDepth sets the per-interface tx queue depth gauge. Wired in from kiss.Manager via OnTxQueueDepth.

func (*Metrics) UpdateFromStatus

func (m *Metrics) UpdateFromStatus(s *pb.StatusUpdate)

UpdateFromStatus folds a Rust-side StatusUpdate into the metric vectors. Counter deltas are computed against the previous update; if the modem restarts (counters go backwards) the gap is ignored to avoid negative deltas.

type RateLimitedLogger

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

RateLimitedLogger emits log messages at most once per interval per key. It exists so every "silent drop" site in graywolf can log the first occurrence of a drop-kind at warn level without flooding the operator's log when a subsystem enters sustained back-pressure.

Thread-safe. The zero value is NOT usable — construct with NewRateLimitedLogger. One instance per drop site (not global), stored as a field on the owning component, so "rate limit" is scoped to the drop kind, not to the whole process.

func NewRateLimitedLogger

func NewRateLimitedLogger(interval time.Duration) *RateLimitedLogger

NewRateLimitedLogger returns a logger that allows at most one emission per (key, interval). A non-positive interval disables rate limiting — every call emits.

func (*RateLimitedLogger) Log

func (r *RateLimitedLogger) Log(logger *slog.Logger, level slog.Level, key, msg string, args ...any) bool

Log emits msg via logger at the given level but only if the last emission for this key is older than interval ago. Returns true if the message was actually emitted. A nil logger is a no-op (but the rate window is still updated so a subsequent call with a real logger still observes suppression).

Jump to

Keyboard shortcuts

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