diff --git a/Cargo.lock b/Cargo.lock index befd74c702..df63b8c02f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1241,6 +1241,7 @@ dependencies = [ "hyper-rustls", "hyper-util", "log", + "millisecond", "rand 0.6.5", "serde", "serde_derive", @@ -1904,6 +1905,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "millisecond" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63befb9f9db85caa92fe71eb3e7a42d76b90b38914f0356983d726442825770" + [[package]] name = "miniz_oxide" version = "0.8.9" diff --git a/servers/Cargo.toml b/servers/Cargo.toml index f2cd6fb3b5..69f792aca2 100644 --- a/servers/Cargo.toml +++ b/servers/Cargo.toml @@ -25,6 +25,7 @@ async-stream = "0.3" walkdir = "2.3.1" hyper-util = { version = "0.1.20", features = ["client-legacy"] } http-body-util = "0.1.3" +millisecond = "0.15" grin_api = { path = "../api", version = "5.5.1-alpha.0" } grin_chain = { path = "../chain", version = "5.5.1-alpha.0" } diff --git a/servers/src/common/stats.rs b/servers/src/common/stats.rs index 308a619819..2a537f4589 100644 --- a/servers/src/common/stats.rs +++ b/servers/src/common/stats.rs @@ -15,19 +15,18 @@ //! Server stat collection types, to be used by tests, logging or GUI/TUI //! to collect information about server status -use crate::util::RwLock; +use chrono::prelude::*; +use grin_core::pow::Difficulty; +use millisecond::MillisecondFormatter; use std::sync::Arc; use std::time::SystemTime; +use crate::chain::SyncStatus; use crate::core::core::hash::Hash; use crate::core::ser::ProtocolVersion; - -use chrono::prelude::*; - -use crate::chain::SyncStatus; use crate::p2p; use crate::p2p::Capabilities; -use grin_core::pow::Difficulty; +use crate::util::RwLock; /// Server state info collection struct, to be passed around into internals /// and populated when required @@ -48,6 +47,8 @@ impl Default for ServerStateInfo { /// consumers might be interested in, such as test results or UI #[derive(Debug, Clone)] pub struct ServerStats { + /// Server uptime in seconds + pub uptime_seconds: u64, /// Number of peers pub peer_count: u32, /// Chain head @@ -68,6 +69,14 @@ pub struct ServerStats { pub disk_usage_gb: String, } +impl ServerStats { + /// Formatted uptime (i.e.: 1y 17d 5h 10m 48s). + pub fn uptime_format(&self) -> String { + let time = millisecond::Millisecond::from_secs(self.uptime_seconds); + time.pretty() + } +} + /// Chain Statistics #[derive(Clone, Serialize, Debug)] pub struct ChainStats { diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index 6996f4d18f..f486350345 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -72,6 +72,7 @@ pub struct Server { pub stop_state: Arc, /// Maintain a lock_file so we do not run multiple Grin nodes from same dir. lock_file: Arc, + start_time: time::Instant, connect_thread: Option>, sync_thread: JoinHandle<()>, dandelion_thread: JoinHandle<()>, @@ -153,6 +154,7 @@ impl Server { ) -> Result { // Obtain our lock_file or fail immediately with an error. let lock_file = Server::one_grin_at_a_time(&config)?; + let start_time = time::Instant::now(); // Defaults to None (optional) in config file. // This translates to false here. @@ -339,6 +341,7 @@ impl Server { }, stop_state, lock_file, + start_time, connect_thread, sync_thread, dandelion_thread, @@ -542,6 +545,7 @@ impl Server { let disk_usage_gb = format!("{:.*}", 3, (disk_usage_bytes as f64 / 1_000_000_000_f64)); Ok(ServerStats { + uptime_seconds: self.start_time.elapsed().as_secs(), peer_count: self.peer_count(), chain_stats: head_stats, header_stats, diff --git a/src/bin/tui/status.rs b/src/bin/tui/status.rs index 80ca87df7e..69b43d3b8e 100644 --- a/src/bin/tui/status.rs +++ b/src/bin/tui/status.rs @@ -180,6 +180,11 @@ impl TUIStatusView { pub fn create() -> impl View { let basic_status_view = ResizedView::with_full_screen( LinearLayout::new(Orientation::Vertical) + .child( + LinearLayout::new(Orientation::Horizontal) + .child(TextView::new("Uptime: ")) + .child(TextView::new("0").with_name("basic_uptime")), + ) .child( LinearLayout::new(Orientation::Horizontal) .child(TextView::new("Current Status: ")) @@ -292,6 +297,9 @@ impl TUIStatusListener for TUIStatusView { fn update(c: &mut Cursive, stats: &ServerStats) { let basic_status = TUIStatusView::update_sync_status(stats.sync_status); + c.call_on_name("basic_uptime", |t: &mut TextView| { + t.set_content(stats.uptime_format()); + }); c.call_on_name("basic_current_status", |t: &mut TextView| { t.set_content(basic_status); });