Skip to content

Commit afab78c

Browse files
committed
docs(logging): add inline comments throughout logging crate
Add concise, high-signal inline comments to function bodies across the logging initialization implementation. Comments explain intent, edge cases, error handling strategies, and concurrency patterns rather than restating code behavior. Key areas covered: - Environment variable precedence (KEYLESS_LOG vs RUST_LOG) - Idempotent initialization (try_init error handling) - UTF-8 conversion (lossy handling for logs) - Channel writer (non-blocking sends, Write contract) - File writer (mutex-protected concurrent writes, best-effort errors) - Three-tier fallback chain (log file → null device → temp → panic) - Filter cloning rationale (shared filters across layers)
1 parent a30c33d commit afab78c

1 file changed

Lines changed: 44 additions & 10 deletions

File tree

keyless-logging/src/lib.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ use tracing_subscriber::{EnvFilter, fmt};
5050
///
5151
/// Prefers `KEYLESS_LOG` over `RUST_LOG`, defaulting to `"info"`.
5252
fn 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 {
6365
pub 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() {
7782
pub 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

97105
impl 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.
130145
fn 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

146163
impl 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.
222250
pub 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

Comments
 (0)