use std::path::{Path, PathBuf};
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::prelude::*;
pub fn init_logging(log_dir: &Path) -> Option<tracing_appender::non_blocking::WorkerGuard> {
if let Err(e) = kimun_core::system::create_dir(log_dir) {
eprintln!("kimun: could not create log directory: {e}");
return None;
}
let log_path = log_dir.join("kimun.log");
let file = match std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&log_path)
{
Ok(f) => f,
Err(e) => {
eprintln!("kimun: could not open log file: {e}");
return None;
}
};
let (writer, guard) = tracing_appender::non_blocking(file);
#[cfg(debug_assertions)]
let file_level_filter = LevelFilter::DEBUG;
#[cfg(not(debug_assertions))]
let file_level_filter = LevelFilter::WARN;
let file_layer = tracing_subscriber::fmt::layer()
.compact()
.with_ansi(false)
.with_writer(writer)
.with_filter(file_level_filter);
let _ = tracing_subscriber::registry().with(file_layer).try_init();
tracing_log::LogTracer::init().ok();
Some(guard)
}
pub fn install_panic_hook(log_path: PathBuf) {
let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if std::thread::current().name() == Some("main") {
super::terminal::leave_if_live(&mut std::io::stderr());
}
tracing::error!("panic: {info}");
let parent = log_path.parent().unwrap_or(Path::new("."));
let _ = kimun_core::system::create_dir(parent);
match std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&log_path)
{
Ok(mut file) => {
use std::io::Write;
let _ = writeln!(file, "[PANIC] {info}");
let bt = std::backtrace::Backtrace::force_capture();
let _ = writeln!(file, "{bt}");
}
Err(e) => {
eprintln!("kimun: could not write panic to log: {e}");
}
}
default_hook(info);
}));
}
#[cfg(test)]
mod tests {
use super::init_logging;
#[test]
fn init_logging_returns_none_on_bad_path() {
let tmp = tempfile::TempDir::new().unwrap();
let not_a_dir = tmp.path().join("regular-file");
std::fs::write(¬_a_dir, b"not a directory").unwrap();
let result = init_logging(¬_a_dir.join("logs"));
assert!(result.is_none());
}
}