Implement panic logging for better debugging
build-linux / build-linux-x64 (push) Successful in 5m24s
build-macos / build-macos-x64 (push) Successful in 9m17s
build-windows / build-windows-x64 (push) Successful in 10m48s

This commit is contained in:
2026-05-06 21:21:01 +02:00
parent 7eb253b0dd
commit 359e09f7cd
2 changed files with 65 additions and 0 deletions
+64
View File
@@ -122,6 +122,7 @@ impl Drop for SimpleCallOnReturn {
}
pub fn global_init() -> bool {
install_panic_hook();
#[cfg(target_os = "linux")]
{
if !crate::platform::linux::is_x11() {
@@ -131,6 +132,69 @@ pub fn global_init() -> bool {
true
}
/// Capture Rust panics so they survive the windows-subsystem build.
///
/// In release we have `panic = 'abort'` and `windows_subsystem = "windows"`,
/// which means a panic prints to stderr (which is null when no console is
/// attached) and immediately calls `abort()`. The flexi_logger file never
/// sees the panic, and on Windows the abort doesn't reliably surface in the
/// Application event log either, so the process appears to vanish.
///
/// This hook routes every panic to two places before the abort:
/// 1. `log::error!` — works once `init_log` has set up flexi_logger.
/// 2. `Config::log_path()/panic.log` — fallback for panics that fire before
/// `init_log` runs (e.g. inside `load_custom_client` or `bootstrap`).
/// The log dir is per-user/per-app on every platform and is writable by
/// the user-session `--server` process; the install dir is not.
fn install_panic_hook() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let payload: &str = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
s
} else if let Some(s) = info.payload().downcast_ref::<String>() {
s.as_str()
} else {
"<non-string panic payload>"
};
let location = info
.location()
.map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column()))
.unwrap_or_else(|| "<unknown>".into());
let backtrace = std::backtrace::Backtrace::force_capture();
let exe = std::env::current_exe()
.ok()
.and_then(|p| p.to_str().map(|s| s.to_owned()))
.unwrap_or_default();
let pid = std::process::id();
let args: Vec<String> = std::env::args().collect();
let header = format!(
"PANIC pid={pid} exe={exe} args={args:?}\n at {location}\n payload: {payload}"
);
log::error!("{header}\n backtrace:\n{backtrace}");
let mut path = config::Config::log_path();
let _ = std::fs::create_dir_all(&path);
path.push("panic.log");
if let Ok(mut f) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&path)
{
use std::io::Write;
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let _ = writeln!(f, "[unix={ts}] {header}\n backtrace:\n{backtrace}\n");
let _ = f.flush();
}
prev(info);
}));
});
}
pub fn global_clean() {}
#[inline]
+1
View File
@@ -5,6 +5,7 @@ fn main() {}
#[cfg(target_os = "macos")]
fn main() {
crate::common::global_init();
crate::common::load_custom_client();
hbb_common::init_log(false, "service");
crate::start_os_service();