// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
extern crate ansi_term;
use self::ansi_term::Colour::{White, Yellow, Green, Cyan, Blue, Purple};
use self::ansi_term::Style;
use std::time::{Instant, Duration};
use std::sync::RwLock;
use std::ops::{Deref, DerefMut};
use ethsync::{EthSync, SyncProvider};
use util::{Uint, RwLockable, NetworkService};
use ethcore::client::*;
use number_prefix::{binary_prefix, Standalone, Prefixed};
pub struct Informant {
chain_info: RwLock>,
cache_info: RwLock >,
report: RwLock >,
last_tick: RwLock,
with_color: bool,
}
impl Default for Informant {
fn default() -> Self {
Informant {
chain_info: RwLock::new(None),
cache_info: RwLock::new(None),
report: RwLock::new(None),
last_tick: RwLock::new(Instant::now()),
with_color: true,
}
}
}
trait MillisecondDuration {
fn as_milliseconds(&self) -> u64;
}
impl MillisecondDuration for Duration {
fn as_milliseconds(&self) -> u64 {
self.as_secs() * 1000 + self.subsec_nanos() as u64 / 1000000
}
}
impl Informant {
/// Make a new instance potentially `with_color` output.
pub fn new(with_color: bool) -> Self {
Informant {
chain_info: RwLock::new(None),
cache_info: RwLock::new(None),
report: RwLock::new(None),
last_tick: RwLock::new(Instant::now()),
with_color: with_color,
}
}
fn format_bytes(b: usize) -> String {
match binary_prefix(b as f64) {
Standalone(bytes) => format!("{} bytes", bytes),
Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix),
}
}
pub fn tick(&self, client: &Client, maybe_sync: Option<(&EthSync, &NetworkService)>) where Message: Send + Sync + Clone + 'static {
let elapsed = self.last_tick.unwrapped_read().elapsed();
if elapsed < Duration::from_secs(5) {
return;
}
*self.last_tick.unwrapped_write() = Instant::now();
let chain_info = client.chain_info();
let queue_info = client.queue_info();
let cache_info = client.blockchain_cache_info();
let mut write_report = self.report.unwrapped_write();
let report = client.report();
let paint = |c: Style, t: String| match self.with_color {
true => format!("{}", c.paint(t)),
false => t,
};
if let (_, _, &Some(ref last_report)) = (
self.chain_info.unwrapped_read().deref(),
self.cache_info.unwrapped_read().deref(),
write_report.deref()
) {
println!("{} {} {} blk/s {} tx/s {} Mgas/s {}{}+{} Qed {} db {} chain {} queue{}",
paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))),
paint(White.bold(), format!("{}", chain_info.best_block_hash)),
paint(Yellow.bold(), format!("{:4}", ((report.blocks_imported - last_report.blocks_imported) * 1000) as u64 / elapsed.as_milliseconds())),
paint(Yellow.bold(), format!("{:4}", ((report.transactions_applied - last_report.transactions_applied) * 1000) as u64 / elapsed.as_milliseconds())),
paint(Yellow.bold(), format!("{:3}", ((report.gas_processed - last_report.gas_processed) / From::from(elapsed.as_milliseconds() * 1000)).low_u64())),
match maybe_sync {
Some((sync, net)) => {
let sync_info = sync.status();
let net_config = net.config();
format!("{}/{}/{} peers {} ",
paint(Green.bold(), format!("{:2}", sync_info.num_active_peers)),
paint(Green.bold(), format!("{:2}", sync_info.num_peers)),
paint(Green.bold(), format!("{:2}", net_config.ideal_peers)),
paint(Cyan.bold(), format!("{:>8}", format!("#{}", sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number)))),
)
}
None => String::new()
},
paint(Blue.bold(), format!("{:5}", queue_info.unverified_queue_size)),
paint(Blue.bold(), format!("{:5}", queue_info.verified_queue_size)),
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(report.state_db_mem))),
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(cache_info.total()))),
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(queue_info.mem_used))),
match maybe_sync {
Some((sync, _)) => {
let sync_info = sync.status();
format!(" {} sync", paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(sync_info.mem_used))))
}
None => String::new()
},
);
}
*self.chain_info.unwrapped_write().deref_mut() = Some(chain_info);
*self.cache_info.unwrapped_write().deref_mut() = Some(cache_info);
*write_report.deref_mut() = Some(report);
}
}