// Diagnostic file-trace shared with `atomcode_tuix::trace`. Enabled via
// `ATOMCODE_TUIX_LOG=/path/to/file`. Opens in append mode so both crates
// can target the same file without one truncating the other's entries.
//
// Format matches the tuix trace: `+{us} [{CAT}] {tid} {message}`. Use
// short 2-4 char categories so columns stay grep-able:
// AGT — agent loop (Cancel intake)
// RNR — turn runner (tool select / cancel branches)
// TOOL — tool implementations (web_search etc.)
//
// Opt-in only — `enabled()` is a single atomic load + branch when the
// env var is unset, so leaving trace points scattered through hot paths
// costs nothing in release.
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::sync::{Mutex, OnceLock};
use std::time::Instant;
static SINK: OnceLock<Option<Mutex<File>>> = OnceLock::new();
static ORIGIN: OnceLock<Instant> = OnceLock::new();
pub fn enabled() -> bool {
sink().is_some()
}
fn sink() -> Option<&'static Mutex<File>> {
SINK.get_or_init(|| {
let path = std::env::var("ATOMCODE_TUIX_LOG").ok()?;
if path.is_empty() {
return None;
}
// Append (not truncate) — atomcode_tuix::trace opens with
// truncate during reader init; we'd otherwise lose its early
// events if init order flipped. POSIX O_APPEND keeps writes
// atomic for our line-sized payloads, so interleaving with
// the tuix sink is safe even though they own separate Mutex
// instances.
let file = OpenOptions::new()
.create(true)
.append(true)
.open(&path)
.ok()?;
Some(Mutex::new(file))
})
.as_ref()
}
fn origin() -> Instant {
*ORIGIN.get_or_init(Instant::now)
}
pub fn write_line(cat: &str, args: std::fmt::Arguments<'_>) {
let Some(sink) = sink() else {
return;
};
let us = origin().elapsed().as_micros();
let tid = std::thread::current().name().unwrap_or("?").to_string();
let line = format!("+{:>10}us [{:>3}] {:>14} {}\n", us, cat, tid, args);
if let Ok(mut f) = sink.lock() {
let _ = f.write_all(line.as_bytes());
}
}
#[macro_export]
macro_rules! ctrace {
($cat:expr, $($arg:tt)*) => {{
if $crate::trace::enabled() {
$crate::trace::write_line($cat, format_args!($($arg)*));
}
}};
}