Skip to main content

command_stream/
trace.rs

1//! Trace/logging utilities for command-stream
2//!
3//! This module provides verbose logging functionality that can be controlled
4//! via environment variables for debugging and development purposes.
5
6use std::env;
7
8/// Check if tracing is enabled via environment variables
9///
10/// Tracing can be controlled via:
11/// - COMMAND_STREAM_TRACE=true/false (explicit control)
12/// - COMMAND_STREAM_VERBOSE=true (enables tracing unless TRACE=false)
13pub fn is_trace_enabled() -> bool {
14    let trace_env = env::var("COMMAND_STREAM_TRACE").ok();
15    let verbose_env = env::var("COMMAND_STREAM_VERBOSE")
16        .map(|v| v == "true")
17        .unwrap_or(false);
18
19    match trace_env.as_deref() {
20        Some("false") => false,
21        Some("true") => true,
22        _ => verbose_env,
23    }
24}
25
26/// Trace function for verbose logging
27///
28/// Outputs trace messages to stderr when tracing is enabled.
29/// Messages are prefixed with timestamp and category.
30///
31/// # Examples
32///
33/// ```
34/// use command_stream::trace::trace;
35///
36/// trace("ProcessRunner", "Starting command execution");
37/// ```
38pub fn trace(category: &str, message: &str) {
39    if !is_trace_enabled() {
40        return;
41    }
42
43    let timestamp = chrono::Utc::now().to_rfc3339();
44    eprintln!("[TRACE {}] [{}] {}", timestamp, category, message);
45}
46
47/// Trace function with lazy message evaluation
48///
49/// Only evaluates the message function if tracing is enabled.
50/// This is useful for expensive message formatting that should
51/// be avoided when tracing is disabled.
52///
53/// # Examples
54///
55/// ```
56/// use command_stream::trace::trace_lazy;
57///
58/// trace_lazy("ProcessRunner", || {
59///     format!("Expensive computation result: {}", 42)
60/// });
61/// ```
62pub fn trace_lazy<F>(category: &str, message_fn: F)
63where
64    F: FnOnce() -> String,
65{
66    if !is_trace_enabled() {
67        return;
68    }
69
70    trace(category, &message_fn());
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use std::env;
77    use std::sync::Mutex;
78
79    // Use a mutex to serialize tests that modify environment variables
80    // This prevents race conditions when tests run in parallel
81    static ENV_MUTEX: Mutex<()> = Mutex::new(());
82
83    /// Helper to save and restore environment variables during tests
84    struct EnvGuard {
85        trace_value: Option<String>,
86        verbose_value: Option<String>,
87    }
88
89    impl EnvGuard {
90        fn new() -> Self {
91            EnvGuard {
92                trace_value: env::var("COMMAND_STREAM_TRACE").ok(),
93                verbose_value: env::var("COMMAND_STREAM_VERBOSE").ok(),
94            }
95        }
96    }
97
98    impl Drop for EnvGuard {
99        fn drop(&mut self) {
100            // Restore original values
101            match &self.trace_value {
102                Some(v) => env::set_var("COMMAND_STREAM_TRACE", v),
103                None => env::remove_var("COMMAND_STREAM_TRACE"),
104            }
105            match &self.verbose_value {
106                Some(v) => env::set_var("COMMAND_STREAM_VERBOSE", v),
107                None => env::remove_var("COMMAND_STREAM_VERBOSE"),
108            }
109        }
110    }
111
112    #[test]
113    fn test_trace_disabled_by_default() {
114        let _lock = ENV_MUTEX.lock().unwrap();
115        let _guard = EnvGuard::new();
116
117        // Clear env vars to test default behavior
118        env::remove_var("COMMAND_STREAM_TRACE");
119        env::remove_var("COMMAND_STREAM_VERBOSE");
120        assert!(!is_trace_enabled());
121    }
122
123    #[test]
124    fn test_trace_enabled_by_verbose() {
125        let _lock = ENV_MUTEX.lock().unwrap();
126        let _guard = EnvGuard::new();
127
128        env::remove_var("COMMAND_STREAM_TRACE");
129        env::set_var("COMMAND_STREAM_VERBOSE", "true");
130        assert!(is_trace_enabled());
131    }
132
133    #[test]
134    fn test_trace_explicit_true() {
135        let _lock = ENV_MUTEX.lock().unwrap();
136        let _guard = EnvGuard::new();
137
138        env::remove_var("COMMAND_STREAM_VERBOSE");
139        env::set_var("COMMAND_STREAM_TRACE", "true");
140        assert!(is_trace_enabled());
141    }
142
143    #[test]
144    fn test_trace_explicit_false_overrides_verbose() {
145        let _lock = ENV_MUTEX.lock().unwrap();
146        let _guard = EnvGuard::new();
147
148        env::set_var("COMMAND_STREAM_TRACE", "false");
149        env::set_var("COMMAND_STREAM_VERBOSE", "true");
150        assert!(!is_trace_enabled());
151    }
152}