--- id: 1768d9ab-0c21-4ec3-bb04-bd70366f6240 type: solution title: "File-based logging for Rust TUI apps using tracing-appender" tags: [sba-scout, rust, tracing, logging, tui, ratatui, solution] importance: 0.6 confidence: 0.8 created: "2026-02-28T16:53:12.444250+00:00" updated: "2026-02-28T16:53:58.777098+00:00" relations: - target: bc4abc6e-57c1-4d6c-b414-213c1367be9b type: RELATED_TO direction: outgoing strength: 0.9 edge_id: d07fb339-8eb5-40c8-b63c-fcce499f0fc3 - target: 2cf3058c-85f4-481a-bbe7-17ebb9b9e908 type: RELATED_TO direction: outgoing strength: 0.7 edge_id: 8b24bed4-38a2-4a70-8b5e-de2782c897e0 --- # File Logging for Rust TUI Apps **Project:** sba-scout (Rust) | **Commit:** `bf7c3f8` ## Problem Writing to stdout/stderr corrupts ratatui TUI display. Any tracing output needs to go to a file instead. ## Solution Use `tracing-appender` with `rolling::never()` for a fixed log file path. ### Dependencies ```toml tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-appender = "0.2" ``` ### Setup ```rust use tracing_appender::rolling; use tracing_subscriber::EnvFilter; let file_appender = rolling::never("data", "sba-scout.log"); let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender); tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .with_writer(non_blocking) .init(); // _guard must stay alive for the duration of the app! ``` ### Usage - Set `RUST_LOG=debug` (or `info`, `error`, etc.) for verbose output - Log is written to `data/sba-scout.log` - Add `tracing::error!()` alongside UI notification sends to capture full error context in log ## Critical The `WorkerGuard` returned by `non_blocking()` must be kept alive (stored in a variable that lives for the entire app scope). Dropping it early silently stops log output.