forest/cli_shared/logger/
mod.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::pin::Pin;
5
6use futures::Future;
7use tracing_subscriber::{EnvFilter, Registry, prelude::*};
8
9use crate::cli_shared::cli::CliOpts;
10use crate::utils::misc::LoggingColor;
11
12type BackgroundTask = Pin<Box<dyn Future<Output = ()> + Send>>;
13
14#[derive(Default)]
15pub struct Guards {
16    #[cfg(feature = "tracing-chrome")]
17    tracing_chrome: Option<tracing_chrome::FlushGuard>,
18}
19
20#[allow(unused_mut)]
21pub fn setup_logger(opts: &CliOpts) -> (Vec<BackgroundTask>, Guards) {
22    let mut background_tasks: Vec<BackgroundTask> = vec![];
23    let mut guards = Guards::default();
24    let mut layers: Vec<Box<dyn tracing_subscriber::layer::Layer<Registry> + Send + Sync>> =
25        // console logger
26        vec![Box::new(
27            tracing_subscriber::fmt::Layer::new()
28                .with_ansi(opts.color.coloring_enabled())
29                .with_filter(get_env_filter(default_env_filter())),
30        )];
31
32    // file logger
33    if let Some(log_dir) = &opts.log_dir {
34        let file_appender = tracing_appender::rolling::hourly(log_dir, "forest.log");
35        layers.push(Box::new(
36            tracing_subscriber::fmt::Layer::new()
37                .with_ansi(false)
38                .with_writer(file_appender)
39                .with_filter(get_env_filter(default_env_filter())),
40        ));
41    }
42
43    if opts.tokio_console {
44        #[cfg(not(feature = "tokio-console"))]
45        tracing::warn!(
46            "`tokio-console` is unavailable, forest binaries need to be recompiled with `tokio-console` feature"
47        );
48
49        #[cfg(feature = "tokio-console")]
50        layers.push(Box::new(
51            console_subscriber::ConsoleLayer::builder()
52                .with_default_env()
53                .spawn(),
54        ));
55    }
56
57    if opts.loki {
58        #[cfg(not(feature = "tracing-loki"))]
59        tracing::warn!(
60            "`tracing-loki` is unavailable, forest binaries need to be recompiled with `tracing-loki` feature"
61        );
62
63        #[cfg(feature = "tracing-loki")]
64        {
65            let (layer, task) = tracing_loki::layer(
66                tracing_loki::url::Url::parse(&opts.loki_endpoint)
67                    .map_err(|e| {
68                        format!("Unable to parse loki endpoint {}: {e}", &opts.loki_endpoint)
69                    })
70                    .unwrap(),
71                vec![(
72                    "host".into(),
73                    gethostname::gethostname()
74                        .to_str()
75                        .unwrap_or_default()
76                        .into(),
77                )]
78                .into_iter()
79                .collect(),
80                Default::default(),
81            )
82            .map_err(|e| format!("Unable to create loki layer: {e}"))
83            .unwrap();
84            background_tasks.push(Box::pin(task));
85            layers.push(Box::new(
86                layer.with_filter(tracing_subscriber::filter::LevelFilter::INFO),
87            ));
88        }
89    }
90
91    // Go to <https://ui.perfetto.dev> to browse trace files.
92    // You may want to call ChromeLayerBuilder::trace_style as appropriate
93    if let Some(_chrome_trace_file) = std::env::var_os("CHROME_TRACE_FILE") {
94        #[cfg(not(feature = "tracing-chrome"))]
95        tracing::warn!(
96            "`tracing-chrome` is unavailable, forest binaries need to be recompiled with `tracing-chrome` feature"
97        );
98
99        #[cfg(feature = "tracing-chrome")]
100        {
101            let (layer, guard) = match _chrome_trace_file.is_empty() {
102                true => tracing_chrome::ChromeLayerBuilder::new().build(),
103                false => tracing_chrome::ChromeLayerBuilder::new()
104                    .file(_chrome_trace_file)
105                    .build(),
106            };
107
108            guards.tracing_chrome = Some(guard);
109            layers.push(Box::new(layer));
110        }
111    }
112
113    tracing_subscriber::registry().with(layers).init();
114    (background_tasks, guards)
115}
116
117// Log warnings to stderr
118pub fn setup_minimal_logger() {
119    tracing_subscriber::registry()
120        .with(
121            tracing_subscriber::fmt::Layer::new()
122                .with_ansi(LoggingColor::Auto.coloring_enabled())
123                .with_writer(std::io::stderr)
124                .with_filter(get_env_filter(default_tool_filter())),
125        )
126        .init();
127}
128
129/// Returns an [`EnvFilter`] according to the `RUST_LOG` environment variable, or a default
130/// - see [`default_env_filter`] and [`default_tool_filter`]
131///
132/// Note that [`tracing_subscriber::filter::Builder`] only allows a single default directive,
133/// whereas we want to provide multiple.
134/// See also <https://github.com/tokio-rs/tracing/blob/27f688efb72316a26f3ec1f952c82626692c08ff/tracing-subscriber/src/filter/env/builder.rs#L189-L194>
135fn get_env_filter(def: EnvFilter) -> EnvFilter {
136    use std::env::{
137        self,
138        VarError::{NotPresent, NotUnicode},
139    };
140    match env::var(tracing_subscriber::EnvFilter::DEFAULT_ENV) {
141        Ok(s) => EnvFilter::new(s),
142        Err(NotPresent) => def,
143        Err(NotUnicode(_)) => EnvFilter::default(),
144    }
145}
146
147fn default_env_filter() -> EnvFilter {
148    let default_directives = [
149        "info",
150        "bellperson::groth16::aggregate::verify=warn",
151        "axum=warn",
152        "filecoin_proofs=warn",
153        "libp2p_bitswap=off",
154        "libp2p_gossipsub=error",
155        "libp2p_kad=error",
156        "storage_proofs_core=warn",
157        "tracing_loki=off",
158        "quinn_udp=error",
159    ];
160    EnvFilter::try_new(default_directives.join(",")).unwrap()
161}
162
163fn default_tool_filter() -> EnvFilter {
164    let default_directives = [
165        "info",
166        "bellperson::groth16::aggregate::verify=warn",
167        "storage_proofs_core=warn",
168        "axum=warn",
169        "filecoin_proofs=warn",
170        "forest::snapshot=info",
171        "forest::progress=info",
172        "libp2p_bitswap=off",
173        "tracing_loki=off",
174        "hickory_resolver::hosts=off",
175        "libp2p_swarm=off",
176    ];
177    EnvFilter::try_new(default_directives.join(",")).unwrap()
178}
179
180#[test]
181fn test_default_env_filter() {
182    let _did_not_panic = default_env_filter();
183}