feat(daemon): write rotating xy.log alongside stderr

This commit is contained in:
2026-07-31 22:57:30 +02:00
parent f70092f1a0
commit b69426f4df
3 changed files with 70 additions and 12 deletions
-2
View File
@@ -66,8 +66,6 @@ pub fn spawn_supervisor(paths: &Paths, cfg: ServerConfig) -> Result<SupervisorHa
}
pub async fn run(paths: Paths) -> Result<()> {
paths.ensure_dirs().context("create state dirs")?;
let _pid =
PidFile::acquire(&paths.pidfile).context("another xy daemon appears to be running")?;
+32
View File
@@ -0,0 +1,32 @@
use std::path::Path;
use std::sync::{Arc, Mutex};
use xy_supervisor::logs::RotatingLogWriter;
const LOG_FILE_MAX_BYTES: u64 = 10 * 1024 * 1024;
const LOG_FILE_KEEP: usize = 5;
pub(crate) fn daemon_writer(log_dir: &Path) -> std::io::Result<Arc<Mutex<RotatingLogWriter>>> {
let writer =
RotatingLogWriter::open(&log_dir.join("xy.log"), LOG_FILE_MAX_BYTES, LOG_FILE_KEEP)?;
Ok(Arc::new(Mutex::new(writer)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn daemon_writer_creates_and_appends_to_xy_log() {
use std::io::Write;
let tmp = tempfile::tempdir().unwrap();
let writer = daemon_writer(tmp.path()).unwrap();
writer.lock().unwrap().write_all(b"line\n").unwrap();
let contents = std::fs::read_to_string(tmp.path().join("xy.log")).unwrap();
assert_eq!(contents, "line\n");
}
}
+38 -10
View File
@@ -1,7 +1,9 @@
use clap::{Parser, Subcommand};
use std::sync::Arc;
mod cli;
mod daemon;
mod logging;
mod paths;
mod pidfile;
@@ -55,23 +57,49 @@ enum Cmd {
#[tokio::main]
async fn main() -> std::process::ExitCode {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.with_writer(std::io::stderr)
.init();
let cli = Cli::parse();
let paths = match paths::Paths::resolve() {
Ok(p) => p,
Err(e) => {
eprintln!("xy: failed to resolve XDG paths: {e}");
Err(err) => {
eprintln!("xy: failed to resolve XDG paths: {err}");
return std::process::ExitCode::from(3);
}
};
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
if matches!(cli.cmd, Cmd::Daemon) {
use tracing_subscriber::fmt::writer::MakeWriterExt;
if let Err(err) = paths.ensure_dirs() {
eprintln!("xy: failed to create state dirs: {err}");
return std::process::ExitCode::from(3);
}
let file = match logging::daemon_writer(&paths.log_dir) {
Ok(writer) => writer,
Err(err) => {
eprintln!("xy: failed to open daemon log: {err}");
return std::process::ExitCode::from(3);
}
};
let file = Arc::into_inner(file).expect("daemon_writer arc has one owner");
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_ansi(false)
.with_writer(std::io::stderr.and(file))
.init();
} else {
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_writer(std::io::stderr)
.init();
}
let result: anyhow::Result<i32> = match cli.cmd {
Cmd::Daemon => daemon::run(paths).await.map(|_| 0),
Cmd::List => cli::list(paths).await,