feat: TUI logs view (#3064)
* fix: add logs page to TUI * chore: print panic traces to TUI logs * chore: stop and start tui nicely and a bit of refactoring * chore: rustfmt * chore: typo * chore: use sync_channel for logs * chore: don't try to unwrap err on try_send log message * chore: fix compiler/lint warnings * fix: Only create logs channel if TUI is enabled and resovle other small review comments * fix: wrap logs in TUI to fix window size * fix: debug and trace logs appear white in the TUI logs
This commit is contained in:
committed by
Quentin Le Sceller
parent
38e6497919
commit
8ce2bfda58
+1
-1
@@ -43,7 +43,7 @@ pub mod secp_static;
|
||||
pub use crate::secp_static::static_secp_instance;
|
||||
|
||||
pub mod types;
|
||||
pub use crate::types::{LogLevel, LoggingConfig, ZeroingString};
|
||||
pub use crate::types::ZeroingString;
|
||||
|
||||
pub mod macros;
|
||||
|
||||
|
||||
+102
-25
@@ -18,9 +18,7 @@ use std::ops::Deref;
|
||||
use backtrace::Backtrace;
|
||||
use std::{panic, thread};
|
||||
|
||||
use crate::types::{self, LogLevel, LoggingConfig};
|
||||
|
||||
use log::{LevelFilter, Record};
|
||||
use log::{Level, Record};
|
||||
use log4rs;
|
||||
use log4rs::append::console::ConsoleAppender;
|
||||
use log4rs::append::file::FileAppender;
|
||||
@@ -32,17 +30,12 @@ use log4rs::append::rolling_file::{
|
||||
use log4rs::append::Append;
|
||||
use log4rs::config::{Appender, Config, Root};
|
||||
use log4rs::encode::pattern::PatternEncoder;
|
||||
use log4rs::encode::writer::simple::SimpleWriter;
|
||||
use log4rs::encode::Encode;
|
||||
use log4rs::filter::{threshold::ThresholdFilter, Filter, Response};
|
||||
|
||||
fn convert_log_level(in_level: &LogLevel) -> LevelFilter {
|
||||
match *in_level {
|
||||
LogLevel::Info => LevelFilter::Info,
|
||||
LogLevel::Warning => LevelFilter::Warn,
|
||||
LogLevel::Debug => LevelFilter::Debug,
|
||||
LogLevel::Trace => LevelFilter::Trace,
|
||||
LogLevel::Error => LevelFilter::Error,
|
||||
}
|
||||
}
|
||||
use std::error::Error;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::SyncSender;
|
||||
|
||||
lazy_static! {
|
||||
/// Flag to observe whether logging was explicitly initialised (don't output otherwise)
|
||||
@@ -56,6 +49,57 @@ lazy_static! {
|
||||
|
||||
const LOGGING_PATTERN: &str = "{d(%Y%m%d %H:%M:%S%.3f)} {h({l})} {M} - {m}{n}";
|
||||
|
||||
/// 32 log files to rotate over by default
|
||||
const DEFAULT_ROTATE_LOG_FILES: u32 = 32 as u32;
|
||||
|
||||
/// Log Entry
|
||||
#[derive(Clone, Serialize, Debug)]
|
||||
pub struct LogEntry {
|
||||
/// The log message
|
||||
pub log: String,
|
||||
/// The log levelO
|
||||
pub level: Level,
|
||||
}
|
||||
|
||||
/// Logging config
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct LoggingConfig {
|
||||
/// whether to log to stdout
|
||||
pub log_to_stdout: bool,
|
||||
/// logging level for stdout
|
||||
pub stdout_log_level: Level,
|
||||
/// whether to log to file
|
||||
pub log_to_file: bool,
|
||||
/// log file level
|
||||
pub file_log_level: Level,
|
||||
/// Log file path
|
||||
pub log_file_path: String,
|
||||
/// Whether to append to log or replace
|
||||
pub log_file_append: bool,
|
||||
/// Size of the log in bytes to rotate over (optional)
|
||||
pub log_max_size: Option<u64>,
|
||||
/// Number of the log files to rotate over (optional)
|
||||
pub log_max_files: Option<u32>,
|
||||
/// Whether the tui is running (optional)
|
||||
pub tui_running: Option<bool>,
|
||||
}
|
||||
|
||||
impl Default for LoggingConfig {
|
||||
fn default() -> LoggingConfig {
|
||||
LoggingConfig {
|
||||
log_to_stdout: true,
|
||||
stdout_log_level: Level::Warn,
|
||||
log_to_file: true,
|
||||
file_log_level: Level::Info,
|
||||
log_file_path: String::from("grin.log"),
|
||||
log_file_append: true,
|
||||
log_max_size: Some(1024 * 1024 * 16), // 16 megabytes default
|
||||
log_max_files: Some(DEFAULT_ROTATE_LOG_FILES),
|
||||
tui_running: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This filter is rejecting messages that doesn't start with "grin"
|
||||
/// in order to save log space for only Grin-related records
|
||||
#[derive(Debug)]
|
||||
@@ -73,8 +117,32 @@ impl Filter for GrinFilter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ChannelAppender {
|
||||
output: Mutex<SyncSender<LogEntry>>,
|
||||
encoder: Box<dyn Encode>,
|
||||
}
|
||||
|
||||
impl Append for ChannelAppender {
|
||||
fn append(&self, record: &Record) -> Result<(), Box<dyn Error + Sync + Send>> {
|
||||
let mut writer = SimpleWriter(Vec::new());
|
||||
self.encoder.encode(&mut writer, record)?;
|
||||
|
||||
let log = String::from_utf8_lossy(writer.0.as_slice()).to_string();
|
||||
|
||||
let _ = self.output.lock().try_send(LogEntry {
|
||||
log,
|
||||
level: record.level(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
/// Initialize the logger with the given configuration
|
||||
pub fn init_logger(config: Option<LoggingConfig>) {
|
||||
pub fn init_logger(config: Option<LoggingConfig>, logs_tx: Option<mpsc::SyncSender<LogEntry>>) {
|
||||
if let Some(c) = config {
|
||||
let tui_running = c.tui_running.unwrap_or(false);
|
||||
if tui_running {
|
||||
@@ -86,8 +154,8 @@ pub fn init_logger(config: Option<LoggingConfig>) {
|
||||
let mut config_ref = LOGGING_CONFIG.lock();
|
||||
*config_ref = c.clone();
|
||||
|
||||
let level_stdout = convert_log_level(&c.stdout_log_level);
|
||||
let level_file = convert_log_level(&c.file_log_level);
|
||||
let level_stdout = c.stdout_log_level.to_level_filter();
|
||||
let level_file = c.file_log_level.to_level_filter();
|
||||
|
||||
// Determine minimum logging level for Root logger
|
||||
let level_minimum = if level_stdout > level_file {
|
||||
@@ -105,15 +173,26 @@ pub fn init_logger(config: Option<LoggingConfig>) {
|
||||
|
||||
let mut appenders = vec![];
|
||||
|
||||
if c.log_to_stdout && !tui_running {
|
||||
let filter = Box::new(ThresholdFilter::new(level_stdout));
|
||||
if tui_running {
|
||||
let channel_appender = ChannelAppender {
|
||||
encoder: Box::new(PatternEncoder::new(&LOGGING_PATTERN)),
|
||||
output: Mutex::new(logs_tx.unwrap()),
|
||||
};
|
||||
|
||||
appenders.push(
|
||||
Appender::builder()
|
||||
.filter(filter)
|
||||
.filter(Box::new(ThresholdFilter::new(level_stdout)))
|
||||
.filter(Box::new(GrinFilter))
|
||||
.build("tui", Box::new(channel_appender)),
|
||||
);
|
||||
root = root.appender("tui");
|
||||
} else if c.log_to_stdout {
|
||||
appenders.push(
|
||||
Appender::builder()
|
||||
.filter(Box::new(ThresholdFilter::new(level_stdout)))
|
||||
.filter(Box::new(GrinFilter))
|
||||
.build("stdout", Box::new(stdout)),
|
||||
);
|
||||
|
||||
root = root.appender("stdout");
|
||||
}
|
||||
|
||||
@@ -123,9 +202,7 @@ pub fn init_logger(config: Option<LoggingConfig>) {
|
||||
let filter = Box::new(ThresholdFilter::new(level_file));
|
||||
let file: Box<dyn Append> = {
|
||||
if let Some(size) = c.log_max_size {
|
||||
let count = c
|
||||
.log_max_files
|
||||
.unwrap_or_else(|| types::DEFAULT_ROTATE_LOG_FILES);
|
||||
let count = c.log_max_files.unwrap_or_else(|| DEFAULT_ROTATE_LOG_FILES);
|
||||
let roller = FixedWindowRoller::builder()
|
||||
.build(&format!("{}.{{}}.gz", c.log_file_path), count)
|
||||
.unwrap();
|
||||
@@ -188,13 +265,13 @@ pub fn init_test_logger() {
|
||||
}
|
||||
let mut logger = LoggingConfig::default();
|
||||
logger.log_to_file = false;
|
||||
logger.stdout_log_level = LogLevel::Debug;
|
||||
logger.stdout_log_level = Level::Debug;
|
||||
|
||||
// Save current logging configuration
|
||||
let mut config_ref = LOGGING_CONFIG.lock();
|
||||
*config_ref = logger;
|
||||
|
||||
let level_stdout = convert_log_level(&config_ref.stdout_log_level);
|
||||
let level_stdout = config_ref.stdout_log_level.to_level_filter();
|
||||
let level_minimum = level_stdout; // minimum logging level for Root logger
|
||||
|
||||
// Start logger
|
||||
|
||||
+1
-58
@@ -12,64 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Logging configuration types
|
||||
|
||||
/// Log level types
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum LogLevel {
|
||||
/// Error
|
||||
Error,
|
||||
/// Warning
|
||||
Warning,
|
||||
/// Info
|
||||
Info,
|
||||
/// Debug
|
||||
Debug,
|
||||
/// Trace
|
||||
Trace,
|
||||
}
|
||||
|
||||
/// 32 log files to rotate over by default
|
||||
pub const DEFAULT_ROTATE_LOG_FILES: u32 = 32 as u32;
|
||||
|
||||
/// Logging config
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct LoggingConfig {
|
||||
/// whether to log to stdout
|
||||
pub log_to_stdout: bool,
|
||||
/// logging level for stdout
|
||||
pub stdout_log_level: LogLevel,
|
||||
/// whether to log to file
|
||||
pub log_to_file: bool,
|
||||
/// log file level
|
||||
pub file_log_level: LogLevel,
|
||||
/// Log file path
|
||||
pub log_file_path: String,
|
||||
/// Whether to append to log or replace
|
||||
pub log_file_append: bool,
|
||||
/// Size of the log in bytes to rotate over (optional)
|
||||
pub log_max_size: Option<u64>,
|
||||
/// Number of the log files to rotate over (optional)
|
||||
pub log_max_files: Option<u32>,
|
||||
/// Whether the tui is running (optional)
|
||||
pub tui_running: Option<bool>,
|
||||
}
|
||||
|
||||
impl Default for LoggingConfig {
|
||||
fn default() -> LoggingConfig {
|
||||
LoggingConfig {
|
||||
log_to_stdout: true,
|
||||
stdout_log_level: LogLevel::Warning,
|
||||
log_to_file: true,
|
||||
file_log_level: LogLevel::Info,
|
||||
log_file_path: String::from("grin.log"),
|
||||
log_file_append: true,
|
||||
log_max_size: Some(1024 * 1024 * 16), // 16 megabytes default
|
||||
log_max_files: Some(DEFAULT_ROTATE_LOG_FILES),
|
||||
tui_running: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
//! Zeroing String
|
||||
|
||||
use std::ops::Deref;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
Reference in New Issue
Block a user