From 64fbe99234425259d2758982caa52e1a62272d03 Mon Sep 17 00:00:00 2001 From: tv Date: Fri, 23 Oct 2026 11:26:01 +0200 Subject: allow changing log filtering while node runs This also changes the default to tracing-subscriber. --- src/logging.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/logging.rs (limited to 'src/logging.rs') diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..c87837c --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,57 @@ +//! Logging: a compact fmt logger whose filter can be replaced while the node runs +//! (`/admin/log`), and, with the `tokio-console` feature, a tokio-console layer. +//! +//! The filter starts from `RUST_LOG`, or `info` without it. It applies to the fmt +//! logger only, so the console layer keeps getting the runtime events it needs. + +use std::sync::OnceLock; + +use anyhow::{anyhow, Result}; +use tracing_subscriber::{prelude::*, reload, EnvFilter, Registry}; + +type FilterHandle = reload::Handle; + +static FILTER: OnceLock = OnceLock::new(); + +/// Installs the global subscriber. Call once, first thing in main. +pub(crate) fn init() { + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); + let (filter, handle) = reload::Layer::new(filter); + + let registry = tracing_subscriber::registry() + .with(tracing_subscriber::fmt::layer().compact().with_filter(filter)); + + #[cfg(feature = "tokio-console")] + let registry = registry.with( + console_subscriber::spawn() + .with_filter(EnvFilter::new("tokio=trace,runtime=trace")), + ); + + registry.init(); + let _ = FILTER.set(handle); +} + +/// The filter in effect, in `RUST_LOG` syntax. +pub(crate) fn filter() -> Result { + handle()? + .with_current(|filter| filter.to_string()) + .map_err(|error| anyhow!("Reading the log filter: {error}")) +} + +/// Replaces the filter. `directives` uses `RUST_LOG` syntax, e.g. +/// `info,continuum::swarm::sync=trace`. An invalid one leaves the old filter in place. +pub(crate) fn set_filter(directives: &str) -> Result { + let filter = EnvFilter::try_new(directives) + .map_err(|error| anyhow!("Invalid log filter {directives:?}: {error}"))?; + let applied = filter.to_string(); + handle()? + .reload(filter) + .map_err(|error| anyhow!("Replacing the log filter: {error}"))?; + Ok(applied) +} + +fn handle() -> Result<&'static FilterHandle> { + FILTER + .get() + .ok_or_else(|| anyhow!("Logging is not initialised")) +} -- cgit v1.3.1