Skip to main content

tui_logger/logger/
api.rs

1use std::thread;
2#[cfg(feature = "waiter")]
3use std::time::Duration;
4
5use crate::logger::fast_hash::fast_str_hash;
6use crate::CircularBuffer;
7use crate::TuiLoggerFile;
8use log::LevelFilter;
9use log::Record;
10use log::SetLoggerError;
11
12use crate::TUI_LOGGER;
13
14// Lots of boilerplate code, so that init_logger can return two error types...
15#[derive(Debug)]
16pub enum TuiLoggerError {
17    SetLoggerError(SetLoggerError),
18    ThreadError(std::io::Error),
19}
20impl std::error::Error for TuiLoggerError {
21    fn description(&self) -> &str {
22        match self {
23            TuiLoggerError::SetLoggerError(_) => "SetLoggerError",
24            TuiLoggerError::ThreadError(_) => "ThreadError",
25        }
26    }
27    fn cause(&self) -> Option<&dyn std::error::Error> {
28        match self {
29            TuiLoggerError::SetLoggerError(_) => None,
30            TuiLoggerError::ThreadError(err) => Some(err),
31        }
32    }
33}
34impl std::fmt::Display for TuiLoggerError {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            TuiLoggerError::SetLoggerError(err) => write!(f, "SetLoggerError({})", err),
38            TuiLoggerError::ThreadError(err) => write!(f, "ThreadError({})", err),
39        }
40    }
41}
42
43/// Init the logger.
44pub fn init_logger(max_level: LevelFilter) -> Result<(), TuiLoggerError> {
45    let join_handle = thread::Builder::new()
46        .name("tui-logger::move_events".into())
47        .spawn(|| {
48            let duration = std::time::Duration::from_millis(10);
49            loop {
50                thread::park_timeout(duration);
51                TUI_LOGGER.move_events();
52            }
53        })
54        .map_err(TuiLoggerError::ThreadError)?;
55    TUI_LOGGER.hot_log.lock().mover_thread = Some(join_handle);
56    if cfg!(feature = "tracing-support") {
57        set_default_level(max_level);
58        Ok(())
59    } else {
60        log::set_max_level(max_level);
61        log::set_logger(&*TUI_LOGGER).map_err(TuiLoggerError::SetLoggerError)
62    }
63}
64
65/// Set the depth of the hot buffer in order to avoid message loss.
66/// This is effective only after a call to move_events()
67pub fn set_hot_buffer_depth(depth: usize) {
68    TUI_LOGGER.inner.lock().hot_depth = depth;
69}
70
71/// Set the depth of the circular buffer in order to avoid message loss.
72/// This will delete all existing messages in the circular buffer.
73pub fn set_buffer_depth(depth: usize) {
74    TUI_LOGGER.inner.lock().events = CircularBuffer::new(depth);
75}
76
77/// Define filename and log formmating options for file dumping.
78pub fn set_log_file(file_options: TuiLoggerFile) {
79    TUI_LOGGER.inner.lock().dump = Some(file_options);
80}
81
82/// Set default levelfilter for unknown targets of the logger
83pub fn set_default_level(levelfilter: LevelFilter) {
84    TUI_LOGGER.hot_select.lock().default = levelfilter;
85    TUI_LOGGER.inner.lock().default = levelfilter;
86}
87
88/// Remove env filter - for debugging purposes
89pub fn remove_env_filter() {
90    TUI_LOGGER.hot_select.lock().filter = None;
91    TUI_LOGGER.inner.lock().filter = None;
92}
93
94fn set_env_filter(filter1: env_filter::Filter, filter2: env_filter::Filter) {
95    // Filter does not support Copy. In order to not unnecessary lock hot_select,
96    // we use a manual copy of the env filter.
97    TUI_LOGGER.hot_select.lock().filter = Some(filter1);
98    TUI_LOGGER.inner.lock().filter = Some(filter2);
99}
100
101/// Parse environment variable for env_filter
102pub fn set_env_filter_from_string(filterstring: &str) {
103    let mut builder1 = env_filter::Builder::new();
104    let mut builder2 = env_filter::Builder::new();
105
106    builder1.parse(filterstring);
107    builder2.parse(filterstring);
108
109    set_env_filter(builder1.build(), builder2.build());
110}
111
112/// Parse environment variable for env_filter
113pub fn set_env_filter_from_env(env_name: Option<&str>) {
114    let mut builder1 = env_filter::Builder::new();
115    let mut builder2 = env_filter::Builder::new();
116
117    // Parse a directives string from an environment variable
118    if let Ok(ref filter) = std::env::var(env_name.unwrap_or("RUST_LOG")) {
119        builder1.parse(filter);
120        builder2.parse(filter);
121
122        set_env_filter(builder1.build(), builder2.build());
123    }
124}
125
126/// Set levelfilter for a specific target in the logger
127pub fn set_level_for_target(target: &str, levelfilter: LevelFilter) {
128    let h = fast_str_hash(target);
129    TUI_LOGGER.inner.lock().targets.set(target, levelfilter);
130    let mut hs = TUI_LOGGER.hot_select.lock();
131    hs.hashtable.insert(h, levelfilter);
132}
133
134// Move events from the hot log to the main log
135pub fn move_events() {
136    TUI_LOGGER.move_events();
137}
138
139/// Block the calling thread until new log messages have been moved into
140/// the widget's main buffer, then return the new moved-index.
141///
142/// Available only when the `waiter` feature is enabled.
143#[cfg(feature = "waiter")]
144#[cfg_attr(docsrs, doc(cfg(feature = "waiter")))]
145pub fn wait() -> u64 {
146    TUI_LOGGER.waiter.wait()
147}
148
149/// Like `wait`, but blocks for at most `timeout`.
150///
151/// Returns `Some(new_index)` if the moved-index advances within the
152/// timeout, or `None` if the timeout expired.
153///
154/// Available only when the `waiter` feature is enabled.
155#[cfg(feature = "waiter")]
156#[cfg_attr(docsrs, doc(cfg(feature = "waiter")))]
157pub fn wait_timeout(timeout: Duration) -> Option<u64> {
158    TUI_LOGGER.waiter.wait_timeout(timeout)
159}
160
161/// A simple `Drain` to log any event directly.
162#[derive(Default)]
163pub struct Drain;
164
165impl Drain {
166    /// Create a new Drain
167    pub fn new() -> Self {
168        Drain
169    }
170    /// Log the given record to the main tui-logger
171    pub fn log(&self, record: &Record) {
172        TUI_LOGGER.raw_log(record)
173    }
174}