From 25893d0f953209950346ca490cb2235d80d80d0e Mon Sep 17 00:00:00 2001 From: damocles Date: Wed, 7 Oct 2026 23:50:36 +0200 Subject: [PATCH] hive-c0re, hive-priv: demote three routine, non-actionable log sites --- hive-c0re/src/lifecycle/mod.rs | 21 +++++++++++++++++++-- hive-c0re/src/socket_server/mod.rs | 22 +++++++++++++++++++++- hive-priv/src/main.rs | 6 ++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/hive-c0re/src/lifecycle/mod.rs b/hive-c0re/src/lifecycle/mod.rs index 3ecc815a..d2c72040 100644 --- a/hive-c0re/src/lifecycle/mod.rs +++ b/hive-c0re/src/lifecycle/mod.rs @@ -806,7 +806,11 @@ pub async fn prebuild_toplevel(name: &str, flake_ref: &str, node_id: Option let pump_stderr = tokio::spawn(async move { let mut lines = BufReader::new(stderr).lines(); while let Ok(Some(line)) = lines.next_line().await { - tracing::warn!(target: "nix-prebuild", cmdline = %stderr_cmdline, "{line}"); + if is_real_nix_warning(&line) { + tracing::warn!(target: "nix-prebuild", cmdline = %stderr_cmdline, "{line}"); + } else { + tracing::info!(target: "nix-prebuild", cmdline = %stderr_cmdline, "{line}"); + } if let (Some(h), Some(id)) = (&stderr_logs, log_id) { h.append_stderr(id, &line); } @@ -889,6 +893,15 @@ pub fn ensure_agent_runtime_dir(name: &str) -> Result<()> { .with_context(|| format!("create agent runtime dir {}", dir.display())) } +/// Whether a `nix`/`nixos-container` stderr line is an actual warning +/// (nix's own convention: a genuine warning starts with `warning:`) rather +/// than routine progress chatter (`copying N paths…`, `fetching git +/// input…`, `structuredAttrs is enabled`, …) — nix writes the latter to +/// stderr too, and it dwarfs the former in volume. +fn is_real_nix_warning(line: &str) -> bool { + line.trim_start().starts_with("warning:") +} + /// Build the per-line callback for `create_container_streaming` / /// `update_container_streaming`. Both ops share identical dispatch logic /// (stdout → info + `append_stdout`, stderr → warn + `append_stderr`); this @@ -907,7 +920,11 @@ fn make_log_callback( } } PrivStream::Stderr => { - tracing::warn!(target: "nixos-container", cmdline = %cmdline, "{line}"); + if is_real_nix_warning(line) { + tracing::warn!(target: "nixos-container", cmdline = %cmdline, "{line}"); + } else { + tracing::info!(target: "nixos-container", cmdline = %cmdline, "{line}"); + } if let (Some(h), Some(id)) = (&logs, log_id) { h.append_stderr(id, line); } diff --git a/hive-c0re/src/socket_server/mod.rs b/hive-c0re/src/socket_server/mod.rs index 54737131..1df9e322 100644 --- a/hive-c0re/src/socket_server/mod.rs +++ b/hive-c0re/src/socket_server/mod.rs @@ -58,7 +58,15 @@ pub fn start(agent: &str, socket_path: &Path, coord: Arc) -> Result let coord = coord.clone(); tokio::spawn(async move { if let Err(e) = serve(stream, agent, coord).await { - tracing::warn!(error = ?e, "agent connection failed"); + // A peer closing its end mid-read/write (the harness + // process exiting, a turn ending) is routine and the + // overwhelming majority of what reaches here — + // demoted so it doesn't bury a genuine failure. + if is_broken_pipe(&e) { + tracing::debug!(error = ?e, "agent connection closed by peer"); + } else { + tracing::warn!(error = ?e, "agent connection failed"); + } } }); }, @@ -201,6 +209,18 @@ async fn read_request_line( }) } +/// Whether `e`'s root cause is the peer closing the connection — `serve`'s +/// own `?`s convert a raw `std::io::Error` straight to `anyhow::Error` with +/// no intervening `.context()`, so a direct downcast reaches it. +fn is_broken_pipe(e: &anyhow::Error) -> bool { + e.downcast_ref::().is_some_and(|io_err| { + matches!( + io_err.kind(), + std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::ConnectionReset + ) + }) +} + /// Answer each request line on `stream` until EOF. An over-long line gets a /// `Response::Err` and then the connection is closed, because the rest of the /// line is still unread. diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index 5962d06b..f785f489 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -491,8 +491,14 @@ async fn exec( async fn start_container(name: &str) -> Result<(String, String)> { validate_agent_name(name)?; let machine = container_system_name(name); + // Silenced: a first-time create means the unit doesn't exist yet, and + // `systemctl`'s own "Unit ... not loaded" complaint for that case would + // otherwise land in the journal at error priority on every such start — + // routine, not a signal, since the result is already discarded below. let _ = Command::new("systemctl") .args(["reset-failed", &format!("container@{machine}.service")]) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) .status() .await; container_run(&["start", &machine]).await