offline_protocol/telemetry/metrics_snapshot.rs
1//! Periodic snapshot of protocol-wide counters and per-transport metrics.
2//!
3//! Emitted on the cadence configured by
4//! [`crate::telemetry::TelemetryConfig::metrics_cadence`] from
5//! `OfflineProtocol::process()`. When cadence is `None`, no periodic emission
6//! occurs — apps fall back to pull-based access via `TransportManager::metrics()`.
7//!
8//! The frame reuses [`TransportMetrics`] verbatim; there is no parallel
9//! "rich" struct. Extending that struct (when UniFFI bindings catch up)
10//! automatically widens the frame.
11
12use offline_protocol_reliability::{DeduplicatorStats, RetryQueueStats};
13use offline_protocol_transport::{TransportMetrics, TransportType};
14
15/// Snapshot of every push-able counter the SDK exposes.
16///
17/// The inner collections are pre-materialized at aggregation time so the sink
18/// receives owned-but-cheap data — no iteration, no locking on the sink side.
19#[derive(Debug, Clone)]
20pub struct MetricsFrame {
21 /// Emission timestamp (milliseconds since Unix epoch).
22 pub timestamp_ms: i64,
23 /// Per-transport metrics for every transport currently reporting
24 /// `TransportStatus::Available`.
25 pub transports: Vec<(TransportType, TransportMetrics)>,
26 /// Retry-queue statistics (pending + ready + per-priority counts).
27 pub retry_queue: RetryQueueStats,
28 /// Deduplicator statistics (tracked ids, capacity, mode).
29 pub dedup: DeduplicatorStats,
30 /// Number of messages awaiting acknowledgement.
31 pub ack_pending: usize,
32 /// Number of currently known neighbors.
33 pub neighbor_count: usize,
34 /// Whether this node is currently acting as a relay on the mesh.
35 ///
36 /// Reflects `RelayManager::current_role()` at the moment of emission.
37 /// Named deliberately — this is a local-role flag, not a peer count.
38 /// A future record with an explicit per-peer-relay registry may add a
39 /// `relay_peer_count` field alongside; those semantics are orthogonal.
40 pub is_local_relay: bool,
41 /// Currently selected transport, if any.
42 pub current_transport: Option<TransportType>,
43}
44
45impl MetricsFrame {
46 /// Stable OTel-friendly name for this record.
47 pub const NAME: &'static str = "protocol.metrics.snapshot";
48
49 /// Returns the stable telemetry name.
50 pub fn telemetry_name(&self) -> &'static str {
51 Self::NAME
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use offline_protocol_reliability::DeduplicatorMode;
59
60 fn sample_frame() -> MetricsFrame {
61 MetricsFrame {
62 timestamp_ms: 0,
63 transports: Vec::new(),
64 retry_queue: RetryQueueStats {
65 total_count: 0,
66 ready_count: 0,
67 critical_priority_count: 0,
68 high_priority_count: 0,
69 medium_priority_count: 0,
70 low_priority_count: 0,
71 },
72 dedup: DeduplicatorStats {
73 total_tracked: 0,
74 recent_tracked: 0,
75 capacity_used_percent: 0,
76 false_positive_rate: None,
77 mode: DeduplicatorMode::HashMap,
78 },
79 ack_pending: 0,
80 neighbor_count: 0,
81 is_local_relay: false,
82 current_transport: None,
83 }
84 }
85
86 #[test]
87 fn name_is_stable() {
88 assert_eq!(MetricsFrame::NAME, "protocol.metrics.snapshot");
89 assert_eq!(sample_frame().telemetry_name(), "protocol.metrics.snapshot");
90 }
91}