Add file-based logging to avoid TUI corruption from stderr output

Errors were only visible in the truncated status bar notification.
Add tracing-appender to write to data/sba-scout.log with env-filter
support (RUST_LOG), and add tracing::error! calls alongside all
notification-only error paths for full diagnostic visibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-02-28 08:04:33 -06:00
parent 6d2b11a797
commit bf7c3f870f
5 changed files with 73 additions and 1 deletions

47
rust/Cargo.lock generated
View File

@ -270,6 +270,15 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
@ -1371,6 +1380,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata",
]
[[package]]
name = "md-5"
version = "0.10.6"
@ -2221,6 +2239,7 @@ dependencies = [
"tokio",
"toml",
"tracing",
"tracing-appender",
"tracing-subscriber",
]
@ -2903,12 +2922,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
@ -2917,6 +2938,16 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinystr"
version = "0.8.2"
@ -3112,6 +3143,18 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-appender"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
dependencies = [
"crossbeam-channel",
"thiserror 2.0.18",
"time",
"tracing-subscriber",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
@ -3150,10 +3193,14 @@ version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]

View File

@ -31,7 +31,8 @@ thiserror = "2"
# Logging
tracing = "0.1"
tracing-subscriber = "0.3"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-appender = "0.2"
# Date/time
chrono = { version = "0.4", features = ["serde"] }

View File

@ -5,11 +5,27 @@ use ratatui::DefaultTerminal;
use sqlx::sqlite::SqlitePool;
use tokio::sync::mpsc;
use tokio::time::{interval, Duration};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::EnvFilter;
use sba_scout::app::{App, AppMessage};
use sba_scout::config;
use sba_scout::db;
fn init_logging(log_dir: &std::path::Path) -> WorkerGuard {
std::fs::create_dir_all(log_dir).ok();
let file_appender = tracing_appender::rolling::never(log_dir, "sba-scout.log");
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
tracing_subscriber::fmt()
.with_writer(non_blocking)
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.with_ansi(false)
.init();
guard
}
#[tokio::main]
async fn main() -> Result<()> {
let settings = match config::load_settings() {
@ -20,6 +36,9 @@ async fn main() -> Result<()> {
}
};
let log_dir = settings.db_path.parent().unwrap_or(std::path::Path::new("data"));
let _log_guard = init_logging(log_dir);
if let Some(parent) = settings.db_path.parent() {
std::fs::create_dir_all(parent)?;
}

View File

@ -56,6 +56,7 @@ impl DashboardState {
let _ = tx.send(AppMessage::RosterLoaded(roster));
}
Err(e) => {
tracing::error!("Failed to load roster: {e}");
let _ = tx.send(AppMessage::Notify(
format!("Failed to load roster: {e}"),
NotifyLevel::Error,
@ -119,6 +120,7 @@ impl DashboardState {
);
}
Err(e) => {
tracing::error!("Sync failed: {e}");
self.sync_state = SyncState::Error;
self.sync_message = format!("Sync failed: {e}");
}

View File

@ -157,6 +157,7 @@ impl GamedayState {
let _ = tx_c.send(AppMessage::TeamsLoaded(teams));
}
Err(e) => {
tracing::error!("Failed to load teams: {e}");
let _ = tx_c.send(AppMessage::Notify(
format!("Failed to load teams: {e}"),
NotifyLevel::Error,
@ -620,6 +621,7 @@ impl GamedayState {
let _ = tx.send(AppMessage::PitchersLoaded(pitchers));
}
Err(e) => {
tracing::error!("Failed to load pitchers: {e}");
let _ = tx.send(AppMessage::Notify(
format!("Failed to load pitchers: {e}"),
NotifyLevel::Error,
@ -661,6 +663,7 @@ impl GamedayState {
let _ = tx.send(AppMessage::MatchupsCalculated(results));
}
Err(e) => {
tracing::error!("Matchup calculation failed: {e}");
let _ = tx.send(AppMessage::Notify(
format!("Matchup calculation failed: {e}"),
NotifyLevel::Error,