netring 0.29.0

High-performance zero-copy packet I/O for Linux (AF_PACKET TPACKET_V3 + AF_XDP)
Documentation
//! Metrics integration via the `metrics` façade (feature: `metrics`).
//!
//! Records packet counts and drops as monotonic counters tagged with the
//! interface label. The actual export (Prometheus, statsd, OTel, ...) is
//! whatever metrics-recorder the host application installs.
//!
//! # Examples
//!
//! ```no_run
//! # #[cfg(feature = "metrics")]
//! # fn _ex() -> Result<(), netring::Error> {
//! use netring::Capture;
//! use netring::metrics::record_capture_delta;
//!
//! let cap = Capture::open("eth0")?;
//! // ... capture some packets ...
//! let delta = cap.stats()?;
//! record_capture_delta("eth0", &delta);
//! # Ok(()) }
//! ```
//!
//! Pair with a recorder such as
//! [`metrics-exporter-prometheus`](https://crates.io/crates/metrics-exporter-prometheus)
//! to actually surface the values.

use crate::stats::CaptureStats;

/// Counter name: total packets received past kernel filter.
pub const COUNTER_PACKETS: &str = "netring_capture_packets_total";

/// Counter name: total packets dropped due to ring exhaustion.
pub const COUNTER_DROPS: &str = "netring_capture_drops_total";

/// Counter name: total ring freeze events.
pub const COUNTER_FREEZES: &str = "netring_capture_freezes_total";

/// Gauge name: cumulative packets delivered, per capture source. Emitted
/// by [`CaptureTelemetry::record_metrics`](crate::monitor::CaptureTelemetry::record_metrics).
pub const GAUGE_PACKETS: &str = "netring_capture_packets";

/// Gauge name: cumulative kernel drops, per capture source.
pub const GAUGE_DROPS: &str = "netring_capture_drops";

/// Gauge name: cumulative ring freezes, per capture source.
pub const GAUGE_FREEZES: &str = "netring_capture_freezes";

/// Gauge name: windowed drop rate (`[0.0, 1.0]`), per capture source.
pub const GAUGE_DROP_RATE: &str = "netring_capture_drop_rate";

/// Gauge name: cumulative handler errors swallowed under
/// `HandlerErrorPolicy::Isolate`. Emitted by
/// [`MonitorHealth::record_metrics`](crate::monitor::MonitorHealth::record_metrics).
pub const GAUGE_HANDLER_ERRORS: &str = "netring_monitor_handler_errors";

/// Gauge name: cumulative backend errors swallowed under
/// `BackendErrorPolicy::SkipSource`.
pub const GAUGE_BACKEND_ERRORS: &str = "netring_monitor_backend_errors";

/// Gauge name: active flows in the central tracker (last event).
pub const GAUGE_ACTIVE_FLOWS: &str = "netring_monitor_active_flows";

/// Record a capture-stats delta as `metrics` counters.
///
/// Best paired with [`crate::Capture::stats`] (which returns the delta
/// since the last read). Calls
/// [`metrics::counter!`](https://docs.rs/metrics/latest/metrics/macro.counter.html)
/// three times — packets / drops / freezes — each tagged with `iface`.
///
/// All three counters use the standard `netring_capture_*_total` naming
/// (Prometheus convention: `_total` suffix on monotonic counters).
pub fn record_capture_delta(iface: &str, delta: &CaptureStats) {
    let iface_label = iface.to_string();
    metrics::counter!(COUNTER_PACKETS, "iface" => iface_label.clone())
        .increment(delta.packets as u64);
    metrics::counter!(COUNTER_DROPS, "iface" => iface_label.clone()).increment(delta.drops as u64);
    metrics::counter!(COUNTER_FREEZES, "iface" => iface_label).increment(delta.freeze_count as u64);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn counter_names_have_total_suffix() {
        assert!(COUNTER_PACKETS.ends_with("_total"));
        assert!(COUNTER_DROPS.ends_with("_total"));
        assert!(COUNTER_FREEZES.ends_with("_total"));
    }

    #[test]
    fn record_does_not_panic_with_no_recorder() {
        // Without a recorder installed the metrics! macros are no-ops —
        // the call should succeed regardless.
        let delta = CaptureStats {
            packets: 1234,
            drops: 5,
            freeze_count: 0,
        };
        record_capture_delta("test_iface", &delta);
    }
}