diff --git a/server/schema/server-properties.json b/server/schema/server-properties.json index 76bc06e..e12065b 100644 --- a/server/schema/server-properties.json +++ b/server/schema/server-properties.json @@ -7,6 +7,10 @@ "default": "0.0.0.0:50052", "type": "string" }, + "logger": { + "default": "barebones", + "type": "string" + }, "name": { "default": "Spah's sappin' mah sentreh", "type": "string" @@ -14,10 +18,6 @@ "schema": { "default": "https://theperkinrex.duckdns.org/CardsSimulator/cards-simulator/raw/branch/main/server/schema/server-properties.json", "type": "string" - }, - "use_colors": { - "default": true, - "type": "boolean" } } } \ No newline at end of file diff --git a/server/src/games/run.rs b/server/src/games/run.rs index bb8f72b..d0dcfe5 100644 --- a/server/src/games/run.rs +++ b/server/src/games/run.rs @@ -141,7 +141,10 @@ impl RunningGame { let mut arr = match self.functions.turn_end(data, self.current_player) { Err(x) => match x.as_ref() { EvalAltResult::Return(x, _) => Ok(x.clone_cast()), - _ =>{log::debug!("ERR: {}", x);Err(x)}, + _ => { + log::debug!("ERR: {}", x); + Err(x) + } }, x => x, }?; diff --git a/server/src/logger.rs b/server/src/logger.rs index 6c32b36..c4dd1c9 100644 --- a/server/src/logger.rs +++ b/server/src/logger.rs @@ -1,9 +1,17 @@ use std::sync::mpsc; mod ansi; +mod barebones; + use fern::Dispatch; -use crate::{allocator::{self, Allocator}, command::command_handler, server_properties::ServerProperties}; +use crate::{ + allocator::{self, Allocator}, + command::command_handler, + server_properties::ServerProperties, +}; + +use self::{ansi::ANSIFrontend, barebones::BarebonesFrontend}; pub type Stdout = mpsc::Sender; pub type Stdin = mpsc::Receiver; @@ -11,7 +19,7 @@ pub type Stdin = mpsc::Receiver; pub struct Close { close: mpsc::Sender<()>, handle: tokio::task::JoinHandle<()>, - close_handler: Box + close_handler: Box, } impl Close { @@ -25,7 +33,7 @@ impl Close { trait LoggingFrontend { fn setup( properties: &crate::server_properties::ServerProperties, - logger_filter: Dispatch + logger_filter: Dispatch, ) -> anyhow::Result<(Close, Stdin)> { Self::setup_with_handler(properties, command_handler, logger_filter) } @@ -33,12 +41,8 @@ trait LoggingFrontend { fn setup_with_handler String + Send + 'static>( properties: &crate::server_properties::ServerProperties, handler: F, - logger_filter: Dispatch + logger_filter: Dispatch, ) -> anyhow::Result<(Close, Stdin)>; - - fn cleanup(&self); - - fn name() -> &'static str; } pub fn setup(properties: &ServerProperties) -> anyhow::Result<(Close, Stdin)> { @@ -51,13 +55,20 @@ pub fn setup(properties: &ServerProperties) -> anyhow::Result<(Close, Stdin BarebonesFrontend::setup(properties, d), + "ANSI" => ANSIFrontend::setup(properties, d), + x => { + println!("Unrecognized logging frontend {}, please select a valid one (barebones, ANSI) from the ones featured on your build, whose list could be smaller than the one shown", x); + Err(anyhow::anyhow!("Unrecognized logging frontend")) + } + } } fn simple_formatter(d: fern::Dispatch) -> fern::Dispatch { diff --git a/server/src/logger/ansi.rs b/server/src/logger/ansi.rs index 9a5374a..efeb52d 100644 --- a/server/src/logger/ansi.rs +++ b/server/src/logger/ansi.rs @@ -12,25 +12,23 @@ use crossterm::{ terminal::{Clear, ClearType}, }; -use fern::Dispatch; use fern::colors::{Color, ColoredLevelConfig}; +use fern::Dispatch; mod color_message; use color_message::print_line; use crate::allocator::{self, Allocator}; -use crate::logger::simple_formatter; pub(super) struct ANSIFrontend; impl LoggingFrontend for ANSIFrontend { fn setup_with_handler String + Send + 'static>( - properties: &crate::server_properties::ServerProperties, + _: &crate::server_properties::ServerProperties, handler: F, - logger_filter: Dispatch + logger_filter: Dispatch, ) -> anyhow::Result<(Close, Stdin)> { - let (stdout, stdin, close, join_handle) = - TerminalHandler::new(handler, properties.use_colors); + let (stdout, stdin, close, join_handle) = TerminalHandler::new(handler); // let simple_formatter = |out: fern::FormatCallback, message: _, record: &log::Record| { // out.finish(format_args!( @@ -41,36 +39,22 @@ impl LoggingFrontend for ANSIFrontend { // message, // )) // }; - let stdout_logger = if properties.use_colors { - colored_formatter(fern::Dispatch::new()) - } else { - simple_formatter(fern::Dispatch::new()) - } - .chain(stdout); - + let stdout_logger = colored_formatter(fern::Dispatch::new()).chain(stdout); - logger_filter - .chain(stdout_logger) - .apply()?; + logger_filter.chain(stdout_logger).apply()?; log::info!("Saving output to output.log"); // std::process::exit(1); Ok(( Close { close, handle: join_handle, - close_handler: Box::new(|| queue!(std::io::stdout(), crossterm::event::DisableMouseCapture).unwrap()) + close_handler: Box::new(|| { + queue!(std::io::stdout(), crossterm::event::DisableMouseCapture).unwrap() + }), }, stdin, )) } - - fn cleanup(&self) { - cleanup() - } - - fn name() -> &'static str { - "ANSI" - } } fn cleanup() { @@ -128,7 +112,6 @@ where handler: F, lines: LimitedVec, command: String, - use_colors: bool, } impl TerminalHandler @@ -139,7 +122,6 @@ where #[allow(clippy::new_ret_no_self)] fn new( handler: F, - use_colors: bool, ) -> ( Stdout, Stdin, @@ -160,7 +142,6 @@ where handler, lines: LimitedVec::new(crossterm::terminal::size().unwrap().1 as usize - 1), command: String::new(), - use_colors, }; loop { if s.iterate() { @@ -256,8 +237,8 @@ where queue!(stdout, Clear(ClearType::All), MoveTo(0, 0)).unwrap(); let mut i = 0; let size = crossterm::terminal::size().unwrap(); - for v in self.lines.iter(self.use_colors) { - print_line(&v, self.use_colors, &mut stdout); + for v in self.lines.iter() { + print_line(&v, &mut stdout); queue!(stdout, MoveToNextLine(1)).unwrap(); i += 1; if i >= size.1 as usize - 1 { @@ -319,7 +300,7 @@ impl LimitedVec { // assert!(self.inner.len() <= self.size) } - fn iter<'a>(&'a self, use_colors: bool) -> Box + 'a> + fn iter<'a>(&'a self) -> Box + 'a> where T: std::fmt::Display, { @@ -341,15 +322,11 @@ impl LimitedVec { let map = iter.enumerate().map(move |(i, x)| { format!( "{}{}", - if use_colors { - format!( - "\x1B[{}m{:>5}\x1B[0m ", - fern::colors::Color::BrightBlack.to_fg_str(), - start + i - ) - } else { - String::new() - }, + format!( + "\x1B[{}m{:>5}\x1B[0m ", + fern::colors::Color::BrightBlack.to_fg_str(), + start + i + ), x ) }); diff --git a/server/src/logger/ansi/color_message.rs b/server/src/logger/ansi/color_message.rs index 38cb717..8c4bfbb 100644 --- a/server/src/logger/ansi/color_message.rs +++ b/server/src/logger/ansi/color_message.rs @@ -9,42 +9,38 @@ lazy_static! { pub static ref ANSI_REGEX: Regex = Regex::new(ANSI_RE).unwrap(); } -pub fn print_line(message: &str, use_colors: bool, stdout: &mut std::io::Stdout) { - if use_colors { - let mut s = String::new(); - let mut highlight = false; - let mut i = 0; - let mut current = "\x1B[0m".to_string(); - for c in message.chars() { - if c == '`' { - // queue!(stdout, Print(format!("{}`", highlight))).unwrap(); - if highlight { - let styled = format!( - "\x1B[{}m{}{}", - fern::colors::Color::Blue.to_fg_str(), - s, - current - ); - queue!(stdout, Print(styled)).unwrap(); - } else { - queue!(stdout, Print(s)).unwrap(); - } - s = String::new(); +pub fn print_line(message: &str, stdout: &mut std::io::Stdout) { + let mut s = String::new(); + let mut highlight = false; + let mut i = 0; + let mut current = "\x1B[0m".to_string(); + for c in message.chars() { + if c == '`' { + // queue!(stdout, Print(format!("{}`", highlight))).unwrap(); + if highlight { + let styled = format!( + "\x1B[{}m{}{}", + fern::colors::Color::Blue.to_fg_str(), + s, + current + ); + queue!(stdout, Print(styled)).unwrap(); + } else { + queue!(stdout, Print(s)).unwrap(); + } + s = String::new(); - highlight = !highlight; - continue; - } else if !highlight { - if let Some(m) = ANSI_REGEX.find_at(message, i) { - if i == m.start() { - current = m.as_str().to_string(); - } + highlight = !highlight; + continue; + } else if !highlight { + if let Some(m) = ANSI_REGEX.find_at(message, i) { + if i == m.start() { + current = m.as_str().to_string(); } } - i += 1; - s.push(c); } - queue!(stdout, Print(&s)).unwrap(); - } else { - queue!(stdout, Print(message)).unwrap(); + i += 1; + s.push(c); } + queue!(stdout, Print(&s)).unwrap(); } diff --git a/server/src/logger/barebones.rs b/server/src/logger/barebones.rs new file mode 100644 index 0000000..4d93ba0 --- /dev/null +++ b/server/src/logger/barebones.rs @@ -0,0 +1,74 @@ +use std::{io::stdout, sync::mpsc, time::Duration}; + +use fern::Dispatch; +use tokio::{ + io::{AsyncBufReadExt, BufReader}, + select, spawn, + task::spawn_blocking, +}; + +use crate::logger::simple_formatter; + +use super::{Close, LoggingFrontend, Stdin}; + +pub(super) struct BarebonesFrontend; + +impl LoggingFrontend for BarebonesFrontend { + fn setup_with_handler String + Send + 'static>( + _: &crate::server_properties::ServerProperties, + handler: F, + logger_filter: fern::Dispatch, + ) -> anyhow::Result<(Close, Stdin)> { + let l = simple_formatter(Dispatch::new()).chain(stdout()); + logger_filter.chain(l).apply()?; + + log::info!("Saving output to output.log"); + + let (stdin_send, stdin) = mpsc::channel(); + let (close, close_recv) = mpsc::channel(); + let (close_send_async, mut close_recv_async) = tokio::sync::mpsc::unbounded_channel(); + + spawn_blocking(move || { + while let Err(mpsc::TryRecvError::Empty) = close_recv.try_recv() {} + close_send_async + .send(()) + .expect("Error resending close signal"); + }); + + let handle = spawn(async move { + select! {_ = close_recv_async.recv() => cleanup(), + _ = async move { + let mut lines = BufReader::new(tokio::io::stdin()).lines(); + while let Some(line) = lines.next_line().await.expect("Error reading line") { + log::info!(target: "command", " >> {}", line); + stdin_send.send(handler(line)).expect("Error sending input line"); + } + } => (), + _ = tokio::signal::ctrl_c() => {println!(); cleanup(); std::process::exit(0)}, + } + }); + + std::panic::set_hook(Box::new(|info| { + let thread = std::thread::current(); + let name = thread.name().unwrap_or(""); + log::error!("thread '{}' {}", name, info); + log::info!("Exiting in 5 seconds"); + std::thread::sleep(Duration::from_secs(5)); + std::process::exit(0) + })); + + Ok(( + Close { + close, + handle, + close_handler: Box::new(|| {}), + }, + stdin, + )) + } +} + +fn cleanup() { + log::info!(target: "cleanup", "Exiting server"); + println!("Exiting server, output has been saved to output.log"); +} diff --git a/server/src/server_properties.rs b/server/src/server_properties.rs index 8778bcf..75fb0e3 100644 --- a/server/src/server_properties.rs +++ b/server/src/server_properties.rs @@ -9,8 +9,8 @@ pub struct ServerProperties { pub name: String, #[serde(default = "default_addr")] pub addr: SocketAddr, - #[serde(default = "default_colors")] - pub use_colors: bool, + #[serde(default = "default_logger")] + pub logger: String, #[serde(alias = "$schema", default = "default_schema")] schema: String, } @@ -33,14 +33,14 @@ impl Default for ServerProperties { Self { name: default_name(), addr: default_addr(), - use_colors: default_colors(), + logger: default_logger(), schema: default_schema(), } } } -const fn default_colors() -> bool { - true +fn default_logger() -> String { + "barebones".into() } fn default_addr() -> SocketAddr {