Skip to main content

forest/cli_shared/logger/
mod.rs

1// Copyright 2019-2026 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#[allow(unused_mut)]
15pub fn setup_logger(opts: &CliOpts) -> anyhow::Result<Vec<BackgroundTask>> {
16    let mut background_tasks: Vec<BackgroundTask> = vec![];
17    let mut layers: Vec<Box<dyn tracing_subscriber::layer::Layer<Registry> + Send + Sync>> =
18        // console logger
19        vec![Box::new(
20            tracing_subscriber::fmt::Layer::new()
21                .with_ansi(opts.color.coloring_enabled())
22                .with_filter(get_env_filter(default_env_filter())),
23        )];
24
25    // file logger
26    if let Some(log_dir) = &opts.log_dir {
27        let file_appender = tracing_appender::rolling::hourly(log_dir, "forest.log");
28        layers.push(Box::new(
29            tracing_subscriber::fmt::Layer::new()
30                .with_ansi(false)
31                .with_writer(file_appender)
32                .with_filter(get_env_filter(default_env_filter())),
33        ));
34    }
35
36    if opts.tokio_console {
37        #[cfg(not(feature = "tokio-console"))]
38        tracing::warn!(
39            "`tokio-console` is unavailable, forest binaries need to be recompiled with `tokio-console` feature"
40        );
41
42        #[cfg(feature = "tokio-console")]
43        layers.push(Box::new(
44            console_subscriber::ConsoleLayer::builder()
45                .with_default_env()
46                .spawn(),
47        ));
48    }
49
50    if opts.loki {
51        #[cfg(not(feature = "tracing-loki"))]
52        tracing::warn!(
53            "`tracing-loki` is unavailable, forest binaries need to be recompiled with `tracing-loki` feature"
54        );
55
56        #[cfg(feature = "tracing-loki")]
57        {
58            use anyhow::Context as _;
59            let (layer, task) = tracing_loki::layer(
60                tracing_loki::url::Url::parse(&opts.loki_endpoint).with_context(|| {
61                    format!("Unable to parse loki endpoint {}", opts.loki_endpoint)
62                })?,
63                vec![(
64                    "host".into(),
65                    gethostname::gethostname()
66                        .to_str()
67                        .unwrap_or_default()
68                        .into(),
69                )]
70                .into_iter()
71                .collect(),
72                Default::default(),
73            )
74            .context("Unable to create loki layer")?;
75            background_tasks.push(Box::pin(task));
76            layers.push(Box::new(
77                layer.with_filter(tracing_subscriber::filter::LevelFilter::INFO),
78            ));
79        }
80    }
81
82    tracing_subscriber::registry().with(layers).init();
83    Ok(background_tasks)
84}
85
86// Log warnings to stderr
87pub fn setup_minimal_logger() {
88    tracing_subscriber::registry()
89        .with(
90            tracing_subscriber::fmt::Layer::new()
91                .with_ansi(LoggingColor::Auto.coloring_enabled())
92                .with_writer(std::io::stderr)
93                .with_filter(get_env_filter(default_tool_filter())),
94        )
95        .init();
96}
97
98/// Returns an [`EnvFilter`] according to the `RUST_LOG` environment variable, or a default
99/// - see [`default_env_filter`] and [`default_tool_filter`]
100///
101/// Note that [`tracing_subscriber::filter::Builder`] only allows a single default directive,
102/// whereas we want to provide multiple.
103/// See also <https://github.com/tokio-rs/tracing/blob/27f688efb72316a26f3ec1f952c82626692c08ff/tracing-subscriber/src/filter/env/builder.rs#L189-L194>
104fn get_env_filter(def: EnvFilter) -> EnvFilter {
105    use std::env::{
106        self,
107        VarError::{NotPresent, NotUnicode},
108    };
109    match env::var(tracing_subscriber::EnvFilter::DEFAULT_ENV) {
110        Ok(s) => EnvFilter::new(s),
111        Err(NotPresent) => def,
112        Err(NotUnicode(_)) => EnvFilter::default(),
113    }
114}
115
116fn default_env_filter() -> EnvFilter {
117    let default_directives = [
118        "info",
119        "bellperson::groth16::aggregate::verify=warn",
120        "axum=warn",
121        "filecoin_proofs=warn",
122        "libp2p_bitswap=off",
123        "libp2p_gossipsub=error",
124        "libp2p_kad=error",
125        "storage_proofs_core=warn",
126        "tracing_loki=off",
127        "quinn_udp=error",
128    ];
129    EnvFilter::try_new(default_directives.join(",")).expect("hardcoded log directives are valid")
130}
131
132fn default_tool_filter() -> EnvFilter {
133    let default_directives = [
134        "info",
135        "bellperson::groth16::aggregate::verify=warn",
136        "storage_proofs_core=warn",
137        "axum=warn",
138        "filecoin_proofs=warn",
139        "forest::snapshot=info",
140        "forest::progress=info",
141        "libp2p_bitswap=off",
142        "tracing_loki=off",
143        "hickory_resolver::hosts=off",
144        "libp2p_swarm=off",
145    ];
146    EnvFilter::try_new(default_directives.join(",")).expect("hardcoded log directives are valid")
147}
148
149#[test]
150fn test_default_env_filter() {
151    let _did_not_panic = default_env_filter();
152}