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