summaryrefslogtreecommitdiffstats
path: root/src/logging.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-23 11:26:01 +0200
committertv <tv@krebsco.de>2026-10-23 11:26:01 +0200
commit64fbe99234425259d2758982caa52e1a62272d03 (patch)
treead75e3a0448987e43712ece8a32527cf3e9809cb /src/logging.rs
parent3c53394def0ba28555fefa97f2a68e09b97a0b70 (diff)
allow changing log filtering while node runs
This also changes the default to tracing-subscriber.
Diffstat (limited to 'src/logging.rs')
-rw-r--r--src/logging.rs57
1 files changed, 57 insertions, 0 deletions
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<EnvFilter, Registry>;
+
+static FILTER: OnceLock<FilterHandle> = 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<String> {
+ 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<String> {
+ 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"))
+}