Adds slog based logging into a widget

This commit is contained in:
Gregor Reitzenstein 2020-10-30 16:32:20 +01:00
parent 4cead5a94f
commit 2044abfea8
5 changed files with 112 additions and 28 deletions

View File

@ -26,6 +26,8 @@ smol = "1.2"
signal-hook = "0.1" signal-hook = "0.1"
slog = "2.5"
libc = "0.2" libc = "0.2"
rsasl = "0.1" rsasl = "0.1"

View File

@ -1,5 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::sync::{Arc, Mutex, MutexGuard};
use futures::prelude::*; use futures::prelude::*;
use futures_signals::signal::{Mutable, Signal, MutableSignalCloned}; use futures_signals::signal::{Mutable, Signal, MutableSignalCloned};
@ -9,6 +10,16 @@ use termion::event::Key;
use crate::input::Inputs; use crate::input::Inputs;
use crate::schema::Api; 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)] #[derive(PartialEq, Eq, Debug, Clone)]
pub enum Window { pub enum Window {
Main, Main,
@ -21,23 +32,23 @@ enum ConnState<F> {
} }
/// Application state struct /// Application state struct
pub struct Sute<S, F> { pub struct Sute<'a, S, F> {
// TODO: BE SMART. Inputs change the state, resize signals change the state, futures completing // TODO: BE SMART. Inputs change the state, resize signals change the state, futures completing
// change the state. // change the state.
pub state: Mutable<SuteState>, pub state: Mutable<SuteState<'a>>,
statesig: MutableSignalCloned<SuteState>, statesig: MutableSignalCloned<SuteState<'a>>,
signal: S, signal: S,
inputs: Inputs, inputs: Inputs,
api: Option<ConnState<F>>, api: Option<ConnState<F>>,
new: bool new: bool
} }
impl<S: Unpin, F: Unpin> Sute<S, F> { impl<'a, S: Unpin, F: Unpin> Sute<'a, S, F> {
pub fn new(s: S, api: F) -> Self { pub fn new(s: S, log: Arc<LogDrain<'a>>, api: F) -> Self {
let inputs = Inputs::new(); let inputs = Inputs::new();
let state = Mutable::new(SuteState::new()); let state = Mutable::new(SuteState::new(log));
Self { Self {
statesig: state.signal_cloned(), statesig: state.signal_cloned(),
@ -45,7 +56,7 @@ impl<S: Unpin, F: Unpin> Sute<S, F> {
signal: s, signal: s,
inputs: inputs, inputs: inputs,
api: Some(ConnState::Connecting(api)), api: Some(ConnState::Connecting(api)),
new: true new: true,
} }
} }
@ -69,13 +80,13 @@ impl<S: Unpin, F: Unpin> Sute<S, F> {
} }
} }
pub fn get_state(&self) -> SuteState { pub fn get_state(&self) -> SuteState<'a> {
self.state.get_cloned() self.state.get_cloned()
} }
} }
impl<S: Signal<Item=(u16,u16)> + Unpin, F: Future<Output=Api> + Unpin> Signal for Sute<S, F> { impl<'a, S: Signal<Item=(u16,u16)> + Unpin, F: Future<Output=Api> + Unpin> Signal for Sute<'a, S, F> {
type Item = SuteState; type Item = SuteState<'a>;
fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> { fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
if let Poll::Ready(Some(key)) = Pin::new(&mut self.inputs).poll_next(cx) { if let Poll::Ready(Some(key)) = Pin::new(&mut self.inputs).poll_next(cx) {
self.handle_input(key); self.handle_input(key);
@ -97,22 +108,71 @@ impl<S: Signal<Item=(u16,u16)> + Unpin, F: Future<Output=Api> + Unpin> Signal fo
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SuteState { pub struct SuteState<'a> {
pub active_win: Window, pub active_win: Window,
pub size: (u16,u16), pub size: (u16,u16),
pub running: bool, pub running: bool,
pub tick: usize, pub tick: usize,
pub server: Option<String>, pub server: Option<String>,
pub log: Arc<LogDrain<'a>>,
} }
impl SuteState { impl<'a> SuteState<'a> {
pub fn new() -> Self { pub fn new(log: Arc<LogDrain<'a>>) -> Self {
Self { Self {
active_win: Window::Main, active_win: Window::Main,
size: (80,20), size: (80,20),
running: true, running: true,
tick: 0, tick: 0,
server: None server: None,
log: log,
} }
} }
} }
#[derive(Debug)]
pub struct LogDrain<'a> {
inner: Mutex<Vec<Spans<'a>>>,
}
impl<'a> LogDrain<'a> {
pub fn new() -> Self {
Self { inner: Mutex::new(Vec::new()) }
}
pub fn get_inner(&self) -> MutexGuard<Vec<Spans<'a>>> {
self.inner.lock().unwrap()
}
}
impl<'a> Drain for LogDrain<'a> {
type Ok = ();
type Err = ();
fn log(&self, record: &Record, values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
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(())
}
}

View File

@ -1,3 +1,6 @@
#[macro_use]
extern crate slog;
use std::io; use std::io;
use std::sync::{Arc, Mutex}; 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(); 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 resize = util::Resize::new()?;
let api = schema::Api::connect(server); let api = schema::Api::connect(log.clone(), server);
let app = app::Sute::new(resize, Box::pin(api)); 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(); let mut state = app.get_state();
terminal.draw(|f| ui::draw_ui(f, &mut state))?; terminal.draw(|f| ui::draw_ui(f, &mut state))?;

View File

@ -1,5 +1,7 @@
use std::ffi::CStr; use std::ffi::CStr;
use slog::Logger;
use futures::prelude::*; use futures::prelude::*;
use smol::io; use smol::io;
@ -32,11 +34,11 @@ impl Api {
pub fn new(stream: TcpStream, bffh: connection_capnp::bootstrap::Client) -> Self { pub fn new(stream: TcpStream, bffh: connection_capnp::bootstrap::Client) -> Self {
Self { stream, bffh } Self { stream, bffh }
} }
pub fn connect<A: AsyncToSocketAddrs>(addr: A) -> impl Future<Output=Api> { pub fn connect<A: AsyncToSocketAddrs>(log: Logger, addr: A) -> impl Future<Output=Api> {
let f = async { let f = async {
let mut stream = TcpStream::connect(addr).await.unwrap(); 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(), let network = Box::new(twoparty::VatNetwork::new(stream.clone(), stream.clone(),
rpc_twoparty_capnp::Side::Client, Default::default())); rpc_twoparty_capnp::Side::Client, Default::default()));
@ -44,19 +46,23 @@ impl Api {
let bffh: connection_capnp::bootstrap::Client let bffh: connection_capnp::bootstrap::Client
= rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server); = 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 f
} }
async fn authenticate(&mut self) { async fn authenticate(&mut self, log: Logger) {
let r = self.bffh.auth_request(); let r = self.bffh.auth_request();
let auth = r.send().pipeline.get_auth(); let auth = r.send().pipeline.get_auth();
let m = auth.mechanisms_request().send().promise.await.unwrap(); let m = auth.mechanisms_request().send().promise.await.unwrap();
for t in m.get().unwrap().get_mechs().unwrap().iter() { 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 host = "localhost";
let program = format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); let program = format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
let version = (0u32,1u32); let version = (0u32,1u32);
@ -129,9 +135,9 @@ async fn handshake(mut stream: &mut TcpStream) -> Result<(), io::Error> {
let major = greeting.get_major(); let major = greeting.get_major();
let minor = greeting.get_minor(); 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 { } else {
println!("Oh noes"); error!(log, "Oh noes");
} }
Ok(()) Ok(())

View File

@ -25,25 +25,28 @@ pub fn draw_ui<B: Backend>(f: &mut Frame<B>, app: &mut SuteState) {
} }
fn draw_header<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) { fn draw_header<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
f.render_widget(Clear, layout_chunk);
f.render_widget(Block::default() f.render_widget(Block::default()
.title("Header") .title("Header")
.borders(Borders::ALL), layout_chunk); .borders(Borders::ALL), layout_chunk);
} }
fn draw_main<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) { fn draw_main<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
f.render_widget(Clear, layout_chunk);
let chunk = Layout::default() let chunk = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref()) .constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref())
.split(layout_chunk); .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(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<B: Backend>(f: &mut Frame<B>, 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::<Vec<tui::text::Spans>>()), layout_chunk);
} }
fn draw_command_line<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) { fn draw_command_line<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
f.render_widget(Clear, layout_chunk);
f.render_widget(Block::default() f.render_widget(Block::default()
.title("Command line") .title("Command line")
.borders(Borders::ALL), layout_chunk); .borders(Borders::ALL), layout_chunk);