use std::io::Write;
use std::sync::{LazyLock, Mutex};
use std::sync::atomic::{AtomicU32, Ordering};
fn psmux_dir() -> String {
let home = std::env::var("USERPROFILE")
.or_else(|_| std::env::var("HOME"))
.unwrap_or_default();
format!("{}/.psmux", home)
}
fn open_log(filename: &str) -> Option<std::fs::File> {
let dir = psmux_dir();
let _ = std::fs::create_dir_all(&dir);
std::fs::OpenOptions::new()
.create(true)
.truncate(true) .write(true)
.open(format!("{}/{}", dir, filename))
.ok()
}
fn env_enabled(var: &str) -> bool {
std::env::var(var).map_or(false, |v| v == "1" || v.eq_ignore_ascii_case("true"))
}
static CLIENT_LOG: LazyLock<Mutex<Option<std::fs::File>>> = LazyLock::new(|| {
if !env_enabled("PSMUX_CLIENT_DEBUG") { return Mutex::new(None); }
Mutex::new(open_log("client_debug.log"))
});
static CLIENT_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
const CLIENT_LOG_CAP: u32 = 5000;
pub fn client_log(component: &str, msg: &str) {
let n = CLIENT_LOG_COUNT.fetch_add(1, Ordering::Relaxed);
if n >= CLIENT_LOG_CAP {
if n == CLIENT_LOG_CAP {
if let Ok(mut guard) = CLIENT_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][log] --- log cap reached ({} entries), further logging suppressed ---",
chrono::Local::now().format("%H:%M:%S%.3f"), CLIENT_LOG_CAP);
let _ = f.flush();
}
}
}
return;
}
if let Ok(mut guard) = CLIENT_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][{}] {}",
chrono::Local::now().format("%H:%M:%S%.3f"), component, msg);
let _ = f.flush();
}
}
}
pub fn client_log_enabled() -> bool {
CLIENT_LOG.lock().ok().map_or(false, |g| g.is_some())
}
static STYLE_LOG: LazyLock<Mutex<Option<std::fs::File>>> = LazyLock::new(|| {
if !env_enabled("PSMUX_STYLE_DEBUG") { return Mutex::new(None); }
Mutex::new(open_log("style_debug.log"))
});
static STYLE_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
const STYLE_LOG_CAP: u32 = 2000;
pub fn style_log(component: &str, msg: &str) {
let n = STYLE_LOG_COUNT.fetch_add(1, Ordering::Relaxed);
if n >= STYLE_LOG_CAP {
if n == STYLE_LOG_CAP {
if let Ok(mut guard) = STYLE_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][log] --- log cap reached ---",
chrono::Local::now().format("%H:%M:%S%.3f"));
let _ = f.flush();
}
}
}
return;
}
if let Ok(mut guard) = STYLE_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][{}] {}",
chrono::Local::now().format("%H:%M:%S%.3f"), component, msg);
let _ = f.flush();
}
}
}
pub fn style_log_enabled() -> bool {
STYLE_LOG.lock().ok().map_or(false, |g| g.is_some())
}
static INPUT_LOG: LazyLock<Mutex<Option<std::fs::File>>> = LazyLock::new(|| {
if !env_enabled("PSMUX_INPUT_DEBUG") { return Mutex::new(None); }
Mutex::new(open_log("input_debug.log"))
});
static INPUT_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
const INPUT_LOG_CAP: u32 = 10000;
pub fn input_log(component: &str, msg: &str) {
let n = INPUT_LOG_COUNT.fetch_add(1, Ordering::Relaxed);
if n >= INPUT_LOG_CAP {
if n == INPUT_LOG_CAP {
if let Ok(mut guard) = INPUT_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][log] --- log cap reached ---",
chrono::Local::now().format("%H:%M:%S%.3f"));
let _ = f.flush();
}
}
}
return;
}
if let Ok(mut guard) = INPUT_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][{}] {}",
chrono::Local::now().format("%H:%M:%S%.3f"), component, msg);
let _ = f.flush();
}
}
}
pub fn input_log_enabled() -> bool {
INPUT_LOG.lock().ok().map_or(false, |g| g.is_some())
}
static SERVER_LOG: LazyLock<Mutex<Option<std::fs::File>>> = LazyLock::new(|| {
if !env_enabled("PSMUX_SERVER_DEBUG") { return Mutex::new(None); }
Mutex::new(open_log("server_debug.log"))
});
static SERVER_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
const SERVER_LOG_CAP: u32 = 10000;
pub fn server_log(component: &str, msg: &str) {
let n = SERVER_LOG_COUNT.fetch_add(1, Ordering::Relaxed);
if n >= SERVER_LOG_CAP {
if n == SERVER_LOG_CAP {
if let Ok(mut guard) = SERVER_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][log] --- log cap reached ---",
chrono::Local::now().format("%H:%M:%S%.3f"));
let _ = f.flush();
}
}
}
return;
}
if let Ok(mut guard) = SERVER_LOG.lock() {
if let Some(ref mut f) = *guard {
let _ = writeln!(f, "[{}][{}] {}",
chrono::Local::now().format("%H:%M:%S%.3f"), component, msg);
let _ = f.flush();
}
}
}
pub fn server_log_enabled() -> bool {
SERVER_LOG.lock().ok().map_or(false, |g| g.is_some())
}