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_CONSENSUS: &str = "fm::consensus";
25pub const LOG_CORE: &str = "fm::core";
26pub const LOG_DB: &str = "fm::db";
27pub const LOG_DEVIMINT: &str = "fm::devimint";
28pub const LOG_NET: &str = "fm::net";
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_MODULE_LN: &str = "fm::module::ln";
44pub const LOG_MODULE_LNV2: &str = "fm::module::lnv2";
45pub const LOG_CLIENT_REACTOR: &str = "fm::client::reactor";
46pub const LOG_CLIENT_NET_API: &str = "fm::client::net::api";
47pub const LOG_CLIENT_BACKUP: &str = "fm::client::backup";
48pub const LOG_CLIENT_RECOVERY: &str = "fm::client::recovery";
49pub const LOG_CLIENT_RECOVERY_MINT: &str = "fm::client::recovery::mint";
50pub const LOG_CLIENT_MODULE_MINT: &str = "fm::client::module::mint";
51pub const LOG_CLIENT_MODULE_META: &str = "fm::client::module::meta";
52pub const LOG_CLIENT_MODULE_LN: &str = "fm::client::module::ln";
53pub const LOG_CLIENT_MODULE_LNV2: &str = "fm::client::module::lnv2";
54pub const LOG_CLIENT_MODULE_WALLET: &str = "fm::client::module::wallet";
55pub const LOG_GATEWAY: &str = "fm::gw";
56pub const LOG_BITCOIND_ESPLORA: &str = "fm::bitcoind::esplora";
57pub const LOG_BITCOIND_CORE: &str = "fm::bitcoind::bitcoincore";
58pub const LOG_BITCOIND: &str = "fm::bitcoind";
59
60/// Consolidates the setup of server tracing into a helper
61#[derive(Default)]
62pub struct TracingSetup {
63    base_level: Option<String>,
64    extra_directives: Option<String>,
65    #[cfg(feature = "telemetry")]
66    tokio_console_bind: Option<std::net::SocketAddr>,
67    #[cfg(feature = "telemetry")]
68    with_jaeger: bool,
69    with_file: Option<File>,
70}
71
72impl TracingSetup {
73    /// Setup a console server for tokio logging <https://docs.rs/console-subscriber>
74    #[cfg(feature = "telemetry")]
75    pub fn tokio_console_bind(&mut self, address: Option<std::net::SocketAddr>) -> &mut Self {
76        self.tokio_console_bind = address;
77        self
78    }
79
80    /// Setup telemetry through Jaeger <https://docs.rs/tracing-jaeger>
81    #[cfg(feature = "telemetry")]
82    pub fn with_jaeger(&mut self, enabled: bool) -> &mut Self {
83        self.with_jaeger = enabled;
84        self
85    }
86
87    pub fn with_file(&mut self, file: Option<File>) -> &mut Self {
88        self.with_file = file;
89        self
90    }
91
92    /// Sets the log level applied to most modules. Some overly chatty modules
93    /// are muted even if this is set to a lower log level, use the `RUST_LOG`
94    /// environment variable to override.
95    pub fn with_base_level(&mut self, level: impl Into<String>) -> &mut Self {
96        self.base_level = Some(level.into());
97        self
98    }
99
100    /// Add a filter directive.
101    pub fn with_directive(&mut self, directive: &str) -> &mut Self {
102        if let Some(old) = self.extra_directives.as_mut() {
103            *old = format!("{old},{directive}");
104        } else {
105            self.extra_directives = Some(directive.to_owned());
106        }
107        self
108    }
109
110    /// Initialize the logging, must be called for tracing to begin
111    pub fn init(&mut self) -> anyhow::Result<()> {
112        use tracing_subscriber::fmt::writer::{BoxMakeWriter, Tee};
113
114        let var = env::var(tracing_subscriber::EnvFilter::DEFAULT_ENV).unwrap_or_default();
115        let filter_layer = EnvFilter::builder().parse(format!(
116            // We prefix everything with a default general log level and
117            // good per-module specific default. User provided RUST_LOG
118            // can override one or both
119            "{},{},{},{},{},{},{},{}",
120            self.base_level.as_deref().unwrap_or("info"),
121            "jsonrpsee_core::client::async_client=off",
122            "hyper=off",
123            "h2=off",
124            "jsonrpsee_server=warn,jsonrpsee_server::transport=off",
125            "AlephBFT-=error",
126            var,
127            self.extra_directives.as_deref().unwrap_or(""),
128        ))?;
129
130        let fmt_writer = if let Some(file) = self.with_file.take() {
131            BoxMakeWriter::new(Tee::new(io::stderr, file))
132        } else {
133            BoxMakeWriter::new(io::stderr)
134        };
135
136        let fmt_layer = tracing_subscriber::fmt::layer()
137            .with_thread_names(false) // can be enabled for debugging
138            .with_writer(fmt_writer)
139            .with_filter(filter_layer);
140
141        let console_opt = || -> Option<Box<dyn Layer<_> + Send + Sync + 'static>> {
142            #[cfg(feature = "telemetry")]
143            if let Some(l) = self.tokio_console_bind {
144                let tracer = console_subscriber::ConsoleLayer::builder()
145                    .retention(std::time::Duration::from_secs(60))
146                    .server_addr(l)
147                    .spawn()
148                    // tokio-console cares only about these layers, so we filter separately for it
149                    .with_filter(EnvFilter::new("tokio=trace,runtime=trace"));
150                return Some(tracer.boxed());
151            }
152            None
153        };
154
155        let telemetry_layer_opt = || -> Option<Box<dyn Layer<_> + Send + Sync + 'static>> {
156            #[cfg(feature = "telemetry")]
157            if self.with_jaeger {
158                // TODO: https://github.com/fedimint/fedimint/issues/4591
159                #[allow(deprecated)]
160                let tracer = opentelemetry_jaeger::new_agent_pipeline()
161                    .with_service_name("fedimint")
162                    .install_simple()
163                    .unwrap();
164
165                return Some(tracing_opentelemetry::layer().with_tracer(tracer).boxed());
166            }
167            None
168        };
169
170        tracing_subscriber::registry()
171            .with(fmt_layer)
172            .with(console_opt())
173            .with(telemetry_layer_opt())
174            .try_init()?;
175        Ok(())
176    }
177}
178
179pub fn shutdown() {
180    #[cfg(feature = "telemetry")]
181    opentelemetry::global::shutdown_tracer_provider();
182}