summaryrefslogtreecommitdiffstats
path: root/src/logging.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/logging.rs')
-rw-r--r--src/logging.rs58
1 files changed, 58 insertions, 0 deletions
diff --git a/src/logging.rs b/src/logging.rs
new file mode 100644
index 0000000..4496125
--- /dev/null
+++ b/src/logging.rs
@@ -0,0 +1,58 @@
+//! 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::{Result, anyhow};
+use tracing_subscriber::{EnvFilter, Registry, prelude::*, reload};
+
+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"))
+}