Skip to main content

ez_tui/utils/
logs.rs

1use log::LevelFilter as LogLevelFilter;
2use std::io;
3use std::io::Stdout;
4use tracing::info;
5use tracing_subscriber::filter::LevelFilter as TracingLevelFilter;
6use tracing_subscriber::fmt::format::{DefaultFields, Format};
7use tracing_subscriber::layer::SubscriberExt;
8use tracing_subscriber::reload::Handle;
9use tracing_subscriber::{Layer, Registry, fmt};
10use tui_logger::TuiTracingSubscriberLayer;
11
12/// A struct to allow togling on or off the stdout logging so we have it during args parsing and app exit but not during TUI execution.
13#[derive(Debug)]
14pub struct LogSwitcher {
15    level: TracingLevelFilter,
16    handle: Handle<
17        tracing_subscriber::filter::Filtered<
18            tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, fn() -> Stdout>,
19            TracingLevelFilter,
20            Registry,
21        >,
22        Registry,
23    >,
24}
25impl LogSwitcher {
26    /// Initializes the logging system with two layers:
27    /// * A layer that logs to stdout
28    /// * A layer that logs to the [`tui_logger`](https://docs.rs/tui_logger/latest/tui_logger/) crate
29    ///
30    /// Both those layers are filtered by the provided `tracing_level_filter`.
31    /// The `stdout` is later toggled on or off using the `toggle_env_logging` method so it is deactivated during TUI runtime.
32    pub(crate) fn initialize(tracing_level_filter: TracingLevelFilter) -> Self {
33        let log_level_filter = Self::convert_level(tracing_level_filter);
34
35        // STDOUT (optional)
36        let stdout_layer = fmt::layer()
37            .with_writer(io::stdout as fn() -> Stdout)
38            .with_target(true)
39            .with_filter(tracing_level_filter);
40        let (reload_layer, reload_handle) = tracing_subscriber::reload::Layer::new(stdout_layer);
41
42        let subscriber = Registry::default()
43            .with(reload_layer)
44            .with(TuiTracingSubscriberLayer.with_filter(tracing_level_filter));
45
46        tracing::subscriber::set_global_default(subscriber)
47            .expect("Failed to set global subscriber");
48
49        tui_logger::init_logger(log_level_filter).expect("Fails to init TUI logger");
50        tui_logger::set_default_level(log_level_filter);
51        tui_logger::set_level_for_target("mio::poll", LogLevelFilter::Warn);
52
53        info!("Logging initialized");
54        Self {
55            level: tracing_level_filter,
56            handle: reload_handle,
57        }
58    }
59    pub(crate) fn toggle_stdout_logging(&self, enable_stdout: bool) {
60        let new_filter = if enable_stdout {
61            self.level
62        } else {
63            TracingLevelFilter::OFF
64        };
65
66        self.handle
67            .modify(|layer| {
68                *layer.filter_mut() = new_filter;
69            })
70            .expect("Failed to modify layer");
71
72        info!(
73            "Env logging {}",
74            if enable_stdout { "enabled" } else { "disabled" }
75        );
76    }
77    fn convert_level(level: TracingLevelFilter) -> LogLevelFilter {
78        match level {
79            TracingLevelFilter::OFF => log::LevelFilter::Off,
80            TracingLevelFilter::ERROR => log::LevelFilter::Error,
81            TracingLevelFilter::WARN => log::LevelFilter::Warn,
82            TracingLevelFilter::INFO => log::LevelFilter::Info,
83            TracingLevelFilter::DEBUG => log::LevelFilter::Debug,
84            TracingLevelFilter::TRACE => log::LevelFilter::Trace,
85        }
86    }
87}