cursive_logger_view/
lib.rs

1//! # A `FlexiLoggerView` for cursive
2//!
3//! This crate provides a new debug view for
4//! [gyscos/cursive](https://github.com/gyscos/cursive) using the
5//! [emabee/flexi_logger](https://github.com/emabee/flexi_logger) crate. This
6//! enables the `FlexiLoggerView` to respect the `RUST_LOG` environment variable
7//! as well as the `flexi_logger` configuration file. Have a look at the `demo`
8//! below to see how it looks.
9//!
10//! ## Using the `FlexiLoggerView`
11//!
12//! To create a `FlexiLoggerView` you first have to register the
13//! `boxed_flexi_log_writer(&cursive)` or `CursiveLogWriter::new(&cursive).into_boxed()` as a `LogTarget` in `flexi_logger`. After the
14//! `flexi_logger` has started, you may create a `FlexiLoggerView::new().wrap_scroll_view()` instance and
15//! add it to cursive.
16//!
17//! ```rust
18//! use cursive::{Cursive, CursiveExt};
19//! use cursive_logger_view::{CursiveLogWriter, FlexiLoggerView};
20//! use flexi_logger::Logger;
21//!     // we need to initialize cursive first, as the cursive-flexi-logger
22//!     // needs a cursive callback sink to notify cursive about screen refreshs
23//!     // when a new log message arrives
24//!     let mut siv = Cursive::default();
25//!
26//!     // Configure the flexi logger with environment-variable($RUST_LOG) or fallback to "trace" level
27//!     Logger::try_with_env_or_str("trace")
28//!         .expect("Could not create Logger from environment :(")
29//!         // Configure logging to both file and Cursive
30//!         .log_to_file_and_writer(
31//!             // File configuration: store logs in 'logs' directory without timestamps
32//!             flexi_logger::FileSpec::default()
33//!                 .directory("logs")
34//!                 .suppress_timestamp(),
35//!             // Create Cursive log writer and box it for dynamic dispatch
36//!             CursiveLogWriter::new(&siv)
37//!                 //// Optional format configuration (commented out example)
38//!                 // .with_format({
39//!                 //     use cursive_logger_view::LogItems::*;
40//!                 //     [Level, DateTime, ModLine, Message]
41//!                 //         .into_iter()
42//!                 //         .collect()
43//!                 // })
44//!                 // .with_time_format("%T%.6f".into())
45//!                 .into_boxed(),
46//!         )
47//!         .start()
48//!         .expect("failed to initialize logger!");
49//!
50//!     // Add the logger view to Cursive
51//!     siv.add_layer(
52//!         FlexiLoggerView::new()
53//!             // .with_indent(true)  // Optional indentation configuration
54//!             //
55//!             .wrap_scroll_view(),
56//!     );
57//!
58//!     log::info!("test log message");
59//!     // siv.run();
60//! ```
61//!
62//!
63//! ```rust
64//! use cursive::{Cursive, CursiveExt};
65//! use cursive_logger_view::{show_flexi_logger_debug_console, hide_flexi_logger_debug_console, toggle_flexi_logger_debug_console};
66//! use flexi_logger::Logger;
67//!     // we need to initialize cursive first, as the cursive-flexi-logger
68//!     // needs a cursive callback sink to notify cursive about screen refreshs
69//!     // when a new log message arrives
70//!     let mut siv = Cursive::default();
71//!
72//!     Logger::try_with_env_or_str("trace")
73//!         .expect("Could not create Logger from environment :(")
74//!         .log_to_file_and_writer(
75//!            flexi_logger::FileSpec::default()
76//!                 .directory("logs")
77//!                 .suppress_timestamp(),
78//!             cursive_logger_view::boxed_flexi_log_writer(&siv)
79//!         )
80//!         .start()
81//!         .expect("failed to initialize logger!");
82//!
83//!     siv.add_global_callback('~', toggle_flexi_logger_debug_console);  // Bind '~' key to show/hide debug console view
84//!     siv.add_global_callback('s', show_flexi_logger_debug_console);  // Bind 's' key to show debug console view
85//!     siv.add_global_callback('h', hide_flexi_logger_debug_console);  // Bind 'h' key to hide debug console view
86//!
87//!     log::info!("test log message");
88//!     // siv.run();
89//! ```
90mod formatter;
91mod log_buffer;
92pub mod toggle;
93mod view;
94
95use compact_str::CompactString;
96use cursive_core::{CbSink, Cursive};
97use flexi_logger::writers::LogWriter;
98use getset::WithSetters;
99use tap::Pipe;
100use tinyvec::TinyVec;
101
102const FLEXI_LOGGER_DEBUG_VIEW_NAME: &str = "_flexi_debug_view";
103
104/// The `FlexiLoggerView` displays log messages from the `cursive_flexi_logger` log target.
105///
106/// ```rust
107/// use cursive_logger_view::FlexiLoggerView;
108///
109/// FlexiLoggerView::new().with_indent(true);
110/// ```
111#[derive(Default, Debug, WithSetters)]
112pub struct FlexiLoggerView {
113    #[getset(set_with = "pub")]
114    pub indent: bool,
115}
116
117///Possible log items
118#[derive(Debug)]
119pub enum LogItems<'c> {
120    DateTime,
121    Thread,
122    ModLine,
123    File,
124    FileLine,
125    Level,
126    Message,
127    // ThreadLine,
128    Custom(&'c str),
129}
130
131impl Default for LogItems<'_> {
132    fn default() -> Self {
133        Self::Level
134    }
135}
136
137/// The `flexi_logger` `LogWriter` implementation for the `FlexiLoggerView`.
138///
139/// Use the `boxed_flexi_log_writer` or `CursiveLogWriter::new` function to create an instance of this struct.
140#[derive(Debug, WithSetters)]
141#[getset(set_with = "pub")]
142pub struct CursiveLogWriter<'fmt> {
143    sink: CbSink,
144    format: TinyVec<[LogItems<'fmt>; 8]>,
145    time_format: CompactString,
146}
147
148impl CursiveLogWriter<'_> {
149    pub fn new(siv: &Cursive) -> Self {
150        use crate::LogItems::{DateTime, Level, Message, ModLine};
151
152        Self {
153            sink: siv.cb_sink().clone(),
154            format: [DateTime, Level, ModLine, Message]
155                .into_iter()
156                .collect(),
157            time_format: "%T%.3f".pipe(CompactString::const_new),
158        }
159    }
160
161    pub fn into_boxed(self) -> Box<Self> {
162        // Box::new(self)
163        self.into()
164    }
165}
166
167/// Creates a new `LogWriter` instance for the `FlexiLoggerView`. Use this to
168/// register a cursive log writer in `flexi_logger`.
169///
170/// Although, it is safe to create multiple cursive log writers, it may not be
171/// what you want. Each instance of a cursive log writer replicates the log
172/// messages in to `FlexiLoggerView`. When registering multiple cursive log
173/// writer instances, a single log messages will be duplicated by each log
174/// writer.
175///
176/// # Registering the cursive log writer in `flexi_logger`
177///
178/// ```rust
179/// use cursive::{Cursive, CursiveExt};
180/// use flexi_logger::Logger;
181///
182///     // we need to initialize cursive first, as the cursive-flexi-logger
183///     // needs a cursive callback sink to notify cursive about screen refreshs
184///     // when a new log message arrives
185///     let mut siv = Cursive::default();
186///
187///     Logger::try_with_env_or_str("trace")
188///         .expect("Could not create Logger from environment :(")
189///         .log_to_file_and_writer(
190///            flexi_logger::FileSpec::default()
191///                 .directory("logs")
192///                 .suppress_timestamp(),
193///             cursive_logger_view::boxed_flexi_log_writer(&siv)
194///         )
195///         .start()
196///         .expect("failed to initialize logger!");
197/// ```
198pub fn boxed_flexi_log_writer(siv: &Cursive) -> Box<dyn LogWriter> {
199    CursiveLogWriter::new(siv) //
200        .pipe(Box::new)
201}