Skip to content

Commit fe57b24

Browse files
committed
fix: silence tracing while the tunnel picker is open
The iroh relay-actor (and other chatty modules under iroh::magicsock / lib::*) write to the same TTY inquire is repainting on. The collision looked like: ? Resume which tunnel? 2026-06-07T19:10:30Z INFO relay-actor: ... roxy.net → http://127.0.0.1:11434 [d38f5f413beb] — a log line spliced across the picker's first option, leaving the terminal unreadable. Wrap the EnvFilter in a reload handle exposed via a OnceLock so the picker can engage a RAII QuietTracing guard that swaps the filter to 'error' for the lifetime of the inquire prompt and restores it on drop. Captures the previous filter via EnvFilter's Display impl since it doesn't implement Clone; round-trip through to_string()/try_new() is the supported path. This is a targeted fix for the symptom. A cleaner long-term move would be to defer ListenNode construction past the picker so iroh hasn't booted yet — but that requires TunnelService to support read-only listing without a node, which is a larger refactor.
1 parent 7de50c7 commit fe57b24

1 file changed

Lines changed: 56 additions & 9 deletions

File tree

cli/src/main.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,20 @@ use std::{
1616
net::{IpAddr, SocketAddr},
1717
path::PathBuf,
1818
};
19+
use std::sync::OnceLock;
1920
use tracing::info;
2021
use tracing_subscriber::prelude::*;
2122

23+
/// Global handle to the EnvFilter so the picker can temporarily silence
24+
/// tracing output (the iroh relay actor logs continuously and corrupts
25+
/// the inquire redraw if left on).
26+
static FILTER_RELOAD: OnceLock<
27+
tracing_subscriber::reload::Handle<
28+
tracing_subscriber::EnvFilter,
29+
tracing_subscriber::Registry,
30+
>,
31+
> = OnceLock::new();
32+
2233
/// Datum Connect Agent
2334
#[derive(Parser, Debug)]
2435
struct Args {
@@ -286,11 +297,12 @@ async fn main() -> n0_error::Result<()> {
286297
// Install the ring-based crypto provider for rustls
287298
let _ = rustls_ring::default_provider().install_default();
288299

300+
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
301+
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn"));
302+
let (filter_layer, filter_reload) = tracing_subscriber::reload::Layer::new(env_filter);
303+
let _ = FILTER_RELOAD.set(filter_reload);
289304
tracing_subscriber::registry()
290-
.with(
291-
tracing_subscriber::EnvFilter::try_from_default_env()
292-
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn")),
293-
)
305+
.with(filter_layer)
294306
.with(tracing_subscriber::fmt::layer())
295307
.with(sentry::integrations::tracing::layer())
296308
.init();
@@ -921,6 +933,38 @@ async fn select_project_interactive(datum: &DatumCloudClient) -> n0_error::Resul
921933
Ok(())
922934
}
923935

936+
/// Guard that silences tracing output for its lifetime, restoring the
937+
/// previous filter on drop. Used to keep the iroh relay actor (and
938+
/// anything else chatty) from corrupting inquire's terminal redraw.
939+
struct QuietTracing {
940+
previous: Option<String>,
941+
}
942+
943+
impl QuietTracing {
944+
fn engage() -> Self {
945+
// Capture the previous filter representation so we can rebuild it
946+
// on restore. EnvFilter doesn't implement Clone; serialize+parse
947+
// is the supported round-trip.
948+
let previous = FILTER_RELOAD
949+
.get()
950+
.and_then(|h| h.with_current(|f| f.to_string()).ok());
951+
if let Some(handle) = FILTER_RELOAD.get() {
952+
let _ = handle.reload(tracing_subscriber::EnvFilter::new("error"));
953+
}
954+
Self { previous }
955+
}
956+
}
957+
958+
impl Drop for QuietTracing {
959+
fn drop(&mut self) {
960+
if let (Some(handle), Some(prev)) = (FILTER_RELOAD.get(), self.previous.take())
961+
&& let Ok(filter) = tracing_subscriber::EnvFilter::try_new(&prev)
962+
{
963+
let _ = handle.reload(filter);
964+
}
965+
}
966+
}
967+
924968
/// Interactive picker for resuming an existing tunnel. Returns the
925969
/// chosen tunnel, or `None` if the user cancelled, there are no
926970
/// candidates, or stdin is not a TTY (in which case the caller should
@@ -976,11 +1020,14 @@ async fn pick_tunnel_interactive(
9761020
})
9771021
.collect();
9781022

979-
let answer = inquire::Select::new("Resume which tunnel?", labels)
980-
.with_help_message("↑↓ navigate · Enter select · Esc cancel · ○ = disabled")
981-
.with_page_size(10)
982-
.raw_prompt_skippable()
983-
.map_err(|err| n0_error::anyerr!("interactive selection failed: {err}"))?;
1023+
let answer = {
1024+
let _quiet = QuietTracing::engage();
1025+
inquire::Select::new("Resume which tunnel?", labels)
1026+
.with_help_message("↑↓ navigate · Enter select · Esc cancel · ○ = disabled")
1027+
.with_page_size(10)
1028+
.raw_prompt_skippable()
1029+
.map_err(|err| n0_error::anyerr!("interactive selection failed: {err}"))?
1030+
};
9841031

9851032
Ok(answer.map(|opt| candidates[opt.index].clone()))
9861033
}

0 commit comments

Comments
 (0)