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}