use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
fn log_path() -> Option<PathBuf> {
if let Ok(p) = std::env::var("ZPWRCHROME_DL_CACHE_DIR") {
let path = PathBuf::from(p);
let _ = fs::create_dir_all(&path);
return Some(path.join("host.log"));
}
let base = std::env::var("XDG_CACHE_HOME")
.ok()
.or_else(|| std::env::var("HOME").ok().map(|h| format!("{h}/.cache")))?;
let dir = PathBuf::from(base).join("zpwrchrome");
let _ = fs::create_dir_all(&dir);
Some(dir.join("host.log"))
}
fn iso_ts() -> String {
let d = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default();
let total_secs = d.as_secs() as i64;
let ms = d.subsec_millis();
#[cfg(unix)]
unsafe {
let mut tm: libc::tm = std::mem::zeroed();
let t: libc::time_t = total_secs as libc::time_t;
libc::gmtime_r(&t, &mut tm);
return format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ms
);
}
#[cfg(not(unix))]
format!("ts={total_secs}.{ms:03}")
}
pub fn log(line: &str) {
let Some(path) = log_path() else { return };
let pid = std::process::id();
let Ok(mut f) = OpenOptions::new().append(true).create(true).open(&path) else { return };
let _ = writeln!(f, "{} pid={} {}", iso_ts(), pid, line);
}
pub fn install_panic_hook() {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let msg = info.payload().downcast_ref::<&'static str>().copied()
.or_else(|| info.payload().downcast_ref::<String>().map(|s| s.as_str()))
.unwrap_or("(no payload)");
let loc = info.location()
.map(|l| format!("{}:{}", l.file(), l.line()))
.unwrap_or_else(|| "(no location)".into());
log(&format!("PANIC at={loc} msg={msg:?}"));
prev(info);
}));
}
pub fn log_start(args: &[String]) {
let cwd = std::env::current_dir()
.map(|p| p.display().to_string())
.unwrap_or_else(|_| "?".into());
let home = std::env::var("HOME").unwrap_or_else(|_| "(unset)".into());
let xdg = std::env::var("XDG_CACHE_HOME").unwrap_or_else(|_| "(unset)".into());
let path = std::env::var("PATH").unwrap_or_default();
let path_short = if path.len() > 200 { format!("{}…", &path[..200]) } else { path };
log(&format!(
"START args={:?} cwd={} HOME={} XDG_CACHE_HOME={} PATH={}",
args, cwd, home, xdg, path_short
));
}
pub fn log_exit(code: i32, reason: &str) {
log(&format!("EXIT code={} reason={}", code, reason));
}