Skip to main content

ftui_runtime/
debug_trace.rs

1#![forbid(unsafe_code)]
2
3//! Zero-cost debug tracing controlled by environment variable.
4//!
5//! Enable runtime debug output by setting `FTUI_DEBUG_TRACE=1` before launching
6//! your application. When disabled (the default), the trace checks compile down
7//! to a single static bool load with no other overhead.
8//!
9//! # Usage
10//!
11//! ```bash
12//! FTUI_DEBUG_TRACE=1 cargo run -p ftui-demo-showcase
13//! ```
14//!
15//! This module provides `debug_trace!` macro for conditional debug output:
16//!
17//! ```ignore
18//! use ftui_runtime::debug_trace;
19//! debug_trace!("loop iteration {}", count);
20//! ```
21
22use std::sync::LazyLock;
23use web_time::Instant;
24
25/// Static flag checked once at startup. After initialization, this is just a bool load.
26static DEBUG_TRACE_ENABLED: LazyLock<bool> = LazyLock::new(|| {
27    std::env::var("FTUI_DEBUG_TRACE")
28        .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
29        .unwrap_or(false)
30});
31
32/// Startup timestamp for relative timing in debug output.
33static START_TIME: LazyLock<Instant> = LazyLock::new(Instant::now);
34
35/// Check if debug tracing is enabled.
36///
37/// This function is inlined and after the first call, compiles down to a single
38/// static bool load - effectively zero cost when disabled.
39#[inline]
40pub fn is_enabled() -> bool {
41    *DEBUG_TRACE_ENABLED
42}
43
44/// Get elapsed time since program start in milliseconds.
45///
46/// Useful for correlating debug output across threads.
47#[inline]
48pub fn elapsed_ms() -> u64 {
49    START_TIME.elapsed().as_millis() as u64
50}
51
52/// Conditionally print debug trace output to stderr.
53///
54/// When `FTUI_DEBUG_TRACE=1` is set, prints timestamped debug messages.
55/// When disabled, compiles to a single bool check (effectively zero cost).
56///
57/// # Example
58///
59/// ```ignore
60/// debug_trace!("subscription started: id={}", sub_id);
61/// debug_trace!("main loop heartbeat: frame={}", frame_count);
62/// ```
63#[macro_export]
64macro_rules! debug_trace {
65    ($($arg:tt)*) => {
66        if $crate::debug_trace::is_enabled() {
67            eprintln!(
68                "[FTUI {:>8}ms] {}",
69                $crate::debug_trace::elapsed_ms(),
70                format_args!($($arg)*)
71            );
72        }
73    };
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_is_enabled_returns_bool() {
82        // Just verify it returns a bool without panicking
83        let _ = is_enabled();
84    }
85
86    #[test]
87    fn test_elapsed_ms_increases() {
88        let t1 = elapsed_ms();
89        std::thread::sleep(std::time::Duration::from_millis(10));
90        let t2 = elapsed_ms();
91        assert!(t2 >= t1);
92    }
93}