diff --git a/Cargo.lock b/Cargo.lock index 05f41c32..f97f6045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,12 +242,6 @@ dependencies = [ "password-hash", ] -[[package]] -name = "arraydeque" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ffd3d69bd89910509a5d31d1f1353f38ccffdd116dd0099bbd6627f7bd8ad8" - [[package]] name = "arrayref" version = "0.3.7" @@ -1300,20 +1294,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "cursive-flexi-logger-view" -version = "0.5.0" -source = "git+https://gitlab.com/veilid/cursive-flexi-logger-view.git#7c931536b8c57339011bbe2ee604e431c91c0aa8" -dependencies = [ - "arraydeque", - "cursive_core", - "flexi_logger", - "lazy_static", - "log", - "time", - "unicode-width", -] - [[package]] name = "cursive-macros" version = "0.1.0" @@ -2233,6 +2213,15 @@ dependencies = [ "ahash 0.7.7", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.6", +] + [[package]] name = "hashbrown" version = "0.14.2" @@ -2835,6 +2824,15 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lru" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" +dependencies = [ + "hashbrown 0.13.2", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -5491,7 +5489,6 @@ dependencies = [ "config", "crossbeam-channel", "cursive", - "cursive-flexi-logger-view", "cursive_buffered_backend", "cursive_table_view", "data-encoding", @@ -5503,6 +5500,8 @@ dependencies = [ "indent", "json", "log", + "lru", + "owning_ref", "parking_lot 0.12.1", "serde", "serde_derive", @@ -5511,6 +5510,7 @@ dependencies = [ "thiserror", "tokio", "tokio-util", + "unicode-width", "veilid-bugsalot", "veilid-tools", ] diff --git a/veilid-cli/Cargo.toml b/veilid-cli/Cargo.toml index 10f08a55..0c0a606a 100644 --- a/veilid-cli/Cargo.toml +++ b/veilid-cli/Cargo.toml @@ -34,7 +34,7 @@ cursive = { git = "https://gitlab.com/veilid/cursive.git", default-features = fa "toml", "ansi", ] } -cursive-flexi-logger-view = { git = "https://gitlab.com/veilid/cursive-flexi-logger-view.git" } + cursive_buffered_backend = { git = "https://gitlab.com/veilid/cursive-buffered-backend.git" } # cursive-multiplex = "0.6.0" # cursive_tree_view = "0.6.0" @@ -64,6 +64,9 @@ data-encoding = { version = "^2" } indent = { version = "0.1.1" } chrono = "0.4.26" +owning_ref = "0.4.1" +unicode-width = "0.1.10" +lru = "0.10.0" [dev-dependencies] serial_test = "^2" diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index 0fc1e2f8..7da88877 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -189,7 +189,7 @@ Server Debug Commands: ui.send_callback(callback); } Err(e) => { - ui.add_node_event(Level::Error, e.to_string()); + ui.add_node_event(Level::Error, format!("{}\n", e)); ui.send_callback(callback); } } @@ -424,7 +424,7 @@ Server Debug Commands: self.inner().ui_sender.add_node_event( log_level, format!( - "{}: {}{}", + "{}: {}{}\n", log["log_level"].as_str().unwrap_or("???"), log["message"].as_str().unwrap_or("???"), if let Some(bt) = log["backtrace"].as_str() { @@ -466,7 +466,7 @@ Server Debug Commands: self.inner().ui_sender.add_node_event( Level::Info, format!( - "AppMessage ({:?}): {}{}", + "AppMessage ({:?}): {}{}\n", msg["sender"], strmsg, if truncated { "..." } else { "" } @@ -506,7 +506,7 @@ Server Debug Commands: self.inner().ui_sender.add_node_event( Level::Info, format!( - "AppCall ({:?}) id = {:016x} : {}{}", + "AppCall ({:?}) id = {:016x} : {}{}\n", call["sender"], id, strmsg, diff --git a/veilid-cli/src/main.rs b/veilid-cli/src/main.rs index 4f46cfdd..35e00e88 100644 --- a/veilid-cli/src/main.rs +++ b/veilid-cli/src/main.rs @@ -9,6 +9,7 @@ use clap::{Parser, ValueEnum}; use flexi_logger::*; use std::{net::ToSocketAddrs, path::PathBuf}; +mod cached_text_view; mod client_api_connection; mod command_processor; mod peers_table_view; @@ -91,7 +92,6 @@ fn main() -> Result<(), String> { let logger = Logger::with(specbuilder.build()); if settings.logging.terminal.enabled { - let flv = sivui.cursive_flexi_logger(); if settings.logging.file.enabled { std::fs::create_dir_all(settings.logging.file.directory.clone()) .map_err(map_to_string)?; @@ -100,13 +100,13 @@ fn main() -> Result<(), String> { FileSpec::default() .directory(settings.logging.file.directory.clone()) .suppress_timestamp(), - flv, + Box::new(uisender.clone()), ) .start() .expect("failed to initialize logger!"); } else { logger - .log_to_writer(flv) + .log_to_writer(Box::new(uisender.clone())) .start() .expect("failed to initialize logger!"); } diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index d8f07351..95d1b60a 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -7,13 +7,16 @@ use cursive::align::*; use cursive::event::*; use cursive::theme::*; use cursive::traits::*; +use cursive::utils::markup::ansi; use cursive::utils::markup::StyledString; use cursive::view::SizeConstraint; use cursive::views::*; use cursive::Cursive; use cursive::CursiveRunnable; -use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView}; +use flexi_logger::writers::LogWriter; +use flexi_logger::DeferredNow; // use cursive_multiplex::*; +use crate::cached_text_view::*; use chrono::{Datelike, Timelike}; use std::collections::{HashMap, VecDeque}; use std::io::Write; @@ -50,6 +53,7 @@ impl Dirty { } pub type UICallback = Box; +pub type NodeEventsPanel = Panel>>>; static START_TIME: AtomicU64 = AtomicU64::new(0); @@ -219,12 +223,25 @@ impl UI { }); } fn clear_handler(siv: &mut Cursive) { - cursive_flexi_logger_view::clear_log(); + Self::node_events_view(siv).set_content(""); UI::update_cb(siv); } - fn node_events_panel(s: &mut Cursive) -> ViewRef>> { + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Selectors + + // fn main_layout(s: &mut Cursive) -> ViewRef { + // s.find_name("main-layout").unwrap() + // } + fn node_events_panel(s: &mut Cursive) -> ViewRef { s.find_name("node-events-panel").unwrap() } + fn node_events_view(s: &mut Cursive) -> ViewRef { + s.find_name("node-events-view").unwrap() + } + // fn node_events_scroll_view(s: &mut Cursive) -> ViewRef> { + // s.find_name("node-events-scroll-view").unwrap() + // } fn command_line(s: &mut Cursive) -> ViewRef { s.find_name("command-line").unwrap() } @@ -237,6 +254,27 @@ impl UI { fn peers(s: &mut Cursive) -> ViewRef { s.find_name("peers").unwrap() } + fn connection_address(s: &mut Cursive) -> ViewRef { + s.find_name("connection-address").unwrap() + } + fn connection_dialog(s: &mut Cursive) -> ViewRef { + s.find_name("connection-dialog").unwrap() + } + //////////////////////////////////////////////////////////////////////////////////////////////// + + pub fn push_styled(s: &mut Cursive, styled_string: StyledString) { + let mut ctv = UI::node_events_view(s); + ctv.append(styled_string) + } + + pub fn push_ansi_lines(s: &mut Cursive, starting_style: Style, lines: String) { + let mut ctv = UI::node_events_view(s); + let (spanned_string, _end_style) = ansi::parse_with_starting_style(starting_style, lines); + ctv.append(spanned_string); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + fn render_attachment_state(inner: &mut UIInner) -> String { let att = match inner.ui_state.attachment_state.get().as_str() { "Detached" => "[----]", @@ -351,16 +389,17 @@ impl UI { return; } // run command - - cursive_flexi_logger_view::parse_lines_to_log( + UI::push_ansi_lines( + s, ColorStyle::primary().into(), - format!("> {} {}", UI::cli_ts(Self::get_start_time()), text), + format!("{}> {}\n", UI::cli_ts(Self::get_start_time()), text), ); match Self::run_command(s, text) { Ok(_) => {} Err(e) => { let color = *Self::inner_mut(s).log_colors.get(&Level::Error).unwrap(); - cursive_flexi_logger_view::parse_lines_to_log( + UI::push_ansi_lines( + s, color.into(), format!(" {} Error: {}", UI::cli_ts(Self::get_start_time()), e), ); @@ -446,7 +485,7 @@ impl UI { } fn submit_connection_address(s: &mut Cursive) { - let edit = s.find_name::("connection-address").unwrap(); + let edit = Self::connection_address(s); let addr = (*edit.get_content()).clone(); let sa = match addr.parse::() { Ok(sa) => Some(sa), @@ -475,7 +514,8 @@ impl UI { && std::io::stdout().flush().is_ok() { let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap(); - cursive_flexi_logger_view::parse_lines_to_log( + UI::push_ansi_lines( + s, color.into(), format!( ">> {} Copied: {}", @@ -491,7 +531,8 @@ impl UI { // X11/Wayland/other system copy if clipboard.set_text(text.as_ref()).is_ok() { let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap(); - cursive_flexi_logger_view::parse_lines_to_log( + UI::push_ansi_lines( + s, color.into(), format!( ">> {} Copied: {}", @@ -501,7 +542,8 @@ impl UI { ); } else { let color = *Self::inner_mut(s).log_colors.get(&Level::Warn).unwrap(); - cursive_flexi_logger_view::parse_lines_to_log( + UI::push_ansi_lines( + s, color.into(), format!( ">> {} Could not copy to clipboard", @@ -622,7 +664,7 @@ impl UI { return true; } if reset { - let mut dlg = s.find_name::("connection-dialog").unwrap(); + let mut dlg = Self::connection_dialog(s); dlg.clear_buttons(); return true; } @@ -644,20 +686,20 @@ impl UI { Some(addr) => addr.to_string(), }; debug!("address is {}", addr); - let mut edit = s.find_name::("connection-address").unwrap(); + let mut edit = Self::connection_address(s); edit.set_content(addr); edit.set_enabled(true); - let mut dlg = s.find_name::("connection-dialog").unwrap(); + let mut dlg = Self::connection_dialog(s); dlg.add_button("Connect", Self::submit_connection_address); } ConnectionState::Connected(_, _) => {} ConnectionState::Retrying(addr, _) => { // - let mut edit = s.find_name::("connection-address").unwrap(); + let mut edit = Self::connection_address(s); debug!("address is {}", addr); edit.set_content(addr.to_string()); edit.set_enabled(false); - let mut dlg = s.find_name::("connection-dialog").unwrap(); + let mut dlg = Self::connection_dialog(s); dlg.add_button("Cancel", |s| { Self::command_processor(s).cancel_reconnect(); }); @@ -837,9 +879,23 @@ impl UI { START_TIME.load(Ordering::Relaxed) } - pub fn new(node_log_scrollback: usize, settings: &Settings) -> (Self, UISender) { - cursive_flexi_logger_view::resize(node_log_scrollback); + fn make_node_events_panel( + node_log_scrollback: usize, + ) -> ResizedView> { + Panel::new( + CachedTextView::new("", node_log_scrollback) + .with_name("node-events-view") + .scrollable() + .scroll_strategy(cursive::view::ScrollStrategy::StickToBottom) + .with_name("node-events-scroll-view"), + ) + .title_position(HAlign::Left) + .title("Node Events") + .with_name("node-events-panel") + .full_screen() + } + pub fn new(node_log_scrollback: usize, settings: &Settings) -> (Self, UISender) { UI::set_start_time(); // Instantiate the cursive runnable let runnable = CursiveRunnable::new( @@ -874,6 +930,7 @@ impl UI { let ui_sender = UISender { inner: this.inner.clone(), cb_sink, + colors: default_log_colors(), }; let mut inner = this.inner.lock(); @@ -882,13 +939,7 @@ impl UI { this.siv.set_user_data(this.inner.clone()); // Create layouts - - let node_events_view = Panel::new(FlexiLoggerView::new_scrollable()) - .title_position(HAlign::Left) - .title("Node Events") - .with_name("node-events-panel") - .full_screen(); - + let node_events_view = Self::make_node_events_panel(node_log_scrollback); let mut peers_table_view = PeersTableView::new() .column(PeerTableColumn::NodeId, "Node Id", |c| c.width(48)) .column(PeerTableColumn::Address, "Address", |c| c) @@ -967,7 +1018,8 @@ impl UI { .child(TextView::new(version)), ); - this.siv.add_fullscreen_layer(mainlayout); + this.siv + .add_fullscreen_layer(mainlayout.with_name("main-layout")); UI::setup_colors(&mut this.siv, &mut inner, settings); UI::setup_quit_handler(&mut this.siv); @@ -978,11 +1030,7 @@ impl UI { (this, ui_sender) } - pub fn cursive_flexi_logger(&self) -> Box { - let mut flv = cursive_flexi_logger_view::cursive_flexi_logger(self.siv.cb_sink().clone()); - flv.set_colors(self.inner.lock().log_colors.clone()); - flv - } + pub fn set_command_processor(&mut self, cmdproc: CommandProcessor) { let mut inner = self.inner.lock(); inner.cmdproc = Some(cmdproc); @@ -999,10 +1047,22 @@ impl UI { type CallbackSink = Box; +/// Default log colors +fn default_log_colors() -> HashMap { + let mut colors = HashMap::::new(); + colors.insert(Level::Trace, Color::Dark(BaseColor::Green)); + colors.insert(Level::Debug, Color::Dark(BaseColor::Cyan)); + colors.insert(Level::Info, Color::Dark(BaseColor::Blue)); + colors.insert(Level::Warn, Color::Dark(BaseColor::Yellow)); + colors.insert(Level::Error, Color::Dark(BaseColor::Red)); + colors +} + #[derive(Clone)] pub struct UISender { inner: Arc>, cb_sink: Sender, + colors: HashMap, } impl UISender { @@ -1094,14 +1154,71 @@ impl UISender { } pub fn add_node_event(&self, log_color: Level, event: String) { - { + let color = { let inner = self.inner.lock(); - let color = *inner.log_colors.get(&log_color).unwrap(); - cursive_flexi_logger_view::parse_lines_to_log( - color.into(), - format!("{}: {}", UI::cli_ts(UI::get_start_time()), event), - ); + *inner.log_colors.get(&log_color).unwrap() + }; + + let _ = self.push_styled_lines( + color.into(), + format!("{}: {}\n", UI::cli_ts(UI::get_start_time()), event), + ); + } + + pub fn push_styled(&self, styled_string: StyledString) -> std::io::Result<()> { + let res = self.cb_sink.send(Box::new(move |s| { + UI::push_styled(s, styled_string); + })); + if res.is_err() { + return Err(std::io::Error::from(std::io::ErrorKind::BrokenPipe)); } - let _ = self.cb_sink.send(Box::new(UI::update_cb)); + Ok(()) + } + + pub fn push_styled_lines(&self, starting_style: Style, lines: String) -> std::io::Result<()> { + let res = self.cb_sink.send(Box::new(move |s| { + UI::push_ansi_lines(s, starting_style, lines); + })); + if res.is_err() { + return Err(std::io::Error::from(std::io::ErrorKind::BrokenPipe)); + } + Ok(()) + } +} +impl LogWriter for UISender { + fn write(&self, _now: &mut DeferredNow, record: &Record) -> std::io::Result<()> { + let color = *self.colors.get(&record.level()).unwrap(); + + let args = format!("{}", &record.args()); + + let mut line = StyledString::new(); + let mut indent = 0; + let levstr = format!("{}: ", record.level()); + indent += levstr.len(); + line.append_styled(levstr, color); + let filestr = format!( + "{}:{} ", + record.file().unwrap_or("(unnamed)"), + record.line().unwrap_or(0), + ); + indent += filestr.len(); + line.append_plain(filestr); + + for argline in args.lines() { + line.append_styled(argline, color); + self.push_styled(line)?; + line = StyledString::new(); + line.append_plain(" ".repeat(indent)); + } + Ok(()) + } + + fn flush(&self) -> std::io::Result<()> { + // we are not buffering + Ok(()) + } + + fn max_log_level(&self) -> log::LevelFilter { + log::LevelFilter::max() } } diff --git a/veilid-core/src/network_manager/native/igd_manager.rs b/veilid-core/src/network_manager/native/igd_manager.rs index 302dba33..19c9f519 100644 --- a/veilid-core/src/network_manager/native/igd_manager.rs +++ b/veilid-core/src/network_manager/native/igd_manager.rs @@ -401,7 +401,7 @@ impl IGDManager { let desc = this.get_description(k.llpt, k.local_port); match gw.add_port(convert_llpt(k.llpt), v.mapped_port, SocketAddr::new(local_ip, k.local_port), (UPNP_MAPPING_LIFETIME_MS + 999) / 1000, &desc) { Ok(()) => { - log_net!(debug "renewed mapped port {:?} -> {:?}", v, k); + log_net!("renewed mapped port {:?} -> {:?}", v, k); inner.port_maps.insert(k, PortMapValue { ext_ip: v.ext_ip, diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 415d1ea0..2d0ece6e 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -1499,8 +1499,7 @@ impl VeilidAPI { let mut dc = DEBUG_CACHE.lock(); dc.opened_record_contexts.insert(*record.key(), rc); - debug!("DHT Record Opened:\n{:#?}", record); - Ok(format!("Opened: {:?}", record)) + Ok(format!("Opened: {} : {:?}", key, record)) } async fn debug_record_close(&self, args: Vec) -> VeilidAPIResult { @@ -1511,7 +1510,6 @@ impl VeilidAPI { return Ok(format!("Can't close DHT record: {}", e)); }; - debug!("DHT Record Closed:\n{:#?}", key); Ok(format!("Closed: {:?}", key)) } @@ -1648,7 +1646,7 @@ impl VeilidAPI { Ok(v) => v, }; - Ok(format!("Expiration at: {:?}", debug_ts(ts.as_u64()))) + Ok(format!("Success: expiration={:?}", debug_ts(ts.as_u64()))) } async fn debug_record_cancel(&self, args: Vec) -> VeilidAPIResult { @@ -1665,10 +1663,11 @@ impl VeilidAPI { Ok(v) => v, }; - Ok(format!( - "Still Active: {:?}", - if still_active { "true" } else { "false" } - )) + Ok(if still_active { + "Watch partially cancelled".to_owned() + } else { + "Watch cancelled".to_owned() + }) } async fn debug_record(&self, args: String) -> VeilidAPIResult { diff --git a/veilid-tools/src/network_interfaces/apple.rs b/veilid-tools/src/network_interfaces/apple.rs index 1a7d5a75..95f8e42b 100644 --- a/veilid-tools/src/network_interfaces/apple.rs +++ b/veilid-tools/src/network_interfaces/apple.rs @@ -485,7 +485,7 @@ impl PlatformSupportApple { ) { Ok(v) => v, Err(e) => { - log_net!(error "failed to get address flags: {}", e); + log_net!(debug "failed to get address flags for ifname={}, ifaddr={:?} : {}", ifname, ifaddr.ifa_addr, e); continue; } };