1use console::style;
20use log::info;
21use sc_client_api::ClientInfo;
22use sc_network::NetworkStatus;
23use sc_network_sync::{SyncState, SyncStatus, WarpSyncPhase, WarpSyncProgress};
24use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Saturating, Zero};
25use std::{fmt, time::Instant};
26
27use crate::PrintFullHashOnDebugLogging;
28
29pub struct InformantDisplay<B: BlockT> {
42 last_number: Option<NumberFor<B>>,
45 last_update: Instant,
47 last_total_bytes_inbound: u64,
49 last_total_bytes_outbound: u64,
51}
52
53impl<B: BlockT> InformantDisplay<B> {
54 pub fn new() -> InformantDisplay<B> {
56 InformantDisplay {
57 last_number: None,
58 last_update: Instant::now(),
59 last_total_bytes_inbound: 0,
60 last_total_bytes_outbound: 0,
61 }
62 }
63
64 pub fn display(
66 &mut self,
67 info: &ClientInfo<B>,
68 net_status: NetworkStatus,
69 sync_status: SyncStatus<B>,
70 num_connected_peers: usize,
71 ) {
72 let best_number = info.chain.best_number;
73 let best_hash = info.chain.best_hash;
74 let finalized_number = info.chain.finalized_number;
75 let speed = speed::<B>(best_number, self.last_number, self.last_update);
76 let total_bytes_inbound = net_status.total_bytes_inbound;
77 let total_bytes_outbound = net_status.total_bytes_outbound;
78
79 let now = Instant::now();
80 let elapsed = (now - self.last_update).as_secs();
81 self.last_update = now;
82 self.last_number = Some(best_number);
83
84 let diff_bytes_inbound = total_bytes_inbound - self.last_total_bytes_inbound;
85 let diff_bytes_outbound = total_bytes_outbound - self.last_total_bytes_outbound;
86 let (avg_bytes_per_sec_inbound, avg_bytes_per_sec_outbound) = if elapsed > 0 {
87 self.last_total_bytes_inbound = total_bytes_inbound;
88 self.last_total_bytes_outbound = total_bytes_outbound;
89 (diff_bytes_inbound / elapsed, diff_bytes_outbound / elapsed)
90 } else {
91 (diff_bytes_inbound, diff_bytes_outbound)
92 };
93
94 let (level, status, target) =
95 match (sync_status.state, sync_status.state_sync, sync_status.warp_sync) {
96 (
102 sync_status,
103 _,
104 Some(WarpSyncProgress { phase: WarpSyncPhase::DownloadingBlocks(n), .. }),
105 ) if !sync_status.is_major_syncing() => ("⏩", "Block history".into(), format!(", #{}", n)),
106 (_, _, Some(warp))
108 if !matches!(warp.phase, WarpSyncPhase::DownloadingBlocks(_)) =>
109 (
110 "⏩",
111 "Warping".into(),
112 format!(
113 ", {}, {:.2} Mib",
114 warp.phase,
115 (warp.total_bytes as f32) / (1024f32 * 1024f32)
116 ),
117 ),
118 (_, Some(state), _) => (
119 "⚙️ ",
120 "State sync".into(),
121 format!(
122 ", {}, {}%, {:.2} Mib",
123 state.phase,
124 state.percentage,
125 (state.size as f32) / (1024f32 * 1024f32)
126 ),
127 ),
128 (SyncState::Idle, _, _) => ("💤", "Idle".into(), "".into()),
129 (SyncState::Downloading { target }, _, _) =>
130 ("⚙️ ", format!("Syncing{}", speed), format!(", target=#{target}")),
131 (SyncState::Importing { target }, _, _) =>
132 ("⚙️ ", format!("Preparing{}", speed), format!(", target=#{target}")),
133 };
134
135 info!(
136 target: "substrate",
137 "{} {}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}",
138 level,
139 style(&status).white().bold(),
140 target,
141 style(num_connected_peers).white().bold(),
142 style(best_number).white().bold(),
143 PrintFullHashOnDebugLogging(&best_hash),
144 style(finalized_number).white().bold(),
145 PrintFullHashOnDebugLogging(&info.chain.finalized_hash),
146 style(TransferRateFormat(avg_bytes_per_sec_inbound)).green(),
147 style(TransferRateFormat(avg_bytes_per_sec_outbound)).red(),
148 )
149 }
150}
151
152fn speed<B: BlockT>(
155 best_number: NumberFor<B>,
156 last_number: Option<NumberFor<B>>,
157 last_update: Instant,
158) -> String {
159 let elapsed_ms = {
161 let elapsed = last_update.elapsed();
162 let since_last_millis = elapsed.as_secs() * 1000;
163 let since_last_subsec_millis = elapsed.subsec_millis() as u64;
164 since_last_millis + since_last_subsec_millis
165 };
166
167 let diff = match last_number {
169 None => return String::new(),
170 Some(n) => best_number.saturating_sub(n),
171 };
172
173 if let Ok(diff) = TryInto::<u128>::try_into(diff) {
174 let speed = diff
177 .saturating_mul(10_000)
178 .checked_div(u128::from(elapsed_ms))
179 .map_or(0.0, |s| s as f64) /
180 10.0;
181 format!(" {:4.1} bps", speed)
182 } else {
183 let one_thousand = NumberFor::<B>::from(1_000u32);
186 let elapsed =
187 NumberFor::<B>::from(<u32 as TryFrom<_>>::try_from(elapsed_ms).unwrap_or(u32::MAX));
188
189 let speed = diff
190 .saturating_mul(one_thousand)
191 .checked_div(&elapsed)
192 .unwrap_or_else(Zero::zero);
193 format!(" {} bps", speed)
194 }
195}
196
197struct TransferRateFormat(u64);
200impl fmt::Display for TransferRateFormat {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 if self.0 == 0 {
204 return write!(f, "0")
205 }
206
207 if self.0 < 100 {
209 return write!(f, "{} B/s", self.0)
210 }
211
212 if self.0 < 1024 * 1024 {
214 return write!(f, "{:.1}kiB/s", self.0 as f64 / 1024.0)
215 }
216
217 write!(f, "{:.1}MiB/s", self.0 as f64 / (1024.0 * 1024.0))
218 }
219}