From 2044abfea86e96b7d0c6321bc0d12dee91a33258 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Fri, 30 Oct 2020 16:32:20 +0100 Subject: [PATCH] Adds slog based logging into a widget --- Cargo.toml | 2 ++ src/app.rs | 88 +++++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 17 ++++++++-- src/schema.rs | 22 ++++++++----- src/ui/mod.rs | 11 ++++--- 5 files changed, 112 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d2f16c4..2179424 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ smol = "1.2" signal-hook = "0.1" +slog = "2.5" + libc = "0.2" rsasl = "0.1" diff --git a/src/app.rs b/src/app.rs index ee4b570..03ae9ac 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; +use std::sync::{Arc, Mutex, MutexGuard}; use futures::prelude::*; use futures_signals::signal::{Mutable, Signal, MutableSignalCloned}; @@ -9,6 +10,16 @@ use termion::event::Key; use crate::input::Inputs; use crate::schema::Api; +use slog::{ + Drain, + Level, + Record, + OwnedKVList, +}; + +use tui::text::{Span, Spans}; +use tui::style::{Style, Color}; + #[derive(PartialEq, Eq, Debug, Clone)] pub enum Window { Main, @@ -21,23 +32,23 @@ enum ConnState { } /// Application state struct -pub struct Sute { +pub struct Sute<'a, S, F> { // TODO: BE SMART. Inputs change the state, resize signals change the state, futures completing // change the state. - pub state: Mutable, - statesig: MutableSignalCloned, + pub state: Mutable>, + statesig: MutableSignalCloned>, signal: S, inputs: Inputs, api: Option>, new: bool } -impl Sute { - pub fn new(s: S, api: F) -> Self { +impl<'a, S: Unpin, F: Unpin> Sute<'a, S, F> { + pub fn new(s: S, log: Arc>, api: F) -> Self { let inputs = Inputs::new(); - let state = Mutable::new(SuteState::new()); + let state = Mutable::new(SuteState::new(log)); Self { statesig: state.signal_cloned(), @@ -45,7 +56,7 @@ impl Sute { signal: s, inputs: inputs, api: Some(ConnState::Connecting(api)), - new: true + new: true, } } @@ -69,13 +80,13 @@ impl Sute { } } - pub fn get_state(&self) -> SuteState { + pub fn get_state(&self) -> SuteState<'a> { self.state.get_cloned() } } -impl + Unpin, F: Future + Unpin> Signal for Sute { - type Item = SuteState; +impl<'a, S: Signal + Unpin, F: Future + Unpin> Signal for Sute<'a, S, F> { + type Item = SuteState<'a>; fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { if let Poll::Ready(Some(key)) = Pin::new(&mut self.inputs).poll_next(cx) { self.handle_input(key); @@ -97,22 +108,71 @@ impl + Unpin, F: Future + Unpin> Signal fo } #[derive(Debug, Clone)] -pub struct SuteState { +pub struct SuteState<'a> { pub active_win: Window, pub size: (u16,u16), pub running: bool, pub tick: usize, pub server: Option, + pub log: Arc>, } -impl SuteState { - pub fn new() -> Self { +impl<'a> SuteState<'a> { + pub fn new(log: Arc>) -> Self { Self { active_win: Window::Main, size: (80,20), running: true, tick: 0, - server: None + server: None, + log: log, } } } + +#[derive(Debug)] +pub struct LogDrain<'a> { + inner: Mutex>>, +} + +impl<'a> LogDrain<'a> { + pub fn new() -> Self { + Self { inner: Mutex::new(Vec::new()) } + } + pub fn get_inner(&self) -> MutexGuard>> { + self.inner.lock().unwrap() + } +} +impl<'a> Drain for LogDrain<'a> { + type Ok = (); + type Err = (); + fn log(&self, record: &Record, values: &OwnedKVList) -> Result { + let critical_style: Style = Style::default().fg(Color::Magenta); + let error_style: Style = Style::default().fg(Color::Red); + let warning_style: Style = Style::default().fg(Color::Yellow); + let info_style: Style = Style::default().fg(Color::Cyan); + let debug_style: Style = Style::default().fg(Color::White); + let trace_style: Style = Style::default().fg(Color::Green); + + let lvlspan = match record.level() { + Level::Critical => Span::styled("CRIT", critical_style), + Level::Error => Span::styled("ERRO", error_style), + Level::Warning => Span::styled("WARN", warning_style), + Level::Info => Span::styled("INFO", info_style), + Level::Debug => Span::styled("DEBG", debug_style), + Level::Trace => Span::styled("TRCE", trace_style), + }; + let contentspan = Span::from(record.msg().to_string()); + + { + let mut v = self.inner.lock().unwrap(); + v.push(Spans::from(vec![ + Span::from("["), lvlspan, Span::from("]: "), + contentspan, + ])); + } + + Ok(()) + } +} + diff --git a/src/main.rs b/src/main.rs index 3ba3a9a..f4bf134 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate slog; + use std::io; use std::sync::{Arc, Mutex}; @@ -54,9 +57,19 @@ fn main() -> Result<(), io::Error> { terminal.resize(tui::layout::Rect::new(0, 0, x,y)).unwrap(); } + let drain = Arc::new(app::LogDrain::new()); + let log = slog::Logger::root(slog::Fuse::new(drain.clone()), o!()); + let resize = util::Resize::new()?; - let api = schema::Api::connect(server); - let app = app::Sute::new(resize, Box::pin(api)); + let api = schema::Api::connect(log.clone(), server); + let app = app::Sute::new(resize, drain, Box::pin(api)); + + crit!(log, "This is a test: {}", 42); + error!(log, "This is a test: {}", 42); + warn!(log, "This is a test: {}", 42); + info!(log, "This is a test: {}", 42); + debug!(log, "This is a test: {}", 42); + trace!(log, "This is a test: {}", 42); let mut state = app.get_state(); terminal.draw(|f| ui::draw_ui(f, &mut state))?; diff --git a/src/schema.rs b/src/schema.rs index 4364038..238849f 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,5 +1,7 @@ use std::ffi::CStr; +use slog::Logger; + use futures::prelude::*; use smol::io; @@ -32,11 +34,11 @@ impl Api { pub fn new(stream: TcpStream, bffh: connection_capnp::bootstrap::Client) -> Self { Self { stream, bffh } } - pub fn connect(addr: A) -> impl Future { + pub fn connect(log: Logger, addr: A) -> impl Future { let f = async { let mut stream = TcpStream::connect(addr).await.unwrap(); - handshake(&mut stream).await.unwrap(); + handshake(log.clone(), &mut stream).await.unwrap(); let network = Box::new(twoparty::VatNetwork::new(stream.clone(), stream.clone(), rpc_twoparty_capnp::Side::Client, Default::default())); @@ -44,19 +46,23 @@ impl Api { let bffh: connection_capnp::bootstrap::Client = rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server); - Api::new(stream, bffh) + let mut api = Api::new(stream, bffh); + + api.authenticate(log).await; + + api }; f } - async fn authenticate(&mut self) { + async fn authenticate(&mut self, log: Logger) { let r = self.bffh.auth_request(); let auth = r.send().pipeline.get_auth(); let m = auth.mechanisms_request().send().promise.await.unwrap(); for t in m.get().unwrap().get_mechs().unwrap().iter() { - println!("{}", t.unwrap()); + info!(log, "Mechanism {} available", t.unwrap()); } } @@ -108,7 +114,7 @@ impl Api { //} } -async fn handshake(mut stream: &mut TcpStream) -> Result<(), io::Error> { +async fn handshake(log: Logger, mut stream: &mut TcpStream) -> Result<(), io::Error> { let host = "localhost"; let program = format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); let version = (0u32,1u32); @@ -129,9 +135,9 @@ async fn handshake(mut stream: &mut TcpStream) -> Result<(), io::Error> { let major = greeting.get_major(); let minor = greeting.get_minor(); - println!("Peer {} running {} API {}.{}", peer_host, peer_program, major, minor) + info!(log, "Peer {} running {} API {}.{}", peer_host, peer_program, major, minor) } else { - println!("Oh noes"); + error!(log, "Oh noes"); } Ok(()) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b5c246a..0db9e1a 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -25,25 +25,28 @@ pub fn draw_ui(f: &mut Frame, app: &mut SuteState) { } fn draw_header(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { - f.render_widget(Clear, layout_chunk); f.render_widget(Block::default() .title("Header") .borders(Borders::ALL), layout_chunk); } fn draw_main(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { - f.render_widget(Clear, layout_chunk); let chunk = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref()) .split(layout_chunk); f.render_widget(Paragraph::new(app.server.as_ref().map(|s| s.as_str()).unwrap_or("Not connected")), chunk[0]); - f.render_widget(Paragraph::new("Main"), chunk[1]); + + draw_logs(f, app, chunk[1]) +} +fn draw_logs(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { + // TODO: Just use a signal. + let v = app.log.get_inner().clone(); + f.render_widget(Paragraph::new(v.into_iter().collect::>()), layout_chunk); } fn draw_command_line(f: &mut Frame, app: &mut SuteState, layout_chunk: Rect) { - f.render_widget(Clear, layout_chunk); f.render_widget(Block::default() .title("Command line") .borders(Borders::ALL), layout_chunk);