fedimint_logging/
lib.rs

1#![deny(clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)]
3#![allow(clippy::missing_panics_doc)]
4
5//! Constants for categorizing the logging type
6//!
7//! To help stabilize logging targets, avoid typos and improve consistency,
8//! it's preferable for logging statements use static target constants,
9//! that we define in this module.
10//!
11//! Core + server side components should use global namespace,
12//! while client should generally be prefixed with `client::`.
13//! This makes it easier to filter interesting calls when
14//! running e.g. `devimint`, that will run both server and client
15//! side.
16
17use std::fs::File;
18use std::{env, io};
19
20use tracing_subscriber::layer::SubscriberExt;
21use tracing_subscriber::util::SubscriberInitExt;
22use tracing_subscriber::{EnvFilter, Layer};
23
24pub const LOG_BLOCKCHAIN: &str = "fm::net::blockchain";
25pub const LOG_CONSENSUS: &str = "fm::consensus";
26pub const LOG_CORE: &str = "fm::core";
27pub const LOG_DB: &str = "fm::db";
28pub const LOG_DEVIMINT: &str = "fm::devimint";
29pub const LOG_NET_API: &str = "fm::net::api";
30pub const LOG_NET_PEER_DKG: &str = "fm::net::peer::dkg";
31pub const LOG_NET_PEER: &str = "fm::net::peer";
32pub const LOG_NET_AUTH: &str = "fm::net::auth";
33pub const LOG_TASK: &str = "fm::task";
34pub const LOG_RUNTIME: &str = "fm::runtime";
35pub const LOG_TEST: &str = "fm::test";
36pub const LOG_TIMING: &str = "fm::timing";
37pub const LOG_CLIENT: &str = "fm::client";
38pub const LOG_CLIENT_DB: &str = "fm::client::db";
39pub const LOG_CLIENT_EVENT_LOG: &str = "fm::client::event-log";
40pub const LOG_MODULE_MINT: &str = "fm::module::mint";
41pub const LOG_MODULE_META: &str = "fm::module::meta";
42pub const LOG_MODULE_WALLET: &str = "fm::module::wallet";
43pub const LOG_CLIENT_REACTOR: &str = "fm::client::reactor";
44pub const LOG_CLIENT_NET_API: &str = "fm::client::net::api";
45pub const LOG_CLIENT_BACKUP: &str = "fm::client::backup";
46pub const LOG_CLIENT_RECOVERY: &str = "fm::client::recovery";
47pub const LOG_CLIENT_RECOVERY_MINT: &str = "fm::client::recovery::mint";
48pub const LOG_CLIENT_MODULE_META: &str = "fm::client::module::meta";
49pub const LOG_CLIENT_MODULE_MINT: &str = "fm::client::module::mint";
50pub const LOG_CLIENT_MODULE_LN: &str = "fm::client::module::ln";
51pub const LOG_CLIENT_MODULE_WALLET: &str = "fm::client::module::wallet";
52
53/// Consolidates the setup of server tracing into a helper
54#[derive(Default)]
55pub struct TracingSetup {
56    base_level: Option<String>,
57    extra_directives: Option<String>,
58    #[cfg(feature = "telemetry")]
59    tokio_console_bind: Option<std::net::SocketAddr>,
60    #[cfg(feature = "telemetry")]
61    with_jaeger: bool,
62    #[cfg(feature = "telemetry")]
63    with_chrome: bool,
64    with_file: Option<File>,
65}
66
67impl TracingSetup {
68    /// Setup a console server for tokio logging <https://docs.rs/console-subscriber>
69    #[cfg(feature = "telemetry")]
70    pub fn tokio_console_bind(&mut self, address: Option<std::net::SocketAddr>) -> &mut Self {
71        self.tokio_console_bind = address;
72        self
73    }
74
75    /// Setup telemetry through Jaeger <https://docs.rs/tracing-jaeger>
76    #[cfg(feature = "telemetry")]
77    pub fn with_jaeger(&mut self, enabled: bool) -> &mut Self {
78        self.with_jaeger = enabled;
79        self
80    }
81
82    /// Setup telemetry through Chrome <https://docs.rs/tracing-chrome>
83    #[cfg(feature = "telemetry")]
84    pub fn with_chrome(&mut self, enabled: bool) -> &mut Self {
85        self.with_chrome = enabled;
86        self
87    }
88
89    pub fn with_file(&mut self, file: Option<File>) -> &mut Self {
90        self.with_file = file;
91        self
92    }
93
94    /// Sets the log level applied to most modules. Some overly chatty modules
95    /// are muted even if this is set to a lower log level, use the `RUST_LOG`
96    /// environment variable to override.
97    pub fn with_base_level(&mut self, level: impl Into<String>) -> &mut Self {
98        self.base_level = Some(level.into());
99        self
100    }
101
102    /// Add a filter directive.
103    pub fn with_directive(&mut self, directive: &str) -> &mut Self {
104        if let Some(old) = self.extra_directives.as_mut() {
105            *old = format!("{old},{directive}");
106        } else {
107            self.extra_directives = Some(directive.to_owned());
108        }
109        self
110    }
111
112    /// Initialize the logging, must be called for tracing to begin
113    pub fn init(&mut self) -> anyhow::Result<()> {
114        use tracing_subscriber::fmt::writer::{BoxMakeWriter, Tee};
115
116        let var = env::var(tracing_subscriber::EnvFilter::DEFAULT_ENV).unwrap_or_default();
117        let filter_layer = EnvFilter::builder().parse(format!(
118            // We prefix everything with a default general log level and
119            // good per-module specific default. User provided RUST_LOG
120            // can override one or both
121            "{},{},{},{},{},{},{},{}",
122            self.base_level.as_deref().unwrap_or("info"),
123            "jsonrpsee_core::client::async_client=off",
124            "hyper=off",
125            "h2=off",
126            "jsonrpsee_server=warn,jsonrpsee_server::transport=off",
127            "AlephBFT-=error",
128            var,
129            self.extra_directives.as_deref().unwrap_or(""),
130        ))?;
131
132        let fmt_writer = if let Some(file) = self.with_file.take() {
133            BoxMakeWriter::new(Tee::new(io::stderr, file))
134        } else {
135            BoxMakeWriter::new(io::stderr)
136        };
137
138        let fmt_layer = tracing_subscriber::fmt::layer()
139            .with_thread_names(false) // can be enabled for debugging
140            .with_writer(fmt_writer)
141            .with_filter(filter_layer);
142
143        let console_opt = || -> Option<Box<dyn Layer<_> + Send + Sync + 'static>> {
144            #[cfg(feature = "telemetry")]
145            if let Some(l) = self.tokio_console_bind {
146                let tracer = console_subscriber::ConsoleLayer::builder()
147                    .retention(std::time::Duration::from_secs(60))
148                    .server_addr(l)
149                    .spawn()
150                    // tokio-console cares only about these layers, so we filter separately for it
151                    .with_filter(EnvFilter::new("tokio=trace,runtime=trace"));
152                return Some(tracer.boxed());
153            }
154            None
155        };
156
157        let telemetry_layer_opt = || -> Option<Box<dyn Layer<_> + Send + Sync + 'static>> {
158            #[cfg(feature = "telemetry")]
159            if self.with_jaeger {
160                // TODO: https://github.com/fedimint/fedimint/issues/4591
161                #[allow(deprecated)]
162                let tracer = opentelemetry_jaeger::new_agent_pipeline()
163                    .with_service_name("fedimint")
164                    .install_simple()
165                    .unwrap();
166
167                return Some(tracing_opentelemetry::layer().with_tracer(tracer).boxed());
168            }
169            None
170        };
171
172        tracing_subscriber::registry()
173            .with(fmt_layer)
174            .with(console_opt())
175            .with(telemetry_layer_opt())
176            .try_init()?;
177        Ok(())
178    }
179}
180
181pub fn shutdown() {
182    #[cfg(feature = "telemetry")]
183    opentelemetry::global::shutdown_tracer_provider();
184}