20260619
This commit is contained in:
155
src/debug_log.rs
Normal file
155
src/debug_log.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
const LOG_NAME: &str = "meatshell-debug.log";
|
||||
|
||||
pub fn init() -> Option<PathBuf> {
|
||||
let (file, path) = match open_log_file() {
|
||||
Some(pair) => pair,
|
||||
None => {
|
||||
install_panic_hook(None);
|
||||
init_stderr_tracing();
|
||||
tracing::warn!("failed to create {LOG_NAME}; logging to stderr only");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
install_panic_hook(Some(path.clone()));
|
||||
|
||||
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info,meatshell=debug,winit=error"));
|
||||
let log_file = file;
|
||||
if tracing_subscriber::fmt()
|
||||
.with_env_filter(filter)
|
||||
.with_ansi(false)
|
||||
.with_thread_ids(true)
|
||||
.with_writer(move || log_file.try_clone().expect("clone debug log file"))
|
||||
.try_init()
|
||||
.is_err()
|
||||
{
|
||||
return Some(path);
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
os = std::env::consts::OS,
|
||||
arch = std::env::consts::ARCH,
|
||||
"debug logging initialized"
|
||||
);
|
||||
|
||||
if log_paths_enabled() {
|
||||
tracing::debug!(log = %path.display(), "debug log path");
|
||||
if let Ok(cwd) = std::env::current_dir() {
|
||||
tracing::debug!(cwd = %cwd.display(), "current working directory");
|
||||
}
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
tracing::debug!(exe = %exe.display(), "current executable");
|
||||
}
|
||||
}
|
||||
|
||||
Some(path)
|
||||
}
|
||||
|
||||
fn init_stderr_tracing() {
|
||||
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info,meatshell=debug,winit=error"));
|
||||
let _ = tracing_subscriber::fmt()
|
||||
.with_env_filter(filter)
|
||||
.with_ansi(false)
|
||||
.with_thread_ids(true)
|
||||
.try_init();
|
||||
}
|
||||
|
||||
fn open_log_file() -> Option<(File, PathBuf)> {
|
||||
let mut dirs = Vec::new();
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(dir) = exe.parent() {
|
||||
dirs.push(dir.to_path_buf());
|
||||
}
|
||||
}
|
||||
if let Ok(cwd) = std::env::current_dir() {
|
||||
if !dirs.iter().any(|dir| dir == &cwd) {
|
||||
dirs.push(cwd);
|
||||
}
|
||||
}
|
||||
|
||||
for dir in dirs {
|
||||
let path = dir.join(LOG_NAME);
|
||||
if let Ok(file) = OpenOptions::new().create(true).append(true).open(&path) {
|
||||
return Some((file, path));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn install_panic_hook(path: Option<PathBuf>) {
|
||||
let previous = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
let payload = if let Some(message) = info.payload().downcast_ref::<&str>() {
|
||||
*message
|
||||
} else if let Some(message) = info.payload().downcast_ref::<String>() {
|
||||
message.as_str()
|
||||
} else {
|
||||
"<non-string panic payload>"
|
||||
};
|
||||
let location = info
|
||||
.location()
|
||||
.map(|loc| format!("{}:{}:{}", short_file(loc.file()), loc.line(), loc.column()))
|
||||
.unwrap_or_else(|| "<unknown>".to_string());
|
||||
let backtrace = if env_flag("MEATSHELL_DEBUG_BACKTRACE") {
|
||||
Some(std::backtrace::Backtrace::force_capture().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(path) = &path {
|
||||
if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(path) {
|
||||
let _ = writeln!(
|
||||
file,
|
||||
"\n===== panic {:?} =====\nlocation: {}\nmessage: {}",
|
||||
SystemTime::now(),
|
||||
location,
|
||||
payload
|
||||
);
|
||||
if let Some(backtrace) = &backtrace {
|
||||
let _ = writeln!(file, "backtrace:\n{}", backtrace);
|
||||
}
|
||||
let _ = writeln!(file);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(backtrace) = &backtrace {
|
||||
tracing::error!(
|
||||
location = %location,
|
||||
message = %payload,
|
||||
backtrace = %backtrace,
|
||||
"panic captured"
|
||||
);
|
||||
} else {
|
||||
tracing::error!(
|
||||
location = %location,
|
||||
message = %payload,
|
||||
"panic captured"
|
||||
);
|
||||
}
|
||||
previous(info);
|
||||
}));
|
||||
}
|
||||
|
||||
pub(crate) fn log_paths_enabled() -> bool {
|
||||
env_flag("MEATSHELL_DEBUG_PATHS")
|
||||
}
|
||||
|
||||
fn env_flag(name: &str) -> bool {
|
||||
matches!(
|
||||
std::env::var(name).as_deref(),
|
||||
Ok("1") | Ok("true") | Ok("TRUE") | Ok("yes") | Ok("YES") | Ok("on") | Ok("ON")
|
||||
)
|
||||
}
|
||||
|
||||
fn short_file(path: &str) -> &str {
|
||||
path.rsplit(['/', '\\']).next().unwrap_or(path)
|
||||
}
|
||||
Reference in New Issue
Block a user