nym_statistics_common/clients/
mod.rs

1// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::report::client::{ClientStatsReport, OsInformation};
5
6use nym_task::ShutdownToken;
7use time::{OffsetDateTime, Time};
8use tokio::sync::mpsc::UnboundedSender;
9
10/// Active gateway connection statistics.
11pub mod gateway_conn_statistics;
12
13/// Nym API connection statistics.
14pub mod nym_api_statistics;
15
16/// Packet count based statistics.
17pub mod packet_statistics;
18
19pub mod connection;
20
21/// Channel receiving generic stats events to be used by a statistics aggregator.
22pub type ClientStatsReceiver = tokio::sync::mpsc::UnboundedReceiver<ClientStatsEvents>;
23
24/// Channel allowing generic statistics events to be reported to a stats event aggregator
25#[derive(Clone)]
26pub struct ClientStatsSender {
27    stats_tx: Option<UnboundedSender<ClientStatsEvents>>,
28    shutdown_token: ShutdownToken,
29}
30
31impl ClientStatsSender {
32    /// Create a new statistics Sender
33    pub fn new(
34        stats_tx: Option<UnboundedSender<ClientStatsEvents>>,
35        shutdown_token: ShutdownToken,
36    ) -> Self {
37        ClientStatsSender {
38            stats_tx,
39            shutdown_token,
40        }
41    }
42
43    /// Report a statistics event using the sender.
44    pub fn report(&self, event: ClientStatsEvents) {
45        if let Some(tx) = &self.stats_tx
46            && let Err(err) = tx.send(event)
47            && !self.shutdown_token.is_cancelled()
48        {
49            log::error!("Failed to send stats event: {err}");
50        }
51    }
52}
53
54/// Client Statistics events (static for now)
55pub enum ClientStatsEvents {
56    /// Packet count events
57    PacketStatistics(packet_statistics::PacketStatisticsEvent),
58    /// Gateway Connection events
59    GatewayConn(gateway_conn_statistics::GatewayStatsEvent),
60    /// Nym API connection events
61    NymApi(nym_api_statistics::NymApiStatsEvent),
62    /// Credential events
63    Connection(connection::ConnectionStatsEvent),
64}
65
66/// Controls stats event handling and reporting
67pub struct ClientStatsController {
68    //static infos
69    last_update_time: OffsetDateTime,
70    client_id: String,
71    client_type: String,
72    os_information: OsInformation,
73
74    // stats collection modules
75    packet_stats: packet_statistics::PacketStatisticsControl,
76    gateway_conn_stats: gateway_conn_statistics::GatewayStatsControl,
77    nym_api_stats: nym_api_statistics::NymApiStatsControl,
78    connection_stats: connection::ConnectionStatsControl,
79}
80
81impl ClientStatsController {
82    /// Creates a ClientStatsController given a client_id
83    pub fn new(client_id: String, client_type: String) -> Self {
84        ClientStatsController {
85            last_update_time: ClientStatsController::get_update_time(),
86            client_id,
87            client_type,
88            os_information: OsInformation::new(),
89            packet_stats: Default::default(),
90            gateway_conn_stats: Default::default(),
91            nym_api_stats: Default::default(),
92            connection_stats: Default::default(),
93        }
94    }
95    /// Returns a static ClientStatsReport that can be sent somewhere
96    pub fn build_report(&self) -> ClientStatsReport {
97        ClientStatsReport {
98            last_update_time: self.last_update_time,
99            client_id: self.client_id.clone(),
100            client_type: self.client_type.clone(),
101            os_information: self.os_information.clone(),
102            packet_stats: self.packet_stats.report(),
103            gateway_conn_stats: self.gateway_conn_stats.report(),
104            nym_api_stats: self.nym_api_stats.report(),
105            connection_stats: self.connection_stats.report(),
106            ..Default::default()
107        }
108    }
109
110    /// Handle and dispatch incoming stats event
111    pub fn handle_event(&mut self, stats_event: ClientStatsEvents) {
112        match stats_event {
113            ClientStatsEvents::PacketStatistics(event) => self.packet_stats.handle_event(event),
114            ClientStatsEvents::GatewayConn(event) => self.gateway_conn_stats.handle_event(event),
115            ClientStatsEvents::NymApi(event) => self.nym_api_stats.handle_event(event),
116            ClientStatsEvents::Connection(event) => self.connection_stats.handle_event(event),
117        }
118    }
119
120    /// Reset the metrics to their initial state.
121    ///
122    /// Used to periodically reset the metrics in accordance with periodic reporting strategy
123    pub fn reset(&mut self) {
124        self.nym_api_stats = Default::default();
125        self.gateway_conn_stats = Default::default();
126        self.connection_stats = Default::default();
127        //no periodic reset for packet stats
128
129        self.last_update_time = ClientStatsController::get_update_time();
130    }
131
132    /// snapshot the current state of the metrics for module that needs it
133    pub fn snapshot(&mut self) {
134        //no snapshot for gateway_conn_stats
135        //no snapshot for nym_api_stats
136        self.packet_stats.snapshot();
137    }
138
139    pub fn local_report(&mut self) {
140        self.packet_stats.local_report();
141        self.gateway_conn_stats.local_report();
142        self.nym_api_stats.local_report();
143    }
144
145    fn get_update_time() -> OffsetDateTime {
146        let now = OffsetDateTime::now_utc();
147        #[allow(clippy::unwrap_used)]
148        //Safety : 0 is always a valid number of seconds, hours and minutes comes from a valid source
149        let new_time = Time::from_hms(now.hour(), now.minute(), 0).unwrap();
150        //allows a bigger anonymity by hiding exact sending time
151        now.replace_time(new_time)
152    }
153}