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}