Skip to main content

reference_query/
trace.rs

1//! Opt-in diagnostics. `rq -v` (or `RQ_LOG` set in the environment, so an
2//! already-installed binary can be debugged without a rebuild) turns on stderr
3//! trace lines describing what each search and index pass decided — the resolved
4//! root and coverage, what got warmed, written, or reconciled away, and how long
5//! the search took. Off by default; when off it's a single relaxed atomic load,
6//! so it costs nothing on the hot path. Trace goes to stderr, never stdout, so
7//! results stay machine-parseable.
8
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::time::Instant;
11
12static ENABLED: AtomicBool = AtomicBool::new(false);
13
14/// Enable tracing from the `-v` flag; `RQ_LOG` in the environment also enables
15/// it, so a shipped binary can be debugged in place.
16pub(crate) fn enable_from(flag: bool) {
17    let on = flag || std::env::var_os("RQ_LOG").is_some();
18    ENABLED.store(on, Ordering::Relaxed);
19}
20
21/// Whether trace output is on.
22pub(crate) fn enabled() -> bool {
23    ENABLED.load(Ordering::Relaxed)
24}
25
26/// A path for display in trace lines, with the home directory shown as `~`.
27pub(crate) fn abbrev(path: &std::path::Path) -> String {
28    let s = path.display().to_string();
29    match std::env::var_os("HOME") {
30        Some(home) if !home.is_empty() => match s.strip_prefix(&*home.to_string_lossy()) {
31            Some(rest) => format!("~{rest}"),
32            None => s,
33        },
34        _ => s,
35    }
36}
37
38/// Emit a trace line to stderr (prefixed `rq:`), only when enabled.
39#[macro_export]
40macro_rules! trace {
41    ($($arg:tt)*) => {
42        if $crate::trace::enabled() {
43            eprintln!("rq: {}", format_args!($($arg)*));
44        }
45    };
46}
47
48/// Times a phase and logs `<label> (N ms)` when dropped — but only if tracing
49/// was on at construction, so it's free otherwise.
50pub(crate) struct Timer {
51    label: &'static str,
52    start: Option<Instant>,
53}
54
55impl Timer {
56    pub(crate) fn start(label: &'static str) -> Self {
57        Self {
58            label,
59            start: enabled().then(Instant::now),
60        }
61    }
62}
63
64impl Drop for Timer {
65    fn drop(&mut self) {
66        if let Some(start) = self.start {
67            eprintln!("rq: {} ({} ms)", self.label, start.elapsed().as_millis());
68        }
69    }
70}