mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +00:00
feat(logging): add daily log rotation and 7-day cleanup
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
Logs were growing unbounded — cli.log and server.log used rolling::never() with no rotation. Switch to daily rotation via tracing-appender builder API (prefix.YYYY-MM-DD.log) and clean up files older than 7 days on startup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5b63e25b28
commit
a7bc63a5ae
3 changed files with 45 additions and 6 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Fabro Logging Strategy
|
||||
|
||||
Fabro uses the `tracing` crate for structured, file-based logging. Logs write to `~/.fabro/logs/YYYY-MM-DD.log`, controlled by the `FABRO_LOG` env var (default: `info`). Logs are for **developers debugging issues after the fact** — they are not user-facing output.
|
||||
Fabro uses the `tracing` crate for structured, file-based logging. Logs write to `~/.fabro/logs/{prefix}.YYYY-MM-DD.log` (e.g. `cli.2026-04-06.log`, `server.2026-04-06.log`), rotated daily by `tracing-appender`. Logs older than 7 days are cleaned up on startup. Controlled by the `FABRO_LOG` env var (default: `info`). Logs are for **developers debugging issues after the fact** — they are not user-facing output.
|
||||
|
||||
Production runs at INFO level. INFO should be low-volume and high-signal — the summary of what happened. When something goes wrong, developers enable `FABRO_LOG=debug` to get the full picture. DEBUG can be as verbose as needed since it's only turned on temporarily.
|
||||
|
||||
|
|
|
|||
|
|
@ -169,14 +169,11 @@ fn execute_daemon(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
// Rotate logs
|
||||
let server_state = Storage::new(storage_dir).server_state();
|
||||
let log_path = server_state.log_path();
|
||||
if let Some(parent) = log_path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let prev_path = log_path.with_extension("log.prev");
|
||||
let _ = std::fs::rename(&log_path, &prev_path);
|
||||
|
||||
let record_path = server_state.record_path();
|
||||
let log_file = std::fs::File::create(&log_path)?;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use fabro_util::run_log;
|
||||
use tracing_appender::rolling;
|
||||
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
const LOG_RETENTION_DAYS: u32 = 7;
|
||||
|
||||
pub(crate) fn init_tracing(
|
||||
debug: bool,
|
||||
config_log_level: Option<&str>,
|
||||
|
|
@ -21,8 +25,14 @@ pub(crate) fn init_tracing(
|
|||
std::fs::create_dir_all(&log_dir)
|
||||
.with_context(|| format!("Failed to create log directory: {}", log_dir.display()))?;
|
||||
|
||||
let filename = format!("{log_prefix}.log");
|
||||
let file_appender = rolling::never(&log_dir, &filename);
|
||||
let file_appender = rolling::RollingFileAppender::builder()
|
||||
.rotation(rolling::Rotation::DAILY)
|
||||
.filename_prefix(log_prefix)
|
||||
.filename_suffix("log")
|
||||
.build(&log_dir)
|
||||
.with_context(|| "Failed to create log file appender")?;
|
||||
|
||||
cleanup_old_logs(&log_dir, log_prefix, LOG_RETENTION_DAYS);
|
||||
|
||||
let run_log_writer = run_log::init();
|
||||
|
||||
|
|
@ -44,3 +54,35 @@ pub(crate) fn init_tracing(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cleanup_old_logs(log_dir: &Path, prefix: &str, max_age_days: u32) {
|
||||
let cutoff = chrono::Utc::now().date_naive() - chrono::Duration::days(i64::from(max_age_days));
|
||||
let Ok(entries) = std::fs::read_dir(log_dir) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let date_prefix = format!("{prefix}.");
|
||||
let date_suffix = ".log";
|
||||
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name();
|
||||
let Some(name) = name.to_str() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(rest) = name.strip_prefix(&date_prefix) else {
|
||||
continue;
|
||||
};
|
||||
let Some(date_str) = rest.strip_suffix(date_suffix) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(date) = chrono::NaiveDate::parse_from_str(date_str, "%Y-%m-%d") else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if date < cutoff {
|
||||
let _ = std::fs::remove_file(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue