nautilus_blockchain/
reporting.rs1use std::{fmt::Display, time::Instant};
19
20fn format_number<T>(n: T) -> String
24where
25 T: Into<f64>,
26{
27 let num = n.into().round() as u64;
28 let mut result = String::new();
29 let s = num.to_string();
30 let chars: Vec<char> = s.chars().collect();
31
32 for (i, ch) in chars.iter().enumerate() {
33 if i > 0 && (chars.len() - i) % 3 == 0 {
34 result.push(',');
35 }
36 result.push(*ch);
37 }
38
39 result
40}
41
42#[derive(Debug, Clone)]
43pub enum BlockchainSyncReportItems {
44 Blocks,
45 PoolCreatedEvents,
46 PoolEvents,
47}
48
49impl Display for BlockchainSyncReportItems {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "{self:?}")
52 }
53}
54
55#[derive(Debug)]
57pub struct BlockchainSyncReporter {
58 item: BlockchainSyncReportItems,
59 start_time: Instant,
60 last_progress_time: Instant,
61 blocks_processed: u64,
62 blocks_since_last_report: u64,
63 total_blocks: u64,
64 progress_update_interval: u64,
65 next_progress_threshold: u64,
66}
67
68impl BlockchainSyncReporter {
69 #[must_use]
71 pub fn new(
72 item: BlockchainSyncReportItems,
73 from_block: u64,
74 total_blocks: u64,
75 update_interval: u64,
76 ) -> Self {
77 let now = Instant::now();
78 Self {
79 item,
80 start_time: now,
81 last_progress_time: now,
82 blocks_processed: 0,
83 blocks_since_last_report: 0,
84 total_blocks,
85 progress_update_interval: update_interval,
86 next_progress_threshold: from_block + update_interval,
87 }
88 }
89
90 pub fn update(&mut self, batch_size: usize) {
92 self.blocks_processed += batch_size as u64;
93 self.blocks_since_last_report += batch_size as u64;
94 }
95
96 #[must_use]
98 pub fn should_log_progress(&self, block_number: u64, current_block: u64) -> bool {
99 let block_threshold_reached =
100 block_number >= self.next_progress_threshold || block_number >= current_block;
101 let time_threshold_reached = self.last_progress_time.elapsed().as_secs_f64() >= 1.0;
103
104 block_threshold_reached && time_threshold_reached
105 }
106
107 pub fn log_progress(&mut self, block_number: u64) {
109 let elapsed = self.start_time.elapsed();
110 let interval_elapsed = self.last_progress_time.elapsed();
111
112 let avg_rate = if elapsed.as_secs_f64() > 0.0 {
114 self.blocks_processed as f64 / elapsed.as_secs_f64()
115 } else {
116 0.0
117 };
118
119 let current_rate = if interval_elapsed.as_secs_f64() > 0.001 {
120 self.blocks_since_last_report as f64 / interval_elapsed.as_secs_f64()
122 } else {
123 0.0
124 };
125
126 let progress_pct =
127 (self.blocks_processed as f64 / self.total_blocks as f64 * 100.0).min(100.0);
128
129 tracing::info!(
130 "Syncing {} progress: {:.1}% | Block: {} | Rate: {} blocks/s | Avg: {} blocks/s",
131 self.item,
132 progress_pct,
133 format_number(block_number as f64),
134 format_number(current_rate),
135 format_number(avg_rate),
136 );
137
138 self.next_progress_threshold = block_number + self.progress_update_interval;
139 self.last_progress_time = Instant::now();
140 self.blocks_since_last_report = 0;
141 }
142
143 pub fn log_final_stats(&self) {
145 let total_elapsed = self.start_time.elapsed();
146 let avg_rate = self.blocks_processed as f64 / total_elapsed.as_secs_f64();
147 tracing::info!(
148 "Finished syncing {} | Total: {} blocks in {:.1}s | Avg rate: {} blocks/s",
149 self.item,
150 format_number(self.blocks_processed as f64),
151 total_elapsed.as_secs_f64(),
152 format_number(avg_rate),
153 );
154 }
155}