kaspa_metrics_core/
data.rs

1use crate::error::Error;
2use crate::result::Result;
3use borsh::{BorshDeserialize, BorshSerialize};
4use kaspa_rpc_core::GetMetricsResponse;
5use separator::{separated_float, separated_int, separated_uint_with_output, Separatable};
6use serde::{Deserialize, Serialize};
7use workflow_core::enums::Describe;
8
9#[derive(Describe, Debug, Clone, Copy, Eq, PartialEq, Hash, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
10pub enum MetricGroup {
11    System,
12    Storage,
13    Bandwidth,
14    Connections,
15    Network,
16}
17
18impl std::fmt::Display for MetricGroup {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            MetricGroup::System => write!(f, "system"),
22            MetricGroup::Storage => write!(f, "storage"),
23            MetricGroup::Bandwidth => write!(f, "bandwidth"),
24            MetricGroup::Connections => write!(f, "connections"),
25            MetricGroup::Network => write!(f, "network"),
26        }
27    }
28}
29
30impl MetricGroup {
31    pub fn title(&self) -> &str {
32        match self {
33            MetricGroup::System => "System",
34            MetricGroup::Storage => "Storage",
35            MetricGroup::Bandwidth => "Bandwidth",
36            MetricGroup::Connections => "Connections",
37            MetricGroup::Network => "Network",
38        }
39    }
40}
41
42impl MetricGroup {
43    pub fn metrics(&self) -> impl Iterator<Item = &Metric> {
44        match self {
45            MetricGroup::System => [
46                Metric::NodeCpuUsage,
47                Metric::NodeResidentSetSizeBytes,
48                Metric::NodeVirtualMemorySizeBytes,
49                Metric::NodeFileHandlesCount,
50            ]
51            .as_slice()
52            .iter(),
53            MetricGroup::Storage => [
54                Metric::NodeDiskIoReadBytes,
55                Metric::NodeDiskIoReadPerSec,
56                Metric::NodeDiskIoWriteBytes,
57                Metric::NodeDiskIoWritePerSec,
58                Metric::NodeStorageSizeBytes,
59            ]
60            .as_slice()
61            .iter(),
62            MetricGroup::Bandwidth => [
63                Metric::NodeTotalBytesTx,
64                Metric::NodeTotalBytesTxPerSecond,
65                Metric::NodeTotalBytesRx,
66                Metric::NodeTotalBytesRxPerSecond,
67                Metric::NodeBorshBytesTx,
68                Metric::NodeBorshBytesTxPerSecond,
69                Metric::NodeBorshBytesRx,
70                Metric::NodeBorshBytesRxPerSecond,
71                Metric::NodeP2pBytesTx,
72                Metric::NodeP2pBytesTxPerSecond,
73                Metric::NodeP2pBytesRx,
74                Metric::NodeP2pBytesRxPerSecond,
75                Metric::NodeGrpcUserBytesTx,
76                Metric::NodeGrpcUserBytesTxPerSecond,
77                Metric::NodeGrpcUserBytesRx,
78                Metric::NodeGrpcUserBytesRxPerSecond,
79                Metric::NodeJsonBytesTx,
80                Metric::NodeJsonBytesTxPerSecond,
81                Metric::NodeJsonBytesRx,
82                Metric::NodeJsonBytesRxPerSecond,
83            ]
84            .as_slice()
85            .iter(),
86            MetricGroup::Connections => [
87                Metric::NodeActivePeers,
88                Metric::NodeBorshLiveConnections,
89                Metric::NodeBorshConnectionAttempts,
90                Metric::NodeBorshHandshakeFailures,
91                Metric::NodeJsonLiveConnections,
92                Metric::NodeJsonConnectionAttempts,
93                Metric::NodeJsonHandshakeFailures,
94            ]
95            .as_slice()
96            .iter(),
97            MetricGroup::Network => [
98                Metric::NodeBlocksSubmittedCount,
99                Metric::NodeHeadersProcessedCount,
100                Metric::NodeDependenciesProcessedCount,
101                Metric::NodeBodiesProcessedCount,
102                Metric::NodeTransactionsProcessedCount,
103                Metric::NodeChainBlocksProcessedCount,
104                Metric::NodeMassProcessedCount,
105                Metric::NodeDatabaseBlocksCount,
106                Metric::NodeDatabaseHeadersCount,
107                Metric::NetworkMempoolSize,
108                Metric::NetworkTransactionsPerSecond,
109                Metric::NetworkTipHashesCount,
110                Metric::NetworkDifficulty,
111                Metric::NetworkPastMedianTime,
112                Metric::NetworkVirtualParentHashesCount,
113                Metric::NetworkVirtualDaaScore,
114            ]
115            .as_slice()
116            .iter(),
117        }
118    }
119}
120
121impl From<Metric> for MetricGroup {
122    fn from(value: Metric) -> Self {
123        match value {
124            Metric::NodeCpuUsage | Metric::NodeResidentSetSizeBytes | Metric::NodeVirtualMemorySizeBytes => MetricGroup::System,
125            // --
126            Metric::NodeFileHandlesCount
127            | Metric::NodeDiskIoReadBytes
128            | Metric::NodeDiskIoWriteBytes
129            | Metric::NodeDiskIoReadPerSec
130            | Metric::NodeDiskIoWritePerSec
131            | Metric::NodeStorageSizeBytes => MetricGroup::Storage,
132            // --
133            Metric::NodeBorshLiveConnections
134            | Metric::NodeBorshConnectionAttempts
135            | Metric::NodeBorshHandshakeFailures
136            | Metric::NodeJsonLiveConnections
137            | Metric::NodeJsonConnectionAttempts
138            | Metric::NodeJsonHandshakeFailures
139            | Metric::NodeActivePeers => MetricGroup::Connections,
140            // --
141            Metric::NodeBorshBytesRx
142            | Metric::NodeBorshBytesTx
143            | Metric::NodeJsonBytesTx
144            | Metric::NodeJsonBytesRx
145            | Metric::NodeP2pBytesTx
146            | Metric::NodeP2pBytesRx
147            | Metric::NodeGrpcUserBytesTx
148            | Metric::NodeGrpcUserBytesRx
149            | Metric::NodeTotalBytesRx
150            | Metric::NodeTotalBytesTx
151            | Metric::NodeBorshBytesRxPerSecond
152            | Metric::NodeBorshBytesTxPerSecond
153            | Metric::NodeJsonBytesTxPerSecond
154            | Metric::NodeJsonBytesRxPerSecond
155            | Metric::NodeP2pBytesTxPerSecond
156            | Metric::NodeP2pBytesRxPerSecond
157            | Metric::NodeGrpcUserBytesTxPerSecond
158            | Metric::NodeGrpcUserBytesRxPerSecond
159            | Metric::NodeTotalBytesRxPerSecond
160            | Metric::NodeTotalBytesTxPerSecond => MetricGroup::Bandwidth,
161            // --
162            Metric::NodeBlocksSubmittedCount
163            | Metric::NodeHeadersProcessedCount
164            | Metric::NodeDependenciesProcessedCount
165            | Metric::NodeBodiesProcessedCount
166            | Metric::NodeTransactionsProcessedCount
167            | Metric::NodeChainBlocksProcessedCount
168            | Metric::NodeMassProcessedCount
169            // --
170            | Metric::NodeDatabaseBlocksCount
171            | Metric::NodeDatabaseHeadersCount
172            // --
173            | Metric::NetworkMempoolSize
174            | Metric::NetworkTransactionsPerSecond
175            | Metric::NetworkTipHashesCount
176            | Metric::NetworkDifficulty
177            | Metric::NetworkPastMedianTime
178            | Metric::NetworkVirtualParentHashesCount
179            | Metric::NetworkVirtualDaaScore => MetricGroup::Network,
180        }
181    }
182}
183
184#[derive(Describe, Debug, Clone, Copy, Eq, PartialEq, Hash, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
185#[serde(rename_all = "kebab-case")]
186pub enum Metric {
187    // NodeCpuCores is used to normalize NodeCpuUsage metric
188    // NodeCpuCores
189    NodeCpuUsage,
190    NodeResidentSetSizeBytes,
191    NodeVirtualMemorySizeBytes,
192    // ---
193    NodeFileHandlesCount,
194    NodeDiskIoReadBytes,
195    NodeDiskIoWriteBytes,
196    NodeDiskIoReadPerSec,
197    NodeDiskIoWritePerSec,
198    NodeStorageSizeBytes,
199    // ---
200    NodeActivePeers,
201    NodeBorshLiveConnections,
202    NodeBorshConnectionAttempts,
203    NodeBorshHandshakeFailures,
204    NodeJsonLiveConnections,
205    NodeJsonConnectionAttempts,
206    NodeJsonHandshakeFailures,
207    // ---
208    NodeTotalBytesTx,
209    NodeTotalBytesRx,
210    NodeTotalBytesTxPerSecond,
211    NodeTotalBytesRxPerSecond,
212
213    NodeP2pBytesTx,
214    NodeP2pBytesRx,
215    NodeP2pBytesTxPerSecond,
216    NodeP2pBytesRxPerSecond,
217
218    NodeBorshBytesTx,
219    NodeBorshBytesRx,
220    NodeBorshBytesTxPerSecond,
221    NodeBorshBytesRxPerSecond,
222
223    NodeGrpcUserBytesTx,
224    NodeGrpcUserBytesRx,
225    NodeGrpcUserBytesTxPerSecond,
226    NodeGrpcUserBytesRxPerSecond,
227
228    NodeJsonBytesTx,
229    NodeJsonBytesRx,
230    NodeJsonBytesTxPerSecond,
231    NodeJsonBytesRxPerSecond,
232
233    // ---
234    NodeBlocksSubmittedCount,
235    NodeHeadersProcessedCount,
236    NodeDependenciesProcessedCount,
237    NodeBodiesProcessedCount,
238    NodeTransactionsProcessedCount,
239    NodeChainBlocksProcessedCount,
240    NodeMassProcessedCount,
241    // --
242    NodeDatabaseBlocksCount,
243    NodeDatabaseHeadersCount,
244    // --
245    NetworkMempoolSize,
246    NetworkTransactionsPerSecond,
247    NetworkTipHashesCount,
248    NetworkDifficulty,
249    NetworkPastMedianTime,
250    NetworkVirtualParentHashesCount,
251    NetworkVirtualDaaScore,
252}
253
254impl Metric {
255    pub fn is_key_performance_metric(&self) -> bool {
256        matches!(
257            self,
258            Metric::NodeCpuUsage
259                | Metric::NodeResidentSetSizeBytes
260                | Metric::NodeFileHandlesCount
261                | Metric::NodeDiskIoReadBytes
262                | Metric::NodeDiskIoWriteBytes
263                | Metric::NodeDiskIoReadPerSec
264                | Metric::NodeDiskIoWritePerSec
265                | Metric::NodeBorshBytesTx
266                | Metric::NodeBorshBytesRx
267                | Metric::NodeP2pBytesTx
268                | Metric::NodeP2pBytesRx
269                | Metric::NodeGrpcUserBytesTx
270                | Metric::NodeGrpcUserBytesRx
271                | Metric::NodeTotalBytesTx
272                | Metric::NodeTotalBytesRx
273                | Metric::NodeBorshBytesTxPerSecond
274                | Metric::NodeBorshBytesRxPerSecond
275                | Metric::NodeP2pBytesTxPerSecond
276                | Metric::NodeP2pBytesRxPerSecond
277                | Metric::NodeGrpcUserBytesTxPerSecond
278                | Metric::NodeGrpcUserBytesRxPerSecond
279                | Metric::NodeTotalBytesTxPerSecond
280                | Metric::NodeTotalBytesRxPerSecond
281                | Metric::NodeActivePeers
282                | Metric::NetworkMempoolSize
283                | Metric::NetworkTipHashesCount
284                | Metric::NetworkTransactionsPerSecond
285                | Metric::NodeTransactionsProcessedCount
286                | Metric::NodeDatabaseBlocksCount
287                | Metric::NodeDatabaseHeadersCount
288        )
289    }
290
291    pub fn format(&self, f: f64, si: bool, short: bool) -> String {
292        match self {
293            Metric::NodeCpuUsage => {
294                if f.is_nan() {
295                    "---".to_string()
296                } else {
297                    format!("{:1.2}%", f)
298                }
299            }
300            Metric::NodeResidentSetSizeBytes => as_mb(f, si, short),
301            Metric::NodeVirtualMemorySizeBytes => as_mb(f, si, short),
302            Metric::NodeFileHandlesCount => f.separated_string(),
303            // --
304            Metric::NodeDiskIoReadBytes => as_mb(f, si, short),
305            Metric::NodeDiskIoWriteBytes => as_mb(f, si, short),
306            Metric::NodeDiskIoReadPerSec => format!("{}/s", as_data_size(f, si)),
307            Metric::NodeDiskIoWritePerSec => format!("{}/s", as_data_size(f, si)),
308            Metric::NodeStorageSizeBytes => as_gb(f, si, short),
309            // --
310            Metric::NodeBorshLiveConnections => f.trunc().separated_string(),
311            Metric::NodeBorshConnectionAttempts => f.trunc().separated_string(),
312            Metric::NodeBorshHandshakeFailures => f.trunc().separated_string(),
313            Metric::NodeJsonLiveConnections => f.trunc().separated_string(),
314            Metric::NodeJsonConnectionAttempts => f.trunc().separated_string(),
315            Metric::NodeJsonHandshakeFailures => f.trunc().separated_string(),
316            Metric::NodeActivePeers => f.trunc().separated_string(),
317            // --
318            Metric::NodeBorshBytesTx => as_data_size(f, si),
319            Metric::NodeBorshBytesRx => as_data_size(f, si),
320            Metric::NodeJsonBytesTx => as_data_size(f, si),
321            Metric::NodeJsonBytesRx => as_data_size(f, si),
322            Metric::NodeP2pBytesTx => as_data_size(f, si),
323            Metric::NodeP2pBytesRx => as_data_size(f, si),
324            Metric::NodeGrpcUserBytesTx => as_data_size(f, si),
325            Metric::NodeGrpcUserBytesRx => as_data_size(f, si),
326            Metric::NodeTotalBytesTx => as_data_size(f, si),
327            Metric::NodeTotalBytesRx => as_data_size(f, si),
328            // --
329            Metric::NodeBorshBytesTxPerSecond => format!("{}/s", as_kb(f, si, short)),
330            Metric::NodeBorshBytesRxPerSecond => format!("{}/s", as_kb(f, si, short)),
331            Metric::NodeJsonBytesTxPerSecond => format!("{}/s", as_kb(f, si, short)),
332            Metric::NodeJsonBytesRxPerSecond => format!("{}/s", as_kb(f, si, short)),
333            Metric::NodeP2pBytesTxPerSecond => format!("{}/s", as_kb(f, si, short)),
334            Metric::NodeP2pBytesRxPerSecond => format!("{}/s", as_kb(f, si, short)),
335            Metric::NodeGrpcUserBytesTxPerSecond => format!("{}/s", as_kb(f, si, short)),
336            Metric::NodeGrpcUserBytesRxPerSecond => format!("{}/s", as_kb(f, si, short)),
337            Metric::NodeTotalBytesTxPerSecond => format!("{}/s", as_kb(f, si, short)),
338            Metric::NodeTotalBytesRxPerSecond => format!("{}/s", as_kb(f, si, short)),
339            // --
340            Metric::NodeBlocksSubmittedCount => format_as_float(f, short),
341            Metric::NodeHeadersProcessedCount => format_as_float(f, short),
342            Metric::NodeDependenciesProcessedCount => format_as_float(f, short),
343            Metric::NodeBodiesProcessedCount => format_as_float(f, short),
344            Metric::NodeTransactionsProcessedCount => format_as_float(f, short),
345            Metric::NodeChainBlocksProcessedCount => format_as_float(f, short),
346            Metric::NodeMassProcessedCount => format_as_float(f, short),
347            // --
348            Metric::NodeDatabaseHeadersCount => format_as_float(f, short),
349            Metric::NodeDatabaseBlocksCount => format_as_float(f, short),
350            // --
351            Metric::NetworkMempoolSize => format_as_float(f.trunc(), short),
352            Metric::NetworkTransactionsPerSecond => format_as_float(f.trunc(), short),
353            Metric::NetworkTipHashesCount => format_as_float(f, short),
354            Metric::NetworkDifficulty => format_as_float(f, short),
355            Metric::NetworkPastMedianTime => format_as_float(f, false),
356            Metric::NetworkVirtualParentHashesCount => format_as_float(f, short),
357            Metric::NetworkVirtualDaaScore => format_as_float(f, false),
358        }
359    }
360
361    pub fn title(&self) -> (&str, &str) {
362        match self {
363            Metric::NodeCpuUsage => ("CPU", "CPU"),
364            Metric::NodeResidentSetSizeBytes => ("Resident Memory", "Memory"),
365            Metric::NodeVirtualMemorySizeBytes => ("Virtual Memory", "Virtual"),
366            // --
367            Metric::NodeFileHandlesCount => ("File Handles", "Handles"),
368            Metric::NodeDiskIoReadBytes => ("Storage Read", "Stor Read"),
369            Metric::NodeDiskIoWriteBytes => ("Storage Write", "Stor Write"),
370            Metric::NodeDiskIoReadPerSec => ("Storage Read/s", "Stor Read"),
371            Metric::NodeDiskIoWritePerSec => ("Storage Write/s", "Stor Write"),
372            Metric::NodeStorageSizeBytes => ("Storage Size", "Stor Size"),
373            // --
374            Metric::NodeActivePeers => ("Active p2p Peers", "Peers"),
375            Metric::NodeBorshLiveConnections => ("Borsh Active Connections", "Borsh Conn"),
376            Metric::NodeBorshConnectionAttempts => ("Borsh Connection Attempts", "Borsh Conn Att"),
377            Metric::NodeBorshHandshakeFailures => ("Borsh Handshake Failures", "Borsh Failures"),
378            Metric::NodeJsonLiveConnections => ("Json Active Connections", "Json Conn"),
379            Metric::NodeJsonConnectionAttempts => ("Json Connection Attempts", "Json Conn Att"),
380            Metric::NodeJsonHandshakeFailures => ("Json Handshake Failures", "Json Failures"),
381            // --
382            Metric::NodeBorshBytesTx => ("wRPC Borsh Tx", "Borsh Tx"),
383            Metric::NodeBorshBytesRx => ("wRPC Borsh Rx", "Borsh Rx"),
384            Metric::NodeJsonBytesTx => ("wRPC JSON Tx", "Json Tx"),
385            Metric::NodeJsonBytesRx => ("wRPC JSON Rx", "Json Rx"),
386            Metric::NodeP2pBytesTx => ("p2p Tx", "p2p Tx"),
387            Metric::NodeP2pBytesRx => ("p2p Rx", "p2p Rx"),
388            Metric::NodeGrpcUserBytesTx => ("gRPC Tx", "gRPC Tx"),
389            Metric::NodeGrpcUserBytesRx => ("gRPC Rx", "gRPC Rx"),
390            Metric::NodeTotalBytesTx => ("Total Tx", "Total Tx"),
391            Metric::NodeTotalBytesRx => ("Total Rx", "Total Rx"),
392            // --
393            Metric::NodeBorshBytesTxPerSecond => ("wRPC Borsh Tx/s", "Borsh Tx/s"),
394            Metric::NodeBorshBytesRxPerSecond => ("wRPC Borsh Rx/s", "Borsh Rx/s"),
395            Metric::NodeJsonBytesTxPerSecond => ("wRPC JSON Tx/s", "JSON Tx/s"),
396            Metric::NodeJsonBytesRxPerSecond => ("wRPC JSON Rx/s", "JSON Rx/s"),
397            Metric::NodeP2pBytesTxPerSecond => ("p2p Tx/s", "p2p Tx/s"),
398            Metric::NodeP2pBytesRxPerSecond => ("p2p Rx/s", "p2p Rx/s"),
399            Metric::NodeGrpcUserBytesTxPerSecond => ("gRPC Tx/s", "gRPC Tx/s"),
400            Metric::NodeGrpcUserBytesRxPerSecond => ("gRPC Rx/s", "gRPC Rx/s"),
401            Metric::NodeTotalBytesTxPerSecond => ("Total Tx/s", "Total Tx/s"),
402            Metric::NodeTotalBytesRxPerSecond => ("Total Rx/s", "Total Rx/s"),
403            // --
404            Metric::NodeBlocksSubmittedCount => ("Submitted Blocks", "Blocks"),
405            Metric::NodeHeadersProcessedCount => ("Processed Headers", "Headers"),
406            Metric::NodeDependenciesProcessedCount => ("Processed Dependencies", "Dependencies"),
407            Metric::NodeBodiesProcessedCount => ("Processed Bodies", "Bodies"),
408            Metric::NodeTransactionsProcessedCount => ("Processed Transactions", "Transactions"),
409            Metric::NodeChainBlocksProcessedCount => ("Chain Blocks", "Chain Blocks"),
410            Metric::NodeMassProcessedCount => ("Processed Mass Counts", "Mass Processed"),
411            // --
412            Metric::NodeDatabaseBlocksCount => ("Database Blocks", "DB Blocks"),
413            Metric::NodeDatabaseHeadersCount => ("Database Headers", "DB Headers"),
414            // --
415            Metric::NetworkMempoolSize => ("Mempool Size", "Mempool"),
416            Metric::NetworkTransactionsPerSecond => ("TPS", "TPS"),
417            Metric::NetworkTipHashesCount => ("Tip Hashes", "Tip Hashes"),
418            Metric::NetworkDifficulty => ("Network Difficulty", "Difficulty"),
419            Metric::NetworkPastMedianTime => ("Past Median Time", "MT"),
420            Metric::NetworkVirtualParentHashesCount => ("Virtual Parent Hashes", "Virt Parents"),
421            Metric::NetworkVirtualDaaScore => ("Virtual DAA Score", "DAA"),
422        }
423    }
424}
425
426#[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
427pub struct MetricsData {
428    pub unixtime_millis: f64,
429
430    // ---
431    pub node_resident_set_size_bytes: u64,
432    pub node_virtual_memory_size_bytes: u64,
433    pub node_cpu_cores: u32,
434    pub node_cpu_usage: f32,
435    pub node_file_handles: u32,
436    // ---
437    pub node_disk_io_read_bytes: u64,
438    pub node_disk_io_write_bytes: u64,
439    pub node_disk_io_read_per_sec: f32,
440    pub node_disk_io_write_per_sec: f32,
441    pub node_storage_size_bytes: u64,
442    // ---
443    pub node_borsh_live_connections: u32,
444    pub node_borsh_connection_attempts: u64,
445    pub node_borsh_handshake_failures: u64,
446    pub node_json_live_connections: u32,
447    pub node_json_connection_attempts: u64,
448    pub node_json_handshake_failures: u64,
449    pub node_active_peers: u32,
450    // ---
451    pub node_borsh_bytes_tx: u64,
452    pub node_borsh_bytes_rx: u64,
453    pub node_json_bytes_tx: u64,
454    pub node_json_bytes_rx: u64,
455    pub node_p2p_bytes_tx: u64,
456    pub node_p2p_bytes_rx: u64,
457    pub node_grpc_user_bytes_tx: u64,
458    pub node_grpc_user_bytes_rx: u64,
459    pub node_total_bytes_tx: u64,
460    pub node_total_bytes_rx: u64,
461    // ---
462    pub node_blocks_submitted_count: u64,
463    pub node_headers_processed_count: u64,
464    pub node_dependencies_processed_count: u64,
465    pub node_bodies_processed_count: u64,
466    pub node_transactions_processed_count: u64,
467    pub node_chain_blocks_processed_count: u64,
468    pub node_mass_processed_count: u64,
469    // ---
470    pub node_database_blocks_count: u64,
471    pub node_database_headers_count: u64,
472    // --
473    pub network_mempool_size: u64,
474    pub network_tip_hashes_count: u32,
475    pub network_difficulty: f64,
476    pub network_past_median_time: u64,
477    pub network_virtual_parent_hashes_count: u32,
478    pub network_virtual_daa_score: u64,
479}
480
481impl MetricsData {
482    pub fn new(unixtime: f64) -> Self {
483        Self { unixtime_millis: unixtime, ..Default::default() }
484    }
485}
486
487impl TryFrom<GetMetricsResponse> for MetricsData {
488    type Error = Error;
489    fn try_from(response: GetMetricsResponse) -> Result<Self> {
490        let GetMetricsResponse {
491            server_time,
492            consensus_metrics,
493            connection_metrics,
494            bandwidth_metrics,
495            process_metrics,
496            storage_metrics,
497            custom_metrics: _,
498        } = response; //rpc.get_metrics(true, true, true, true, true, false).await?;
499
500        let consensus_metrics = consensus_metrics.ok_or(Error::MissingData("Consensus Metrics"))?;
501        let connection_metrics = connection_metrics.ok_or(Error::MissingData("Connection Metrics"))?;
502        let bandwidth_metrics = bandwidth_metrics.ok_or(Error::MissingData("Bandwidth Metrics"))?;
503        let process_metrics = process_metrics.ok_or(Error::MissingData("Process Metrics"))?;
504        let storage_metrics = storage_metrics.ok_or(Error::MissingData("Storage Metrics"))?;
505
506        Ok(MetricsData {
507            unixtime_millis: server_time as f64,
508
509            node_blocks_submitted_count: consensus_metrics.node_blocks_submitted_count,
510            node_headers_processed_count: consensus_metrics.node_headers_processed_count,
511            node_dependencies_processed_count: consensus_metrics.node_dependencies_processed_count,
512            node_bodies_processed_count: consensus_metrics.node_bodies_processed_count,
513            node_transactions_processed_count: consensus_metrics.node_transactions_processed_count,
514            node_chain_blocks_processed_count: consensus_metrics.node_chain_blocks_processed_count,
515            node_mass_processed_count: consensus_metrics.node_mass_processed_count,
516            // --
517            node_database_blocks_count: consensus_metrics.node_database_blocks_count,
518            node_database_headers_count: consensus_metrics.node_database_headers_count,
519            network_mempool_size: consensus_metrics.network_mempool_size,
520            network_tip_hashes_count: consensus_metrics.network_tip_hashes_count,
521            network_difficulty: consensus_metrics.network_difficulty,
522            network_past_median_time: consensus_metrics.network_past_median_time,
523            network_virtual_parent_hashes_count: consensus_metrics.network_virtual_parent_hashes_count,
524            network_virtual_daa_score: consensus_metrics.network_virtual_daa_score,
525
526            node_borsh_live_connections: connection_metrics.borsh_live_connections,
527            node_borsh_connection_attempts: connection_metrics.borsh_connection_attempts,
528            node_borsh_handshake_failures: connection_metrics.borsh_handshake_failures,
529            node_json_live_connections: connection_metrics.json_live_connections,
530            node_json_connection_attempts: connection_metrics.json_connection_attempts,
531            node_json_handshake_failures: connection_metrics.json_handshake_failures,
532            node_active_peers: connection_metrics.active_peers,
533
534            node_borsh_bytes_tx: bandwidth_metrics.borsh_bytes_tx,
535            node_borsh_bytes_rx: bandwidth_metrics.borsh_bytes_rx,
536            node_json_bytes_tx: bandwidth_metrics.json_bytes_tx,
537            node_json_bytes_rx: bandwidth_metrics.json_bytes_rx,
538            node_p2p_bytes_tx: bandwidth_metrics.p2p_bytes_tx,
539            node_p2p_bytes_rx: bandwidth_metrics.p2p_bytes_rx,
540            node_grpc_user_bytes_tx: bandwidth_metrics.grpc_bytes_tx,
541            node_grpc_user_bytes_rx: bandwidth_metrics.grpc_bytes_rx,
542
543            node_total_bytes_tx: bandwidth_metrics.borsh_bytes_tx
544                + bandwidth_metrics.json_bytes_tx
545                + bandwidth_metrics.p2p_bytes_tx
546                + bandwidth_metrics.grpc_bytes_tx,
547
548            node_total_bytes_rx: bandwidth_metrics.borsh_bytes_rx
549                + bandwidth_metrics.json_bytes_rx
550                + bandwidth_metrics.p2p_bytes_rx
551                + bandwidth_metrics.grpc_bytes_rx,
552
553            node_resident_set_size_bytes: process_metrics.resident_set_size,
554            node_virtual_memory_size_bytes: process_metrics.virtual_memory_size,
555            node_cpu_cores: process_metrics.core_num,
556            node_cpu_usage: process_metrics.cpu_usage,
557            node_file_handles: process_metrics.fd_num,
558            node_disk_io_read_bytes: process_metrics.disk_io_read_bytes,
559            node_disk_io_write_bytes: process_metrics.disk_io_write_bytes,
560            node_disk_io_read_per_sec: process_metrics.disk_io_read_per_sec,
561            node_disk_io_write_per_sec: process_metrics.disk_io_write_per_sec,
562
563            node_storage_size_bytes: storage_metrics.storage_size_bytes,
564        })
565    }
566}
567
568#[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
569pub struct MetricsSnapshot {
570    pub data: MetricsData,
571
572    pub unixtime_millis: f64,
573    pub duration_millis: f64,
574    // ---
575    pub node_resident_set_size_bytes: f64,
576    pub node_virtual_memory_size_bytes: f64,
577    pub node_cpu_cores: f64,
578    pub node_cpu_usage: f64,
579    // ---
580    pub node_file_handles: f64,
581    pub node_disk_io_read_bytes: f64,
582    pub node_disk_io_write_bytes: f64,
583    pub node_disk_io_read_per_sec: f64,
584    pub node_disk_io_write_per_sec: f64,
585    // ---
586    pub node_borsh_active_connections: f64,
587    pub node_borsh_connection_attempts: f64,
588    pub node_borsh_handshake_failures: f64,
589    pub node_json_active_connections: f64,
590    pub node_json_connection_attempts: f64,
591    pub node_json_handshake_failures: f64,
592    pub node_active_peers: f64,
593    // ---
594    pub node_borsh_bytes_tx: f64,
595    pub node_borsh_bytes_rx: f64,
596    pub node_json_bytes_tx: f64,
597    pub node_json_bytes_rx: f64,
598    pub node_p2p_bytes_tx: f64,
599    pub node_p2p_bytes_rx: f64,
600    pub node_grpc_user_bytes_tx: f64,
601    pub node_grpc_user_bytes_rx: f64,
602    pub node_total_bytes_tx: f64,
603    pub node_total_bytes_rx: f64,
604
605    pub node_borsh_bytes_tx_per_second: f64,
606    pub node_borsh_bytes_rx_per_second: f64,
607    pub node_json_bytes_tx_per_second: f64,
608    pub node_json_bytes_rx_per_second: f64,
609    pub node_p2p_bytes_tx_per_second: f64,
610    pub node_p2p_bytes_rx_per_second: f64,
611    pub node_grpc_user_bytes_tx_per_second: f64,
612    pub node_grpc_user_bytes_rx_per_second: f64,
613    pub node_total_bytes_tx_per_second: f64,
614    pub node_total_bytes_rx_per_second: f64,
615
616    // ---
617    pub node_blocks_submitted_count: f64,
618    pub node_headers_processed_count: f64,
619    pub node_dependencies_processed_count: f64,
620    pub node_bodies_processed_count: f64,
621    pub node_transactions_processed_count: f64,
622    pub node_chain_blocks_processed_count: f64,
623    pub node_mass_processed_count: f64,
624    // ---
625    pub network_mempool_size: f64,
626    pub network_transactions_per_second: f64,
627    pub node_database_blocks_count: f64,
628    pub node_database_headers_count: f64,
629    pub network_tip_hashes_count: f64,
630    pub network_difficulty: f64,
631    pub network_past_median_time: f64,
632    pub network_virtual_parent_hashes_count: f64,
633    pub network_virtual_daa_score: f64,
634    // ---
635    pub node_storage_size_bytes: f64,
636}
637
638impl MetricsSnapshot {
639    pub fn get(&self, metric: &Metric) -> f64 {
640        match metric {
641            // CpuCores
642            Metric::NodeCpuUsage => self.node_cpu_usage, // / self.cpu_cores,
643            Metric::NodeResidentSetSizeBytes => self.node_resident_set_size_bytes,
644            Metric::NodeVirtualMemorySizeBytes => self.node_virtual_memory_size_bytes,
645            Metric::NodeFileHandlesCount => self.node_file_handles,
646            Metric::NodeDiskIoReadBytes => self.node_disk_io_read_bytes,
647            Metric::NodeDiskIoWriteBytes => self.node_disk_io_write_bytes,
648            Metric::NodeDiskIoReadPerSec => self.node_disk_io_read_per_sec,
649            Metric::NodeDiskIoWritePerSec => self.node_disk_io_write_per_sec,
650            Metric::NodeStorageSizeBytes => self.node_storage_size_bytes,
651            // ---
652            Metric::NodeActivePeers => self.node_active_peers,
653            Metric::NodeBorshLiveConnections => self.node_borsh_active_connections,
654            Metric::NodeBorshConnectionAttempts => self.node_borsh_connection_attempts,
655            Metric::NodeBorshHandshakeFailures => self.node_borsh_handshake_failures,
656            Metric::NodeJsonLiveConnections => self.node_json_active_connections,
657            Metric::NodeJsonConnectionAttempts => self.node_json_connection_attempts,
658            Metric::NodeJsonHandshakeFailures => self.node_json_handshake_failures,
659            // ---
660            Metric::NodeBorshBytesTx => self.node_borsh_bytes_tx,
661            Metric::NodeBorshBytesRx => self.node_borsh_bytes_rx,
662            Metric::NodeJsonBytesTx => self.node_json_bytes_tx,
663            Metric::NodeJsonBytesRx => self.node_json_bytes_rx,
664            Metric::NodeP2pBytesTx => self.node_p2p_bytes_tx,
665            Metric::NodeP2pBytesRx => self.node_p2p_bytes_rx,
666            Metric::NodeGrpcUserBytesTx => self.node_grpc_user_bytes_tx,
667            Metric::NodeGrpcUserBytesRx => self.node_grpc_user_bytes_rx,
668            Metric::NodeTotalBytesTx => self.node_total_bytes_tx,
669            Metric::NodeTotalBytesRx => self.node_total_bytes_rx,
670
671            Metric::NodeBorshBytesTxPerSecond => self.node_borsh_bytes_tx_per_second,
672            Metric::NodeBorshBytesRxPerSecond => self.node_borsh_bytes_rx_per_second,
673            Metric::NodeJsonBytesTxPerSecond => self.node_json_bytes_tx_per_second,
674            Metric::NodeJsonBytesRxPerSecond => self.node_json_bytes_rx_per_second,
675            Metric::NodeP2pBytesTxPerSecond => self.node_p2p_bytes_tx_per_second,
676            Metric::NodeP2pBytesRxPerSecond => self.node_p2p_bytes_rx_per_second,
677            Metric::NodeGrpcUserBytesTxPerSecond => self.node_grpc_user_bytes_tx_per_second,
678            Metric::NodeGrpcUserBytesRxPerSecond => self.node_grpc_user_bytes_rx_per_second,
679            Metric::NodeTotalBytesTxPerSecond => self.node_total_bytes_tx_per_second,
680            Metric::NodeTotalBytesRxPerSecond => self.node_total_bytes_rx_per_second,
681            // ---
682            Metric::NodeBlocksSubmittedCount => self.node_blocks_submitted_count,
683            Metric::NodeHeadersProcessedCount => self.node_headers_processed_count,
684            Metric::NodeDependenciesProcessedCount => self.node_dependencies_processed_count,
685            Metric::NodeBodiesProcessedCount => self.node_bodies_processed_count,
686            Metric::NodeTransactionsProcessedCount => self.node_transactions_processed_count,
687            Metric::NodeChainBlocksProcessedCount => self.node_chain_blocks_processed_count,
688            Metric::NodeMassProcessedCount => self.node_mass_processed_count,
689            // --
690            Metric::NodeDatabaseBlocksCount => self.node_database_blocks_count,
691            Metric::NodeDatabaseHeadersCount => self.node_database_headers_count,
692            // --
693            Metric::NetworkMempoolSize => self.network_mempool_size,
694            Metric::NetworkTransactionsPerSecond => self.network_transactions_per_second,
695            Metric::NetworkTipHashesCount => self.network_tip_hashes_count,
696            Metric::NetworkDifficulty => self.network_difficulty,
697            Metric::NetworkPastMedianTime => self.network_past_median_time,
698            Metric::NetworkVirtualParentHashesCount => self.network_virtual_parent_hashes_count,
699            Metric::NetworkVirtualDaaScore => self.network_virtual_daa_score,
700        }
701    }
702
703    pub fn format(&self, metric: &Metric, si: bool, short: bool) -> String {
704        if short {
705            format!("{}: {}", metric.title().1, metric.format(self.get(metric), si, short))
706        } else {
707            format!("{}: {}", metric.title().0, metric.format(self.get(metric), si, short))
708        }
709    }
710}
711
712#[inline(always)]
713fn per_sec(a: u64, b: u64, duration_millis: f64) -> f64 {
714    b.checked_sub(a).unwrap_or_default() as f64 * 1000. / duration_millis
715}
716
717impl From<(&MetricsData, &MetricsData)> for MetricsSnapshot {
718    fn from((a, b): (&MetricsData, &MetricsData)) -> Self {
719        let duration_millis = b.unixtime_millis - a.unixtime_millis;
720
721        let network_transactions_per_second =
722            per_sec(a.node_transactions_processed_count, b.node_transactions_processed_count, duration_millis);
723        let node_borsh_bytes_tx_per_second = per_sec(a.node_borsh_bytes_tx, b.node_borsh_bytes_tx, duration_millis);
724        let node_borsh_bytes_rx_per_second = per_sec(a.node_borsh_bytes_rx, b.node_borsh_bytes_rx, duration_millis);
725        let node_json_bytes_tx_per_second = per_sec(a.node_json_bytes_tx, b.node_json_bytes_tx, duration_millis);
726        let node_json_bytes_rx_per_second = per_sec(a.node_json_bytes_rx, b.node_json_bytes_rx, duration_millis);
727        let node_p2p_bytes_tx_per_second = per_sec(a.node_p2p_bytes_tx, b.node_p2p_bytes_tx, duration_millis);
728        let node_p2p_bytes_rx_per_second = per_sec(a.node_p2p_bytes_rx, b.node_p2p_bytes_rx, duration_millis);
729        let node_grpc_user_bytes_tx_per_second = per_sec(a.node_grpc_user_bytes_tx, b.node_grpc_user_bytes_tx, duration_millis);
730        let node_grpc_user_bytes_rx_per_second = per_sec(a.node_grpc_user_bytes_rx, b.node_grpc_user_bytes_rx, duration_millis);
731        let node_total_bytes_tx_per_second = per_sec(a.node_total_bytes_tx, b.node_total_bytes_tx, duration_millis);
732        let node_total_bytes_rx_per_second = per_sec(a.node_total_bytes_rx, b.node_total_bytes_rx, duration_millis);
733
734        Self {
735            unixtime_millis: b.unixtime_millis,
736            duration_millis,
737            // ---
738            node_cpu_usage: b.node_cpu_usage as f64 / b.node_cpu_cores as f64 * 100.0,
739            node_cpu_cores: b.node_cpu_cores as f64,
740            node_resident_set_size_bytes: b.node_resident_set_size_bytes as f64,
741            node_virtual_memory_size_bytes: b.node_virtual_memory_size_bytes as f64,
742            node_file_handles: b.node_file_handles as f64,
743            node_disk_io_read_bytes: b.node_disk_io_read_bytes as f64,
744            node_disk_io_write_bytes: b.node_disk_io_write_bytes as f64,
745            node_disk_io_read_per_sec: b.node_disk_io_read_per_sec as f64,
746            node_disk_io_write_per_sec: b.node_disk_io_write_per_sec as f64,
747            node_storage_size_bytes: b.node_storage_size_bytes as f64,
748            // ---
749            node_borsh_active_connections: b.node_borsh_live_connections as f64,
750            node_borsh_connection_attempts: b.node_borsh_connection_attempts as f64,
751            node_borsh_handshake_failures: b.node_borsh_handshake_failures as f64,
752            node_json_active_connections: b.node_json_live_connections as f64,
753            node_json_connection_attempts: b.node_json_connection_attempts as f64,
754            node_json_handshake_failures: b.node_json_handshake_failures as f64,
755            node_active_peers: b.node_active_peers as f64,
756            // ---
757            node_borsh_bytes_tx: b.node_borsh_bytes_tx as f64,
758            node_borsh_bytes_rx: b.node_borsh_bytes_rx as f64,
759            node_json_bytes_tx: b.node_json_bytes_tx as f64,
760            node_json_bytes_rx: b.node_json_bytes_rx as f64,
761            node_p2p_bytes_tx: b.node_p2p_bytes_tx as f64,
762            node_p2p_bytes_rx: b.node_p2p_bytes_rx as f64,
763            node_grpc_user_bytes_tx: b.node_grpc_user_bytes_tx as f64,
764            node_grpc_user_bytes_rx: b.node_grpc_user_bytes_rx as f64,
765            node_total_bytes_tx: b.node_total_bytes_tx as f64,
766            node_total_bytes_rx: b.node_total_bytes_rx as f64,
767
768            node_borsh_bytes_tx_per_second,
769            node_borsh_bytes_rx_per_second,
770            node_json_bytes_tx_per_second,
771            node_json_bytes_rx_per_second,
772            node_p2p_bytes_tx_per_second,
773            node_p2p_bytes_rx_per_second,
774            node_grpc_user_bytes_tx_per_second,
775            node_grpc_user_bytes_rx_per_second,
776            node_total_bytes_tx_per_second,
777            node_total_bytes_rx_per_second,
778            // ---
779            node_blocks_submitted_count: b.node_blocks_submitted_count as f64,
780            node_headers_processed_count: b.node_headers_processed_count as f64,
781            node_dependencies_processed_count: b.node_dependencies_processed_count as f64,
782            node_bodies_processed_count: b.node_bodies_processed_count as f64,
783            node_transactions_processed_count: b.node_transactions_processed_count as f64,
784            node_chain_blocks_processed_count: b.node_chain_blocks_processed_count as f64,
785            node_mass_processed_count: b.node_mass_processed_count as f64,
786            // ---
787            node_database_blocks_count: b.node_database_blocks_count as f64,
788            node_database_headers_count: b.node_database_headers_count as f64,
789            // --
790            network_mempool_size: b.network_mempool_size as f64,
791            network_transactions_per_second,
792            network_tip_hashes_count: b.network_tip_hashes_count as f64,
793            network_difficulty: b.network_difficulty,
794            network_past_median_time: b.network_past_median_time as f64,
795            network_virtual_parent_hashes_count: b.network_virtual_parent_hashes_count as f64,
796            network_virtual_daa_score: b.network_virtual_daa_score as f64,
797
798            data: b.clone(),
799        }
800    }
801}
802
803/// Display KB or KiB if `short` is false, otherwise if `short` is true
804/// and the value is greater than 1MB or 1MiB, display units using [`as_data_size()`].
805pub fn as_kb(bytes: f64, si: bool, short: bool) -> String {
806    let unit = if si { 1000_f64 } else { 1024_f64 };
807    if short && bytes > unit.powi(2) {
808        as_data_size(bytes, si)
809    } else {
810        let suffix = if si { " KB" } else { " KiB" };
811        let kb = bytes / unit; //(( * 100.) as u64) as f64 / 100.;
812        format_with_precision(kb) + suffix
813    }
814}
815
816/// Display MB or MiB if `short` is false, otherwise if `short` is true
817/// and the value is greater than 1GB or 1GiB, display units using [`as_data_size()`].
818pub fn as_mb(bytes: f64, si: bool, short: bool) -> String {
819    let unit = if si { 1000_f64 } else { 1024_f64 };
820    if short && bytes > unit.powi(3) {
821        as_data_size(bytes, si)
822    } else {
823        let suffix = if si { " MB" } else { " MiB" };
824        let mb = bytes / unit.powi(2); //(( * 100.) as u64) as f64 / 100.;
825        format_with_precision(mb) + suffix
826    }
827}
828
829/// Display GB or GiB if `short` is false, otherwise if `short` is true
830/// and the value is greater than 1TB or 1TiB, display units using [`as_data_size()`].
831pub fn as_gb(bytes: f64, si: bool, short: bool) -> String {
832    let unit = if si { 1000_f64 } else { 1024_f64 };
833    if short && bytes > unit.powi(4) {
834        as_data_size(bytes, si)
835    } else {
836        let suffix = if si { " GB" } else { " GiB" };
837        let gb = bytes / unit.powi(3); //(( * 100.) as u64) as f64 / 100.;
838        format_with_precision(gb) + suffix
839    }
840}
841
842/// Display units dynamically formatted based on the size of the value.
843pub fn as_data_size(bytes: f64, si: bool) -> String {
844    let unit = if si { 1000_f64 } else { 1024_f64 };
845    let mut size = bytes;
846    let mut unit_str = " B";
847
848    if size >= unit.powi(4) {
849        size /= unit.powi(4);
850        unit_str = " TB";
851    } else if size >= unit.powi(3) {
852        size /= unit.powi(3);
853        unit_str = " GB";
854    } else if size >= unit.powi(2) {
855        size /= unit.powi(2);
856        unit_str = " MB";
857    } else if size >= unit {
858        size /= unit;
859        unit_str = " KB";
860    }
861
862    format_with_precision(size) + unit_str
863}
864
865/// Format supplied value as a float with 2 decimal places.
866pub fn format_as_float(f: f64, short: bool) -> String {
867    if short {
868        if f < 1000.0 {
869            format_with_precision(f)
870        } else if f < 1000000.0 {
871            format_with_precision(f / 1000.0) + " K"
872        } else if f < 1000000000.0 {
873            format_with_precision(f / 1000000.0) + " M"
874        } else if f < 1000000000000.0 {
875            format_with_precision(f / 1000000000.0) + " G"
876        } else if f < 1000000000000000.0 {
877            format_with_precision(f / 1000000000000.0) + " T"
878        } else if f < 1000000000000000000.0 {
879            format_with_precision(f / 1000000000000000.0) + " P"
880        } else {
881            format_with_precision(f / 1000000000000000000.0) + " E"
882        }
883    } else {
884        f.separated_string()
885    }
886}
887
888/// Format supplied value as a float with 2 decimal places.
889fn format_with_precision(f: f64) -> String {
890    if f.fract() < 0.01 {
891        separated_float!(format!("{}", f.trunc()))
892    } else {
893        separated_float!(format!("{:.2}", f))
894    }
895}