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<()> {
|
pub async fn run(paths: Paths) -> Result<()> {
|
||||||
paths.ensure_dirs().context("create state dirs")?;
|
|
||||||
|
|
||||||
let _pid =
|
let _pid =
|
||||||
PidFile::acquire(&paths.pidfile).context("another xy daemon appears to be running")?;
|
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 clap::{Parser, Subcommand};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod daemon;
|
mod daemon;
|
||||||
|
mod logging;
|
||||||
mod paths;
|
mod paths;
|
||||||
mod pidfile;
|
mod pidfile;
|
||||||
|
|
||||||
@@ -55,23 +57,49 @@ enum Cmd {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> std::process::ExitCode {
|
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 cli = Cli::parse();
|
||||||
|
|
||||||
let paths = match paths::Paths::resolve() {
|
let paths = match paths::Paths::resolve() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
eprintln!("xy: failed to resolve XDG paths: {e}");
|
eprintln!("xy: failed to resolve XDG paths: {err}");
|
||||||
return std::process::ExitCode::from(3);
|
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 {
|
let result: anyhow::Result<i32> = match cli.cmd {
|
||||||
Cmd::Daemon => daemon::run(paths).await.map(|_| 0),
|
Cmd::Daemon => daemon::run(paths).await.map(|_| 0),
|
||||||
Cmd::List => cli::list(paths).await,
|
Cmd::List => cli::list(paths).await,
|
||||||
|
|||||||
Reference in New Issue
Block a user