@@ -50,6 +50,8 @@ use tracing_subscriber::{EnvFilter, fmt};
5050///
5151/// Prefers `KEYLESS_LOG` over `RUST_LOG`, defaulting to `"info"`.
5252fn env_log_level ( ) -> String {
53+ // Prefer KEYLESS_LOG (app-specific), fallback to RUST_LOG (standard), then default to "info".
54+ // or_else chains fallthrough; only calls closure if previous var() returned Err.
5355 std:: env:: var ( "KEYLESS_LOG" )
5456 . or_else ( |_| std:: env:: var ( "RUST_LOG" ) )
5557 . unwrap_or_else ( |_| "info" . to_string ( ) )
@@ -63,6 +65,9 @@ fn env_log_level() -> String {
6365pub fn init_stdout ( ) {
6466 let env_level = env_log_level ( ) ;
6567
68+ // Ignore init errors (try_init returns Err if subscriber already set; idempotent).
69+ // with_target(false) hides crate names for cleaner output (level/time shown instead).
70+ // uptime() shows time since app start (better than absolute time for short runs).
6671 let _ = tracing_subscriber:: fmt ( )
6772 . with_env_filter ( EnvFilter :: new ( env_level) )
6873 . with_target ( false )
@@ -77,6 +82,9 @@ pub fn init_stdout() {
7782pub fn init_json ( ) {
7883 let env_level = env_log_level ( ) ;
7984
85+ // Ignore init errors (try_init returns Err if subscriber already set; idempotent).
86+ // with_target(true) includes crate names (useful for programmatic filtering).
87+ // flatten_event, with_current_span, with_span_list provide full context in JSON.
8088 let _ = tracing_subscriber:: fmt ( )
8189 . with_env_filter ( EnvFilter :: new ( env_level) )
8290 . with_target ( true )
@@ -96,15 +104,20 @@ struct ChannelWriter {
96104
97105impl Write for ChannelWriter {
98106 fn write ( & mut self , buf : & [ u8 ] ) -> IoResult < usize > {
107+ // Skip empty buffers (tracing may call write with empty slices).
99108 if !buf. is_empty ( ) {
100- // Best-effort UTF-8 conversion; lossy is fine for logs
109+ // Best-effort UTF-8 conversion; lossy is fine for logs (replaces invalid bytes with �).
110+ // to_string() clones the String (needed for send; lossy returns Cow<str>).
101111 let s = String :: from_utf8_lossy ( buf) . to_string ( ) ;
112+ // Try-send is non-blocking; drops log line if channel full (prevents stalling).
102113 let _ = self . tx . try_send ( s) ;
103114 }
115+ // Always return full buffer length (Write contract: always claim to write all bytes).
104116 Ok ( buf. len ( ) )
105117 }
106118
107119 fn flush ( & mut self ) -> IoResult < ( ) > {
120+ // No-op: channel send is immediate (no buffering to flush).
108121 Ok ( ( ) )
109122 }
110123}
@@ -120,6 +133,8 @@ impl<'a> MakeWriter<'a> for ChannelWriterFactory {
120133 type Writer = ChannelWriter ;
121134
122135 fn make_writer ( & ' a self ) -> Self :: Writer {
136+ // Clone channel sender for each writer instance (tracing may create multiple writers).
137+ // SyncSender is cheap to clone (just increments reference count).
123138 ChannelWriter {
124139 tx : self . tx . clone ( ) ,
125140 }
@@ -128,6 +143,8 @@ impl<'a> MakeWriter<'a> for ChannelWriterFactory {
128143
129144/// Get the log file path in the keyless cache.
130145fn log_file_path ( ) -> std:: path:: PathBuf {
146+ // Use platform-specific cache directory (e.g., ~/.cache/keyless/session.log).
147+ // Fallback to .cache in current directory if cache_dir() unavailable (rare edge case).
131148 if let Some ( base) = dirs_next:: cache_dir ( ) {
132149 base. join ( "keyless" ) . join ( "session.log" )
133150 } else {
@@ -145,14 +162,21 @@ struct FileWriter {
145162
146163impl Write for FileWriter {
147164 fn write ( & mut self , buf : & [ u8 ] ) -> IoResult < usize > {
165+ // Mutex protects concurrent writes from multiple tracing threads.
166+ // Ignore lock errors (continue even if mutex poisoned); better than panicking.
148167 if let Ok ( mut f) = self . file . lock ( ) {
168+ // Ignore write errors (best-effort logging; don't fail on disk full).
149169 let _ = f. write ( buf) ;
150170 }
171+ // Always return full buffer length (Write contract: always claim to write all bytes).
151172 Ok ( buf. len ( ) )
152173 }
153174
154175 fn flush ( & mut self ) -> IoResult < ( ) > {
176+ // Flush file buffer to ensure logs are written to disk (important for crash recovery).
177+ // Ignore lock errors (continue even if mutex poisoned).
155178 if let Ok ( mut f) = self . file . lock ( ) {
179+ // Ignore flush errors (best-effort; disk may be full or filesystem may be read-only).
156180 let _ = f. flush ( ) ;
157181 }
158182 Ok ( ( ) )
@@ -170,21 +194,24 @@ impl<'a> MakeWriter<'a> for FileWriterFactory {
170194 type Writer = FileWriter ;
171195
172196 fn make_writer ( & ' a self ) -> Self :: Writer {
173- // Try to open the configured log file, with platform-aware fallbacks
197+ // Three-tier fallback: primary log file → null device → temp file → panic.
198+ // Ensures logging never silently fails (better to panic than lose all logs).
174199 let file = OpenOptions :: new ( )
175200 . create ( true )
176201 . append ( true )
177202 . open ( & self . file_path )
178203 . or_else ( |_| {
179- // Fallback 1: platform-specific null device (should always exist)
204+ // Fallback 1: platform-specific null device (should always exist).
205+ // Discards logs but prevents panic (system likely broken if null device missing).
180206 #[ cfg( unix) ]
181207 let null_path = "/dev/null" ;
182208 #[ cfg( windows) ]
183209 let null_path = "NUL" ;
184210 OpenOptions :: new ( ) . write ( true ) . open ( null_path)
185211 } )
186212 . or_else ( |_| {
187- // Fallback 2: temp file in current directory
213+ // Fallback 2: temp file in current directory (last resort before panic).
214+ // May fail if current dir is read-only, but better than nothing.
188215 File :: create ( ".keyless.log.tmp" )
189216 } )
190217 . unwrap_or_else ( |e| {
@@ -203,6 +230,7 @@ impl<'a> MakeWriter<'a> for FileWriterFactory {
203230 )
204231 } ) ;
205232
233+ // Wrap file in Mutex for thread-safe concurrent writes (multiple tracing threads).
206234 FileWriter {
207235 file : Mutex :: new ( file) ,
208236 }
@@ -220,14 +248,15 @@ impl<'a> MakeWriter<'a> for FileWriterFactory {
220248///
221249/// Safe to call once at startup. Subsequent calls will no-op if a subscriber exists.
222250pub fn init_channel ( tx : SyncSender < String > ) {
223- // Ensure log directory exists
251+ // Ensure log directory exists (e.g., ~/.cache/keyless/).
252+ // Ignore creation errors (best-effort; may fail due to permissions).
224253 let log_path = log_file_path ( ) ;
225254 if let Some ( dir) = log_path. parent ( ) {
226255 let _ = std:: fs:: create_dir_all ( dir) ;
227256 }
228257
229- // Shared filter: quiet dependencies, verbose for keyless crates
230- // This applies to both TUI and file to keep them consistent
258+ // Shared filter: quiet dependencies (info+) , verbose for keyless crates (debug+).
259+ // This applies to both TUI and file to keep them consistent (same log level).
231260 let log_filter = EnvFilter :: new (
232261 "info,\
233262 keyless=debug,\
@@ -239,27 +268,32 @@ pub fn init_channel(tx: SyncSender<String>) {
239268 keyless_core=debug",
240269 ) ;
241270
242- // Channel writer (for TUI display)
271+ // Channel writer (for TUI display): routes logs to mpsc channel for UI consumption.
272+ // without_time() removes timestamps (TUI shows its own timing).
273+ // with_ansi(false) strips color codes (TUI applies its own styling).
274+ // compact() uses shorter format (saves channel bandwidth).
243275 let channel_layer = tracing_subscriber:: fmt:: layer ( )
244276 . with_writer ( ChannelWriterFactory { tx } )
245277 . without_time ( )
246278 . with_ansi ( false )
247279 . compact ( )
248280 . with_filter ( log_filter. clone ( ) ) ;
249281
250- // File writer (same filter as TUI for consistency)
282+ // File writer (same filter as TUI for consistency): appends to disk log file.
251283 let file_writer = FileWriterFactory {
252284 file_path : log_path,
253285 } ;
254286
287+ // File layer: same formatting as channel (compact, no time, no ANSI) for consistency.
255288 let file_layer = tracing_subscriber:: fmt:: layer ( )
256289 . with_writer ( file_writer)
257290 . without_time ( )
258291 . with_ansi ( false )
259292 . compact ( )
260293 . with_filter ( log_filter) ;
261294
262- // Ignore error if already set (e.g., in tests); we want best-effort setup
295+ // Ignore error if already set (e.g., in tests); we want best-effort setup.
296+ // Registry combines both layers (channel + file) into single subscriber.
263297 let _ = tracing_subscriber:: registry ( )
264298 . with ( channel_layer)
265299 . with ( file_layer)
0 commit comments