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).is_multiple_of(3) {
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 PoolProfilerBootstrap,
48}
49
50impl Display for BlockchainSyncReportItems {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 write!(f, "{self:?}")
53 }
54}
55
56#[derive(Debug)]
58pub struct BlockchainSyncReporter {
59 item: BlockchainSyncReportItems,
60 start_time: Instant,
61 last_progress_time: Instant,
62 from_block: u64,
63 blocks_processed: u64,
64 blocks_since_last_report: u64,
65 total_blocks: u64,
66 progress_update_interval: u64,
67 next_progress_threshold: u64,
68}
69
70impl BlockchainSyncReporter {
71 #[must_use]
73 pub fn new(
74 item: BlockchainSyncReportItems,
75 from_block: u64,
76 total_blocks: u64,
77 update_interval: u64,
78 ) -> Self {
79 let now = Instant::now();
80 Self {
81 item,
82 start_time: now,
83 last_progress_time: now,
84 from_block,
85 blocks_processed: 0,
86 blocks_since_last_report: 0,
87 total_blocks,
88 progress_update_interval: update_interval,
89 next_progress_threshold: from_block + update_interval,
90 }
91 }
92
93 pub fn update(&mut self, batch_size: usize) {
95 self.blocks_processed += batch_size as u64;
96 self.blocks_since_last_report += batch_size as u64;
97 }
98
99 #[must_use]
101 pub fn should_log_progress(&self, block_number: u64, current_block: u64) -> bool {
102 let block_threshold_reached =
103 block_number >= self.next_progress_threshold || block_number >= current_block;
104 let time_threshold_reached = self.last_progress_time.elapsed().as_secs_f64() >= 1.0;
106
107 block_threshold_reached && time_threshold_reached
108 }
109
110 pub fn log_progress(&mut self, block_number: u64) {
112 let elapsed = self.start_time.elapsed();
113 let interval_elapsed = self.last_progress_time.elapsed();
114
115 let avg_rate = if elapsed.as_secs_f64() > 0.0 {
117 self.blocks_processed as f64 / elapsed.as_secs_f64()
118 } else {
119 0.0
120 };
121
122 let current_rate = if interval_elapsed.as_secs_f64() > 0.001 {
123 self.blocks_since_last_report as f64 / interval_elapsed.as_secs_f64()
125 } else {
126 0.0
127 };
128
129 let blocks_completed = block_number.saturating_sub(self.from_block);
131 let progress_pct = (blocks_completed as f64 / self.total_blocks as f64 * 100.0).min(100.0);
132
133 log::info!(
134 "Syncing {} progress: {:.1}% | Block: {} | Rate: {} blocks/s | Avg: {} blocks/s",
135 self.item,
136 progress_pct,
137 format_number(block_number as f64),
138 format_number(current_rate),
139 format_number(avg_rate),
140 );
141
142 self.next_progress_threshold = block_number + self.progress_update_interval;
143 self.last_progress_time = Instant::now();
144 self.blocks_since_last_report = 0;
145 }
146
147 pub fn log_final_stats(&self) {
149 let total_elapsed = self.start_time.elapsed();
150 let avg_rate = self.blocks_processed as f64 / total_elapsed.as_secs_f64();
151 log::info!(
152 "Finished syncing {} | Total: {} blocks in {:.1}s | Avg rate: {} blocks/s",
153 self.item,
154 format_number(self.blocks_processed as f64),
155 total_elapsed.as_secs_f64(),
156 format_number(avg_rate),
157 );
158 }
159}