From 47824c5fca2b9fcb01cc93710e26f3ddd73a1868 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Fri, 23 Jul 2021 12:21:21 +0300 Subject: [PATCH] Remove page server interactive mode. It was pretty cool, but no one used it, and it had gotten badly out of date. The main interesting thing with it was to see some basic metrics on the fly, while the page server is running, but the metrics collection had been broken for a long time, too. Best to just remove it. --- Cargo.lock | 100 +++------- pageserver/Cargo.toml | 2 - pageserver/src/bin/pageserver.rs | 45 +---- pageserver/src/lib.rs | 4 - pageserver/src/logger.rs | 44 ++--- pageserver/src/repository.rs | 1 - pageserver/src/tui.rs | 307 ------------------------------- pageserver/src/tui_event.rs | 96 ---------- pageserver/src/tui_logger.rs | 199 -------------------- 9 files changed, 48 insertions(+), 750 deletions(-) delete mode 100644 pageserver/src/tui.rs delete mode 100644 pageserver/src/tui_event.rs delete mode 100644 pageserver/src/tui_logger.rs diff --git a/Cargo.lock b/Cargo.lock index 9c6e5ba00c..c8b69e0a7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "ahash" version = "0.4.7" @@ -187,12 +185,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "cc" version = "1.0.69" @@ -1054,12 +1046,6 @@ dependencies = [ "libc", ] -[[package]] -name = "numtoa" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" - [[package]] name = "once_cell" version = "1.8.0" @@ -1148,12 +1134,10 @@ dependencies = [ "slog-stdlog", "slog-term", "tar", - "termion", "thiserror", "tokio", "tokio-stream", "toml", - "tui", "walkdir", "workspace_hack", "zenith_utils", @@ -1246,24 +1230,6 @@ dependencies = [ "tokio-postgres 0.7.1", ] -[[package]] -name = "postgres-protocol" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff3e0f70d32e20923cabf2df02913be7c1842d4c772db8065c00fcfdd1d1bff3" -dependencies = [ - "base64", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand", - "sha2", - "stringprep", -] - [[package]] name = "postgres-protocol" version = "0.6.1" @@ -1283,14 +1249,21 @@ dependencies = [ ] [[package]] -name = "postgres-types" -version = "0.2.1" +name = "postgres-protocol" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430f4131e1b7657b0cd9a2b0c3408d77c9a43a042d300b8c77f981dffcc43a2f" +checksum = "ff3e0f70d32e20923cabf2df02913be7c1842d4c772db8065c00fcfdd1d1bff3" dependencies = [ + "base64", + "byteorder", "bytes", "fallible-iterator", - "postgres-protocol 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", ] [[package]] @@ -1303,6 +1276,17 @@ dependencies = [ "postgres-protocol 0.6.1 (git+https://github.com/zenithdb/rust-postgres.git?rev=9eb0dbfbeb6a6c1b79099b9f7ae4a8c021877858)", ] +[[package]] +name = "postgres-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "430f4131e1b7657b0cd9a2b0c3408d77c9a43a042d300b8c77f981dffcc43a2f" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "postgres_ffi" version = "0.1.0" @@ -1434,15 +1418,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_termios" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" -dependencies = [ - "redox_syscall", -] - [[package]] name = "redox_users" version = "0.4.0" @@ -1904,18 +1879,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "termion" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" -dependencies = [ - "libc", - "numtoa", - "redox_syscall", - "redox_termios", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -2132,19 +2095,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" -[[package]] -name = "tui" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ced152a8e9295a5b168adc254074525c17ac4a83c90b2716274cc38118bddc9" -dependencies = [ - "bitflags", - "cassowary", - "termion", - "unicode-segmentation", - "unicode-width", -] - [[package]] name = "typenum" version = "1.13.0" @@ -2169,12 +2119,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - [[package]] name = "unicode-width" version = "0.1.8" diff --git a/pageserver/Cargo.toml b/pageserver/Cargo.toml index 529f55912a..f83723cac9 100644 --- a/pageserver/Cargo.toml +++ b/pageserver/Cargo.toml @@ -21,8 +21,6 @@ slog-term = "2.8.0" slog = "2.7.0" log = "0.4.14" clap = "2.33.0" -termion = "1.5.6" -tui = "0.14.0" daemonize = "0.4.1" rust-s3 = { version = "0.27.0-rc4", features = ["no-verify-ssl"] } tokio = { version = "1.3.0", features = ["full"] } diff --git a/pageserver/src/bin/pageserver.rs b/pageserver/src/bin/pageserver.rs index 31f96e366e..d31f7dc90d 100644 --- a/pageserver/src/bin/pageserver.rs +++ b/pageserver/src/bin/pageserver.rs @@ -17,7 +17,7 @@ use anyhow::Result; use clap::{App, Arg, ArgMatches}; use daemonize::Daemonize; -use pageserver::{branches, logger, page_cache, page_service, tui, PageServerConf}; +use pageserver::{branches, logger, page_cache, page_service, PageServerConf}; const DEFAULT_LISTEN_ADDR: &str = "127.0.0.1:64000"; @@ -88,7 +88,6 @@ impl CfgFileParams { Ok(PageServerConf { daemonize: false, - interactive: false, listen_addr, gc_horizon, @@ -113,13 +112,6 @@ fn main() -> Result<()> { .takes_value(true) .help("listen for incoming page requests on ip:port (default: 127.0.0.1:5430)"), ) - .arg( - Arg::with_name("interactive") - .short("i") - .long("interactive") - .takes_value(false) - .help("Interactive mode"), - ) .arg( Arg::with_name("daemonize") .short("d") @@ -189,15 +181,9 @@ fn main() -> Result<()> { let mut conf = params.try_into_config()?; conf.daemonize = arg_matches.is_present("daemonize"); - conf.interactive = arg_matches.is_present("interactive"); - if init && (conf.daemonize || conf.interactive) { - eprintln!("--daemonize and --interactive may not be used with --init"); - exit(1); - } - - if conf.daemonize && conf.interactive { - eprintln!("--daemonize is not allowed with --interactive: choose one"); + if init && conf.daemonize { + eprintln!("--daemonize cannot be used with --init"); exit(1); } @@ -230,20 +216,6 @@ fn start_pageserver(conf: &'static PageServerConf) -> Result<()> { // Note: this `info!(...)` macro comes from `log` crate info!("standard logging redirected to slog"); - let tui_thread = if conf.interactive { - // Initialize the UI - Some( - thread::Builder::new() - .name("UI thread".into()) - .spawn(|| { - let _ = tui::ui_main(); - }) - .unwrap(), - ) - } else { - None - }; - // TODO: Check that it looks like a valid repository before going further if conf.daemonize { @@ -280,14 +252,9 @@ fn start_pageserver(conf: &'static PageServerConf) -> Result<()> { .name("Page Service thread".into()) .spawn(move || page_service::thread_main(conf, pageserver_listener))?; - if let Some(tui_thread) = tui_thread { - // The TUI thread exits when the user asks to Quit. - tui_thread.join().unwrap(); - } else { - page_service_thread - .join() - .expect("Page service thread has panicked")? - } + page_service_thread + .join() + .expect("Page service thread has panicked")?; Ok(()) } diff --git a/pageserver/src/lib.rs b/pageserver/src/lib.rs index f9c3703d77..4db9e74a53 100644 --- a/pageserver/src/lib.rs +++ b/pageserver/src/lib.rs @@ -18,9 +18,6 @@ pub mod page_service; pub mod repository; pub mod restore_local_repo; pub mod rocksdb_storage; -pub mod tui; -pub mod tui_event; -mod tui_logger; pub mod waldecoder; pub mod walreceiver; pub mod walredo; @@ -28,7 +25,6 @@ pub mod walredo; #[derive(Debug, Clone)] pub struct PageServerConf { pub daemonize: bool, - pub interactive: bool, pub listen_addr: String, pub gc_horizon: u64, pub gc_period: Duration, diff --git a/pageserver/src/logger.rs b/pageserver/src/logger.rs index b1a98bbb7e..56006a54af 100644 --- a/pageserver/src/logger.rs +++ b/pageserver/src/logger.rs @@ -1,11 +1,11 @@ -use crate::{tui, PageServerConf}; +use crate::PageServerConf; use anyhow::{Context, Result}; use slog::{Drain, FnValue}; use std::fs::{File, OpenOptions}; pub fn init_logging( - conf: &PageServerConf, + _conf: &PageServerConf, log_filename: &str, ) -> Result<(slog_scope::GlobalLoggerGuard, File)> { // Don't open the same file for output multiple times; @@ -18,32 +18,28 @@ pub fn init_logging( let logger_file = log_file.try_clone().unwrap(); - if conf.interactive { - Ok((tui::init_logging(), logger_file)) - } else { - let decorator = slog_term::PlainSyncDecorator::new(logger_file); - let drain = slog_term::FullFormat::new(decorator).build(); - let drain = slog::Filter::new(drain, |record: &slog::Record| { - if record.level().is_at_least(slog::Level::Info) { - return true; - } - false - }); - let drain = std::sync::Mutex::new(drain).fuse(); - let logger = slog::Logger::root( - drain, - slog::o!( - "location" => + let decorator = slog_term::PlainSyncDecorator::new(logger_file); + let drain = slog_term::FullFormat::new(decorator).build(); + let drain = slog::Filter::new(drain, |record: &slog::Record| { + if record.level().is_at_least(slog::Level::Info) { + return true; + } + false + }); + let drain = std::sync::Mutex::new(drain).fuse(); + let logger = slog::Logger::root( + drain, + slog::o!( + "location" => FnValue(move |record| { format!("{}, {}:{}", record.module(), record.file(), record.line() - ) - } + ) + } ) - ), - ); - Ok((slog_scope::set_global_logger(logger), log_file)) - } + ), + ); + Ok((slog_scope::set_global_logger(logger), log_file)) } diff --git a/pageserver/src/repository.rs b/pageserver/src/repository.rs index ffa7cf4fe6..2b809ef563 100644 --- a/pageserver/src/repository.rs +++ b/pageserver/src/repository.rs @@ -361,7 +361,6 @@ mod tests { let conf = PageServerConf { daemonize: false, - interactive: false, gc_horizon: 64 * 1024 * 1024, gc_period: Duration::from_secs(10), listen_addr: "127.0.0.1:5430".to_string(), diff --git a/pageserver/src/tui.rs b/pageserver/src/tui.rs deleted file mode 100644 index a94719a1ab..0000000000 --- a/pageserver/src/tui.rs +++ /dev/null @@ -1,307 +0,0 @@ -use crate::tui_event::{Event, Events}; -use crate::tui_logger::TuiLogger; -use crate::tui_logger::TuiLoggerWidget; - -use lazy_static::lazy_static; -use std::sync::Arc; -use std::{error::Error, io}; -use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; -use tui::backend::TermionBackend; -use tui::buffer::Buffer; -use tui::layout::{Constraint, Direction, Layout, Rect}; -use tui::style::{Color, Modifier, Style}; -use tui::text::{Span, Spans, Text}; -use tui::widgets::{Block, BorderType, Borders, Paragraph, Widget}; -use tui::Terminal; - -use slog::Drain; - -lazy_static! { - pub static ref PAGESERVICE_DRAIN: Arc = Arc::new(TuiLogger::default()); - pub static ref WALRECEIVER_DRAIN: Arc = Arc::new(TuiLogger::default()); - pub static ref WALREDO_DRAIN: Arc = Arc::new(TuiLogger::default()); - pub static ref CATCHALL_DRAIN: Arc = Arc::new(TuiLogger::default()); -} - -pub fn init_logging() -> slog_scope::GlobalLoggerGuard { - let pageservice_drain = - slog::Filter::new(PAGESERVICE_DRAIN.as_ref(), |record: &slog::Record| { - if record.level().is_at_least(slog::Level::Debug) - && record.module().starts_with("pageserver::page_service") - { - return true; - } - false - }) - .fuse(); - - let walredo_drain = slog::Filter::new(WALREDO_DRAIN.as_ref(), |record: &slog::Record| { - if record.level().is_at_least(slog::Level::Debug) - && record.module().starts_with("pageserver::walredo") - { - return true; - } - false - }) - .fuse(); - - let walreceiver_drain = - slog::Filter::new(WALRECEIVER_DRAIN.as_ref(), |record: &slog::Record| { - if record.level().is_at_least(slog::Level::Debug) - && record.module().starts_with("pageserver::walreceiver") - { - return true; - } - false - }) - .fuse(); - - let catchall_drain = slog::Filter::new(CATCHALL_DRAIN.as_ref(), |record: &slog::Record| { - if record.level().is_at_least(slog::Level::Info) { - return true; - } - if record.level().is_at_least(slog::Level::Debug) - && record.module().starts_with("pageserver") - { - return true; - } - false - }) - .fuse(); - - let drain = pageservice_drain; - let drain = slog::Duplicate::new(drain, walreceiver_drain).fuse(); - let drain = slog::Duplicate::new(drain, walredo_drain).fuse(); - let drain = slog::Duplicate::new(drain, catchall_drain).fuse(); - let drain = slog_async::Async::new(drain).chan_size(1000).build().fuse(); - let drain = slog::Filter::new(drain, |record: &slog::Record| { - if record.level().is_at_least(slog::Level::Info) { - return true; - } - if record.level().is_at_least(slog::Level::Debug) - && record.module().starts_with("pageserver") - { - return true; - } - - false - }) - .fuse(); - let logger = slog::Logger::root(drain, slog::o!()); - slog_scope::set_global_logger(logger) -} - -pub fn ui_main() -> Result<(), Box> { - // Terminal initialization - let stdout = io::stdout().into_raw_mode()?; - let stdout = MouseTerminal::from(stdout); - let stdout = AlternateScreen::from(stdout); - let backend = TermionBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - - // Setup event handlers - let events = Events::new(); - - loop { - terminal.draw(|f| { - let size = f.size(); - - // +----------------+----------------+ - // | | | - // | top_top_left | top_top_right | - // | | | - // +----------------+----------------| - // | | | - // | top_bot_left | top_left_right | - // | | | - // +----------------+----------------+ - // | | - // | bottom | - // | | - // +---------------------------------+ - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Percentage(70), Constraint::Percentage(30)].as_ref()) - .split(size); - let top_chunk = chunks[0]; - let bottom_chunk = chunks[1]; - - let top_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) - .split(top_chunk); - let top_left_chunk = top_chunks[0]; - let top_right_chunk = top_chunks[1]; - - let c = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) - .split(top_left_chunk); - let top_top_left_chunk = c[0]; - let top_bot_left_chunk = c[1]; - - let c = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) - .split(top_right_chunk); - let top_top_right_chunk = c[0]; - let top_bot_right_chunk = c[1]; - - f.render_widget( - LogWidget::new(PAGESERVICE_DRAIN.as_ref(), "Page Service"), - top_top_left_chunk, - ); - - f.render_widget( - LogWidget::new(WALREDO_DRAIN.as_ref(), "WAL Redo"), - top_bot_left_chunk, - ); - - f.render_widget( - LogWidget::new(WALRECEIVER_DRAIN.as_ref(), "WAL Receiver"), - top_top_right_chunk, - ); - - f.render_widget(MetricsWidget {}, top_bot_right_chunk); - - f.render_widget( - LogWidget::new(CATCHALL_DRAIN.as_ref(), "All Log").show_module(true), - bottom_chunk, - ); - })?; - - // If ther user presses 'q', quit. - - // silence clippy's suggestion to rewrite this as an if-statement. Match - // makes more sense as soon as we get another command than 'q'. - #[allow(clippy::single_match)] - #[allow(clippy::collapsible_match)] - if let Event::Input(key) = events.next()? { - match key { - Key::Char('q') => { - break; - } - _ => (), - } - } - } - - terminal.show_cursor().unwrap(); - terminal.clear().unwrap(); - - Ok(()) -} - -#[allow(dead_code)] -struct LogWidget<'a> { - logger: &'a TuiLogger, - title: &'a str, - show_module: bool, -} - -impl<'a> LogWidget<'a> { - fn new(logger: &'a TuiLogger, title: &'a str) -> LogWidget<'a> { - LogWidget { - logger, - title, - show_module: false, - } - } - - fn show_module(mut self, b: bool) -> LogWidget<'a> { - self.show_module = b; - self - } -} - -impl<'a> Widget for LogWidget<'a> { - fn render(self, area: Rect, buf: &mut Buffer) { - let w = TuiLoggerWidget::default(self.logger) - .block( - Block::default() - .borders(Borders::ALL) - .title(self.title) - .border_type(BorderType::Rounded), - ) - .show_module(true) - .style_error(Style::default().fg(Color::Red)) - .style_warn(Style::default().fg(Color::Yellow)) - .style_info(Style::default().fg(Color::Green)); - w.render(area, buf); - } -} - -// Render a widget to show some metrics -struct MetricsWidget {} - -fn _get_metric_u64(title: &str, value: u64) -> Spans { - Spans::from(vec![ - Span::styled(format!("{:<20}", title), Style::default()), - Span::raw(": "), - Span::styled( - value.to_string(), - Style::default().add_modifier(Modifier::BOLD), - ), - ]) -} - -// This is not used since LSNs were removed from page cache stats. -// Maybe it will be used in the future? -fn _get_metric_str<'a>(title: &str, value: &'a str) -> Spans<'a> { - Spans::from(vec![ - Span::styled(format!("{:<20}", title), Style::default()), - Span::raw(": "), - Span::styled(value, Style::default().add_modifier(Modifier::BOLD)), - ]) -} - -impl tui::widgets::Widget for MetricsWidget { - fn render(self, area: Rect, buf: &mut Buffer) { - let block = Block::default() - .borders(Borders::ALL) - .title("Page Cache Metrics") - .border_type(BorderType::Rounded); - let inner_area = block.inner(area); - - block.render(area, buf); - - #[allow(unused_mut)] - let mut lines: Vec = Vec::new(); - - // FIXME - //let page_cache_stats = crate::page_cache::get_stats(); - - // This is not used since LSNs were removed from page cache stats. - // Maybe it will be used in the future? - /* - let lsnrange = format!( - "{} - {}", - page_cache_stats.first_valid_lsn, page_cache_stats.last_valid_lsn - ); - let last_valid_recordlsn_str = page_cache_stats.last_record_lsn.to_string(); - lines.push(get_metric_str("Valid LSN range", &lsnrange)); - lines.push(get_metric_str("Last record LSN", &last_valid_recordlsn_str)); - */ - /* - lines.push(get_metric_u64( - "# of cache entries", - page_cache_stats.num_entries, - )); - lines.push(get_metric_u64( - "# of page images", - page_cache_stats.num_page_images, - )); - lines.push(get_metric_u64( - "# of WAL records", - page_cache_stats.num_wal_records, - )); - lines.push(get_metric_u64( - "# of GetPage@LSN calls", - page_cache_stats.num_getpage_requests, - )); - */ - let text = Text::from(lines); - - Paragraph::new(text).render(inner_area, buf); - } -} diff --git a/pageserver/src/tui_event.rs b/pageserver/src/tui_event.rs deleted file mode 100644 index bafb6242e5..0000000000 --- a/pageserver/src/tui_event.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::io; -use std::sync::mpsc; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, -}; -use std::thread; -use std::time::Duration; - -use termion::event::Key; -use termion::input::TermRead; - -pub enum Event { - Input(I), - Tick, -} - -/// A small event handler that wrap termion input and tick events. Each event -/// type is handled in its own thread and returned to a common `Receiver` -#[allow(dead_code)] -pub struct Events { - rx: mpsc::Receiver>, - input_handle: thread::JoinHandle<()>, - ignore_exit_key: Arc, - tick_handle: thread::JoinHandle<()>, -} - -#[derive(Debug, Clone, Copy)] -pub struct Config { - pub exit_key: Key, - pub tick_rate: Duration, -} - -impl Default for Config { - fn default() -> Config { - Config { - exit_key: Key::Char('q'), - tick_rate: Duration::from_millis(250), - } - } -} - -impl Events { - pub fn new() -> Events { - Events::with_config(Config::default()) - } - - pub fn with_config(config: Config) -> Events { - let (tx, rx) = mpsc::channel(); - let ignore_exit_key = Arc::new(AtomicBool::new(false)); - let input_handle = { - let tx = tx.clone(); - let ignore_exit_key = ignore_exit_key.clone(); - thread::spawn(move || { - let stdin = io::stdin(); - for evt in stdin.keys() { - // This will panic if stdin returns EOF. - let key = evt.unwrap(); - if let Err(err) = tx.send(Event::Input(key)) { - eprintln!("{}", err); - return; - } - if !ignore_exit_key.load(Ordering::Relaxed) && key == config.exit_key { - return; - } - } - }) - }; - let tick_handle = { - thread::spawn(move || loop { - if tx.send(Event::Tick).is_err() { - break; - } - thread::sleep(config.tick_rate); - }) - }; - Events { - rx, - input_handle, - ignore_exit_key, - tick_handle, - } - } - - pub fn next(&self) -> Result, mpsc::RecvError> { - self.rx.recv() - } - - pub fn disable_exit_key(&mut self) { - self.ignore_exit_key.store(true, Ordering::Relaxed); - } - - pub fn enable_exit_key(&mut self) { - self.ignore_exit_key.store(false, Ordering::Relaxed); - } -} diff --git a/pageserver/src/tui_logger.rs b/pageserver/src/tui_logger.rs deleted file mode 100644 index 663add4065..0000000000 --- a/pageserver/src/tui_logger.rs +++ /dev/null @@ -1,199 +0,0 @@ -// -// A TUI Widget that displays log entries -// -// This is heavily inspired by gin66's tui_logger crate at https://github.com/gin66/tui-logger, -// but I wrote this based on the 'slog' module, which simplified things a lot. tui-logger also -// implemented the slog Drain trait, but it had a model of one global buffer for the records. -// With this implementation, each TuiLogger is a separate ring buffer and separate slog Drain. -// Also, I didn't do any of the "hot log" stuff that gin66's implementation had, you can use an -// AsyncDrain to buffer and handle overflow if desired. -// -use chrono::offset::Local; -use chrono::DateTime; -use slog::{Drain, Level, OwnedKVList, Record}; -use slog_async::AsyncRecord; -use std::collections::VecDeque; -use std::sync::Mutex; -use std::time::SystemTime; -use tui::buffer::Buffer; -use tui::layout::Rect; -use tui::style::{Modifier, Style}; -use tui::text::{Span, Spans}; -use tui::widgets::{Block, Paragraph, Widget, Wrap}; - -// Size of the log ring buffer, in # of records -static BUFFER_SIZE: usize = 1000; - -pub struct TuiLogger { - events: Mutex>, -} - -impl<'a> Default for TuiLogger { - fn default() -> TuiLogger { - TuiLogger { - events: Mutex::new(VecDeque::with_capacity(BUFFER_SIZE)), - } - } -} - -impl Drain for TuiLogger { - type Ok = (); - type Err = slog::Error; - - fn log(&self, record: &Record, values: &OwnedKVList) -> Result { - let mut events = self.events.lock().unwrap(); - - let now = SystemTime::now(); - let asyncrec = AsyncRecord::from(record, values); - events.push_front((now, asyncrec)); - - if events.len() > BUFFER_SIZE { - events.pop_back(); - } - - Ok(()) - } -} - -// TuiLoggerWidget renders a TuiLogger ring buffer -pub struct TuiLoggerWidget<'b> { - block: Option>, - /// Base style of the widget - style: Style, - /// Level based style - style_error: Option