Skip to main content

claude_code_statusline_core/
debug.rs

1//! Debug logging utilities
2//!
3//! This module provides debug logging functionality for development
4//! and troubleshooting. Debug output can be enabled via configuration
5//! or environment variable.
6
7use std::fs::OpenOptions;
8use std::io::Write;
9use std::path::{Path, PathBuf};
10
11/// Debug logger for development and troubleshooting
12///
13/// Writes debug information to a temporary log file when enabled.
14/// Can be activated through configuration (`debug = true`) or
15/// environment variable (`CCS_DEBUG=1`).
16///
17/// # Log Location
18///
19/// - Unix/Linux: `/tmp/claude-code-statusline.log`
20/// - Windows: `%TEMP%\claude-code-statusline.log`
21/// - macOS: `/var/folders/.../claude-code-statusline.log`
22pub struct DebugLogger {
23    enabled: bool,
24    log_file: PathBuf,
25}
26
27impl DebugLogger {
28    /// Creates a new DebugLogger instance
29    ///
30    /// # Arguments
31    ///
32    /// * `enabled` - Whether debug logging is enabled in configuration
33    ///
34    /// # Environment Variables
35    ///
36    /// - `CCS_DEBUG=1` - Forces debug logging regardless of config
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use claude_code_statusline_core::debug::DebugLogger;
42    ///
43    /// let logger = DebugLogger::new(true);
44    /// logger.log("Debug message");
45    /// ```
46    pub fn new(enabled: bool) -> Self {
47        // Check environment variable as well
48        let enabled = enabled || std::env::var("CCS_DEBUG").unwrap_or_default() == "1";
49
50        // Use cross-platform temp directory
51        let log_file = std::env::temp_dir().join("claude-code-statusline.log");
52
53        Self { enabled, log_file }
54    }
55
56    /// Log a message if debug mode is enabled
57    pub fn log(&self, message: &str) {
58        if !self.enabled {
59            // Still forward to tracing at debug level for centralized logging
60            tracing::debug!(target: "claude-code-statusline", "{message}");
61            return;
62        }
63
64        // Create parent directory if it doesn't exist
65        if let Some(parent) = Path::new(&self.log_file).parent() {
66            std::fs::create_dir_all(parent).ok();
67        }
68
69        if let Ok(mut file) = OpenOptions::new()
70            .create(true)
71            .append(true)
72            .open(&self.log_file)
73        {
74            writeln!(file, "{message}").ok();
75        }
76    }
77
78    /// Log to stderr if debug mode is enabled
79    pub fn log_stderr(&self, message: &str) {
80        // Emit via tracing; subscriber decides output
81        tracing::debug!(target: "claude-code-statusline", "{message}");
82        if self.enabled {
83            eprintln!("[DEBUG] {message}");
84        }
85    }
86
87    /// Log a new execution marker
88    pub fn log_execution_start(&self) {
89        self.log("--- New execution ---");
90    }
91
92    /// Log configuration information
93    pub fn log_config(&self, debug: bool, command_timeout: u64) {
94        self.log(&format!(
95            "Config loaded: debug={debug}, command_timeout={command_timeout}"
96        ));
97    }
98
99    /// Log input information
100    pub fn log_input(&self, buffer: &str) {
101        self.log(&format!("Input length: {} bytes", buffer.len()));
102        if !buffer.is_empty() {
103            self.log(&format!(
104                "First 500 chars: {}",
105                &buffer[..buffer.len().min(500)]
106            ));
107        }
108    }
109
110    /// Log successful parse
111    pub fn log_success(&self, model: &str, cwd: &str) {
112        self.log(&format!("SUCCESS: Model={model}, CWD={cwd}"));
113    }
114
115    /// Log generated prompt
116    pub fn log_prompt(&self, prompt: &str) {
117        self.log(&format!("Generated: {prompt}"));
118    }
119
120    /// Log error
121    pub fn log_error(&self, error: &str) {
122        tracing::error!(target: "claude-code-statusline", "{error}");
123        self.log(&format!("ERROR: {error}"));
124    }
125
126    /// Check if debug mode is enabled
127    #[allow(dead_code)]
128    pub fn is_enabled(&self) -> bool {
129        self.enabled
130    }
131}