feat(daemon): write rotating xy.log alongside stderr
This commit is contained in:
@@ -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")?;
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user