log_json: cache an open failure so a misconfigured path doesn't re-issue an open() syscall (and silently drop) on every notice — the error is now surfaced once via stderr and not retried until the path changes; the write-failure reopen (for logrotate) is preserved

This commit is contained in:
Jean Chevronnet 2026-08-19 01:28:42 +00:00
parent 65bedaf417
commit b557c9887f

View file

@ -11,8 +11,10 @@ use std::io::Write;
use crate::server::Server; use crate::server::Server;
thread_local! { thread_local! {
/// (configured path, open append handle) cached for reuse. /// (configured path, open append handle or `None` if opening it failed) cached
static SINK: RefCell<Option<(String, File)>> = const { RefCell::new(None) }; /// for reuse. Caching the failure stops us re-issuing an `open` syscall — and a
/// silent drop — on every single notice when the path is misconfigured.
static SINK: RefCell<Option<(String, Option<File>)>> = const { RefCell::new(None) };
} }
/// Append `msg` as a JSON line to the configured log file. Called at the tail of /// Append `msg` as a JSON line to the configured log file. Called at the tail of
@ -30,16 +32,19 @@ pub fn tee(s: &Server, msg: &str) {
None => true, None => true,
}; };
if need_open { if need_open {
*slot = OpenOptions::new() match OpenOptions::new().create(true).append(true).open(path) {
.create(true) Ok(f) => *slot = Some((path.to_string(), Some(f))),
.append(true) Err(e) => {
.open(path) // surface once (this branch only runs when the path changes),
.ok() // then remember the failure so we don't retry every notice
.map(|f| (path.to_string(), f)); eprintln!("echoircd: log_json cannot open {path}: {e}");
*slot = Some((path.to_string(), None));
}
}
} }
if let Some((_, f)) = slot.as_mut() { if let Some((_, Some(f))) = slot.as_mut() {
if writeln!(f, "{line}").is_err() { if writeln!(f, "{line}").is_err() {
*slot = None; // reopen next time *slot = None; // reopen next time (e.g. after a logrotate rename)
} }
} }
}); });