anvil_zksync_core/
observability.rs

1use anvil_zksync_types::LogLevel;
2use std::sync::{Arc, RwLock};
3use std::{fs::File, sync::Mutex};
4use tracing_subscriber::{
5    filter::LevelFilter, layer::SubscriberExt, reload, util::SubscriberInitExt, EnvFilter, Registry,
6};
7
8/// A sharable reference to the observability stack.
9#[derive(Debug, Clone)]
10pub struct Observability {
11    binary_names: Vec<String>,
12    reload_handle: reload::Handle<EnvFilter, Registry>,
13    /// Last directives used to reload the underlying `EnvFilter` instance.
14    last_directives: Arc<RwLock<String>>,
15}
16
17impl Observability {
18    /// Initialize the tracing subscriber.
19    pub fn init(
20        binary_names: Vec<String>,
21        log_level_filter: LevelFilter,
22        log_file: File,
23        disabled: bool,
24    ) -> Result<Self, anyhow::Error> {
25        let directives = binary_names
26            .iter()
27            .map(|x| format!("{}={}", x, log_level_filter.to_string().to_lowercase()))
28            .collect::<Vec<String>>()
29            .join(",");
30        let filter = if disabled {
31            EnvFilter::new("off")
32        } else {
33            Self::parse_filter(&directives)?
34        };
35        let (filter, reload_handle) = reload::Layer::<EnvFilter, Registry>::new(filter);
36
37        tracing_subscriber::registry()
38            .with(filter)
39            .with(
40                tracing_subscriber::fmt::layer().event_format(
41                    tracing_subscriber::fmt::format()
42                        .compact()
43                        .without_time()
44                        .with_target(false),
45                ),
46            )
47            .with(
48                tracing_subscriber::fmt::layer()
49                    .event_format(
50                        tracing_subscriber::fmt::format()
51                            .compact()
52                            .without_time()
53                            .with_target(false),
54                    )
55                    .with_writer(Mutex::new(log_file))
56                    .with_ansi(false),
57            )
58            .init();
59
60        Ok(Self {
61            binary_names,
62            reload_handle,
63            last_directives: Arc::new(RwLock::new(directives)),
64        })
65    }
66
67    /// Set the log level for the binary.
68    pub fn set_log_level(&self, level: LogLevel) -> anyhow::Result<()> {
69        let level = LevelFilter::from(level);
70        let directives = self
71            .binary_names
72            .join(&format!("={},", level.to_string().to_lowercase()));
73        let new_filter = Self::parse_filter(&directives)?;
74        self.reload_handle.reload(new_filter)?;
75        *self
76            .last_directives
77            .write()
78            .expect("Observability lock is poisoned") = directives;
79
80        Ok(())
81    }
82
83    /// Sets advanced logging directive.
84    /// Example:
85    ///     * "my_crate=debug"
86    ///     * "my_crate::module=trace"
87    ///     * "my_crate=debug,other_crate=warn"
88    pub fn set_logging(&self, directives: String) -> Result<(), anyhow::Error> {
89        let new_filter = Self::parse_filter(&directives)?;
90        self.reload_handle.reload(new_filter)?;
91        *self
92            .last_directives
93            .write()
94            .expect("Observability lock is poisoned") = directives;
95
96        Ok(())
97    }
98
99    /// Parses a directive and builds an [EnvFilter] from it.
100    /// Example:
101    ///     * "my_crate=debug"
102    ///     * "my_crate::module=trace"
103    ///     * "my_crate=debug,other_crate=warn"
104    fn parse_filter(directives: &str) -> Result<EnvFilter, anyhow::Error> {
105        let mut filter = EnvFilter::from_default_env();
106        for directive in directives.split(',') {
107            filter = filter.add_directive(directive.parse()?);
108        }
109
110        Ok(filter)
111    }
112
113    /// Enables logging with the latest used directives.
114    pub fn enable_logging(&self) -> Result<(), anyhow::Error> {
115        let last_directives = &*self
116            .last_directives
117            .read()
118            .expect("Observability lock is poisoned");
119        let new_filter = Self::parse_filter(last_directives)?;
120        self.reload_handle.reload(new_filter)?;
121
122        Ok(())
123    }
124
125    /// Disables all logging.
126    pub fn disable_logging(&self) -> Result<(), anyhow::Error> {
127        let new_filter = EnvFilter::new("off");
128        self.reload_handle.reload(new_filter)?;
129
130        Ok(())
131    }
132}