Documentation
¶
Overview ¶
Package metrics exposes graywolf's Prometheus metrics and a helper to fold Rust-side StatusUpdate messages into them.
Index ¶
- type Metrics
- func (m *Metrics) Handler() http.Handler
- func (m *Metrics) ObserveKissBroadcastSuppressed(ifaceID uint32)
- func (m *Metrics) ObserveKissClientReconnect(ifaceID uint32)
- func (m *Metrics) ObserveKissClientTxDrop(ifaceID uint32, reason string)
- func (m *Metrics) ObserveKissIngressFrame(ifaceID uint32, mode string)
- func (m *Metrics) ObserveKissTncRxDispatched(ifaceID uint32)
- func (m *Metrics) ObserveReceivedFrame(channel uint32)
- func (m *Metrics) ObserveTxBackendSubmit(channel uint32, backend, instance, outcome string, d time.Duration)
- func (m *Metrics) ObserveTxFrame(channel uint32)
- func (m *Metrics) ObserveTxNoBackend(channel uint32)
- func (m *Metrics) SetAgwClients(n int)
- func (m *Metrics) SetChildUp(up bool)
- func (m *Metrics) SetKissClientBackoffSeconds(ifaceID uint32, secs uint32)
- func (m *Metrics) SetKissClientConnected(ifaceID uint32, name string, connected bool)
- func (m *Metrics) SetKissClients(iface string, n int)
- func (m *Metrics) SetKissInstanceTxQueueDepth(ifaceID uint32, depth int32)
- func (m *Metrics) UpdateFromStatus(s *pb.StatusUpdate)
- type RateLimitedLogger
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 (*Metrics) ObserveKissBroadcastSuppressed ¶
ObserveKissBroadcastSuppressed increments when a KISS broadcast skips the originating TNC interface (self-loop guard).
func (*Metrics) ObserveKissClientReconnect ¶
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 ¶
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 ¶
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 ¶
ObserveKissTncRxDispatched increments when a TNC-mode frame survives rate-limit + queue and is enqueued onto the shared RX fanout.
func (*Metrics) ObserveReceivedFrame ¶
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 ¶
ObserveTxFrame increments the tx counter for a channel.
func (*Metrics) ObserveTxNoBackend ¶
ObserveTxNoBackend increments when the dispatcher drops a frame because the channel has no registered backend.
func (*Metrics) SetAgwClients ¶
SetAgwClients sets the AGW client gauge.
func (*Metrics) SetChildUp ¶
SetChildUp records whether the Rust child is running.
func (*Metrics) SetKissClientBackoffSeconds ¶
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 ¶
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 ¶
SetKissClients sets the gauge for a KISS interface name.
func (*Metrics) SetKissInstanceTxQueueDepth ¶
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).