use std::fs::{File, OpenOptions}; use std::io::Write; use std::path::Path; use std::sync::Mutex; use std::time::{SystemTime, UNIX_EPOCH}; /// Optional diagnostic trace, one timestamped line per event, for diagnosing /// where a transfer stalls (e.g. which side is still waiting, and on what). /// `Logger::none()` is a no-op so call sites don't need to branch on whether /// `--log` was given. pub struct Logger(Option>); impl Logger { pub fn none() -> Logger { Logger(None) } /// Truncates any existing file at `path` -- each run starts a fresh /// log, so a re-run never leaves stale lines from a previous attempt /// mixed in with (or mistaken for) the current one. pub fn open(path: &Path) -> std::io::Result { let f = OpenOptions::new().create(true).write(true).truncate(true).open(path)?; Ok(Logger(Some(Mutex::new(f)))) } pub fn log(&self, args: std::fmt::Arguments) { let Some(m) = &self.0 else { return }; let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default(); if let Ok(mut f) = m.lock() { let _ = writeln!(f, "[{:.6}] {}", now.as_secs_f64(), args); } } }