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}