feat: push logs to a dedicated buffer

This commit is contained in:
Chance 2025-04-12 21:03:31 -04:00 committed by BitSyndicate
parent 6ce3e627cc
commit f9c2b93c01
Signed by: bitsyndicate
GPG key ID: 443E4198D6BBA6DE
3 changed files with 2426 additions and 2 deletions

2177
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -25,4 +25,12 @@ split-debuginfo = "off"
[profile.dev]
debug = 0
[dependencies]
terminator = "0.3.2"
thiserror = "2.0.12"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
vulkano = "0.35.1"
winit = "0.30.9"

View file

@ -1,6 +1,245 @@
use std::{
fs::OpenOptions, io::{self, BufWriter, Stdout, Write}, path::{Path, PathBuf}, str::FromStr, sync::{mpsc, Arc, RwLock}
};
use tracing::{error, info};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::{Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt};
use winit::event_loop::EventLoop;
#[derive(Debug, Default)]
struct App;
fn main() {
println!("Happy Halloween!")
struct BufferWriter {
buffer: Arc<RwLock<Vec<u8>>>,
}
impl Write for BufferWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if let Ok(mut guard) = self.buffer.write() {
guard.extend_from_slice(buf);
Ok(buf.len())
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Failed to acquire write lock on log buffer",
))
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
enum LogLevel {
Error,
#[default]
Info,
Debug,
Trace,
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{self:?}")
}
}
impl FromStr for LogLevel {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"trace" => Ok(Self::Trace),
"debug" => Ok(Self::Debug),
"info" => Ok(Self::Info),
"error" => Ok(Self::Error),
_ => Err(()),
}
}
}
#[derive(Debug)]
pub struct LoggerConfig {
pub log_level: LogLevel,
pub log_to_file: bool,
pub log_file_path: PathBuf,
pub log_to_stdout: bool,
pub stdout_color: bool,
pub log_to_buffer: bool,
}
impl LoggerConfig {
pub fn level(mut self,level: LogLevel) -> Self {
self.log_level = level;
self
}
pub fn log_to_file(mut self,log_to_file: bool ) -> Self {
self.log_to_file = log_to_file;
self
}
pub fn colored_stdout(mut self,show_color: bool ) -> Self {
self.stdout_color = show_color;
self
}
pub fn log_to_stdout(mut self,stdout: bool ) -> Self {
self.log_to_stdout = stdout;
self
}
pub fn log_path<P: AsRef<Path>>(mut self,path: P ) -> Self {
self.log_file_path = path.as_ref().to_path_buf();
self
}
}
impl Default for LoggerConfig {
fn default() -> Self {
Self {
log_level: LogLevel::Debug,
log_to_file: true,
log_file_path: "zenyx.log".into(),
log_to_stdout: true,
stdout_color: true,
log_to_buffer: true,
}
}
}
pub struct Logger {
config: LoggerConfig,
log_buffer: Arc<RwLock<Vec<u8>>>,
}
enum Log {
All,
From(std::time::SystemTime),
}
impl Logger {
pub fn new(config: LoggerConfig) -> Self {
Self {
config,
log_buffer: Arc::new(RwLock::new(Vec::new())),
}
}
pub fn init(&self) {
let env_filter =
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
format!("{}={}", env!("CARGO_CRATE_NAME"), self.config.log_level).into()
});
let mut layers = Vec::new();
if self.config.log_to_stdout {
let fmt_layer_stdout = tracing_subscriber::fmt::layer()
.with_level(true)
.compact()
.with_ansi(self.config.stdout_color)
.log_internal_errors(false)
.without_time()
.with_thread_names(true)
.with_span_events(FmtSpan::CLOSE)
.with_writer(std::io::stdout)
.boxed();
layers.push(fmt_layer_stdout);
}
if self.config.log_to_file {
let log_file_path = self.config.log_file_path.clone();
let fmt_layer_file = tracing_subscriber::fmt::layer()
.with_level(true)
.compact()
.with_ansi(false)
.log_internal_errors(false)
.without_time()
.with_thread_names(true)
.with_span_events(FmtSpan::CLOSE)
.with_writer(move || {
let file = OpenOptions::new()
.append(true)
.open(&log_file_path)
.unwrap_or_else(|_| {
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&log_file_path)
.expect("Failed to create log file")
});
BufWriter::new(file)
})
.boxed();
layers.push(fmt_layer_file);
}
// let (tx,rx) = mpsc::channel();
// std::thread::spawn(move || {
// for log in rx.iter() {
// println!("{log:#?}")
// }
// });
if self.config.log_to_buffer {
let log_buffer = Arc::clone(&self.log_buffer);
let fmt_layer_buffer = tracing_subscriber::fmt::layer()
.with_level(true)
.compact()
.with_ansi(false)
.log_internal_errors(false)
.without_time()
.with_thread_names(true)
.with_span_events(FmtSpan::CLOSE)
.with_writer(move || BufferWriter {
buffer: log_buffer.clone(),
})
.boxed();
layers.push(fmt_layer_buffer);
}
if !layers.is_empty() {
let subscriber = Registry::default().with(env_filter).with(layers);
subscriber.init();
}
}
pub fn get_logs(&self) -> Option<String> {
if !self.config.log_to_buffer {
return None;
}
self.log_buffer
.read()
.ok()
.map(|guard| String::from_utf8_lossy(&guard).into_owned())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let logger_config = LoggerConfig::default()
.colored_stdout(true)
.level(LogLevel::Debug)
.log_to_file(true)
.log_path(format!("{}.log",env!("CARGO_PKG_NAME")));
let logger = Logger::new(logger_config);
logger.init();
let event_loop = EventLoop::new()?;
info!("Application initialized");
let app = App::default();
info!("This is a test log message");
error!("Another log message with value: {}", 42);
if let Some(logs) = logger.get_logs() {
println!("\n--- Logs from Buffer ---");
println!("{}", logs);
println!("------------------------");
} else {
eprintln!("Failed to read logs from buffer or buffer logging is disabled");
}
Ok(())
}