This commit is contained in:
2026-06-19 03:09:53 +08:00
Unverified
commit d32882443f
49 changed files with 19399 additions and 0 deletions

155
src/debug_log.rs Normal file
View 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)
}