Replace logging backend to log4rs and add log rotation (#1789)
* Replace logging backend to flexi-logger and add log rotation * Changed flexi_logger to log4rs * Disable logging level filtering in Root logger * Support different logging levels for file and stdout * Don't log messages from modules other than Grin-related * Fix formatting * Place backed up compressed log copies into log file directory * Increase default log file size to 16 MiB * Add comment to config file on log_max_size option
This commit is contained in:
+2
-3
@@ -13,9 +13,8 @@ lazy_static = "1"
|
||||
rand = "0.5"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
slog = { version = "~2.3", features = ["max_level_trace", "release_max_level_trace"] }
|
||||
slog-term = "~2.4"
|
||||
slog-async = "~2.3"
|
||||
log4rs = { version = "0.8.1", features = ["rolling_file_appender", "compound_policy", "size_trigger", "fixed_window_roller"] }
|
||||
log = "0.4"
|
||||
walkdir = "2"
|
||||
zip = "0.4"
|
||||
parking_lot = {version = "0.6"}
|
||||
|
||||
+3
-5
@@ -26,10 +26,8 @@ extern crate base64;
|
||||
extern crate byteorder;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_async;
|
||||
extern crate slog_term;
|
||||
|
||||
extern crate log;
|
||||
extern crate log4rs;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
@@ -48,7 +46,7 @@ pub extern crate secp256k1zkp as secp;
|
||||
|
||||
// Logging related
|
||||
pub mod logger;
|
||||
pub use logger::{init_logger, init_test_logger, LOGGER};
|
||||
pub use logger::{init_logger, init_test_logger};
|
||||
|
||||
// Static secp instance
|
||||
pub mod secp_static;
|
||||
|
||||
+119
-59
@@ -12,10 +12,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//! Logging wrapper to be used throughout all crates in the workspace
|
||||
use slog::{Discard, Drain, Duplicate, Level, LevelFilter, Logger};
|
||||
use slog_async;
|
||||
use slog_term;
|
||||
use std::fs::OpenOptions;
|
||||
use std::ops::Deref;
|
||||
use Mutex;
|
||||
|
||||
@@ -24,14 +20,27 @@ use std::{panic, thread};
|
||||
|
||||
use types::{LogLevel, LoggingConfig};
|
||||
|
||||
fn convert_log_level(in_level: &LogLevel) -> Level {
|
||||
use log::{LevelFilter, Record};
|
||||
use log4rs;
|
||||
use log4rs::append::console::ConsoleAppender;
|
||||
use log4rs::append::file::FileAppender;
|
||||
use log4rs::append::rolling_file::{
|
||||
policy::compound::roll::fixed_window::FixedWindowRoller,
|
||||
policy::compound::trigger::size::SizeTrigger, policy::compound::CompoundPolicy,
|
||||
RollingFileAppender,
|
||||
};
|
||||
use log4rs::append::Append;
|
||||
use log4rs::config::{Appender, Config, Root};
|
||||
use log4rs::encode::pattern::PatternEncoder;
|
||||
use log4rs::filter::{threshold::ThresholdFilter, Filter, Response};
|
||||
|
||||
fn convert_log_level(in_level: &LogLevel) -> LevelFilter {
|
||||
match *in_level {
|
||||
LogLevel::Info => Level::Info,
|
||||
LogLevel::Critical => Level::Critical,
|
||||
LogLevel::Warning => Level::Warning,
|
||||
LogLevel::Debug => Level::Debug,
|
||||
LogLevel::Trace => Level::Trace,
|
||||
LogLevel::Error => Level::Error,
|
||||
LogLevel::Info => LevelFilter::Info,
|
||||
LogLevel::Warning => LevelFilter::Warn,
|
||||
LogLevel::Debug => LevelFilter::Debug,
|
||||
LogLevel::Trace => LevelFilter::Trace,
|
||||
LogLevel::Error => LevelFilter::Error,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,60 +52,115 @@ lazy_static! {
|
||||
static ref TUI_RUNNING: Mutex<bool> = Mutex::new(false);
|
||||
/// Static Logging configuration, should only be set once, before first logging call
|
||||
static ref LOGGING_CONFIG: Mutex<LoggingConfig> = Mutex::new(LoggingConfig::default());
|
||||
}
|
||||
|
||||
/// And a static reference to the logger itself, accessible from all crates
|
||||
pub static ref LOGGER: Logger = {
|
||||
let was_init = WAS_INIT.lock().clone();
|
||||
let config = LOGGING_CONFIG.lock();
|
||||
let slog_level_stdout = convert_log_level(&config.stdout_log_level);
|
||||
let slog_level_file = convert_log_level(&config.file_log_level);
|
||||
if config.tui_running.is_some() && config.tui_running.unwrap() {
|
||||
let mut tui_running_ref = TUI_RUNNING.lock();
|
||||
*tui_running_ref = true;
|
||||
/// This filter is rejecting messages that doesn't start with "grin"
|
||||
/// in order to save log space for only Grin-related records
|
||||
#[derive(Debug)]
|
||||
struct GrinFilter;
|
||||
|
||||
impl Filter for GrinFilter {
|
||||
fn filter(&self, record: &Record) -> Response {
|
||||
if let Some(module_path) = record.module_path() {
|
||||
if module_path.starts_with("grin") {
|
||||
return Response::Neutral;
|
||||
}
|
||||
}
|
||||
|
||||
//Terminal output drain
|
||||
let terminal_decorator = slog_term::TermDecorator::new().build();
|
||||
let terminal_drain = slog_term::FullFormat::new(terminal_decorator).build().fuse();
|
||||
let terminal_drain = LevelFilter::new(terminal_drain, slog_level_stdout).fuse();
|
||||
let mut terminal_drain = slog_async::Async::new(terminal_drain).build().fuse();
|
||||
if !config.log_to_stdout || !was_init {
|
||||
terminal_drain = slog_async::Async::new(Discard{}).build().fuse();
|
||||
}
|
||||
|
||||
if config.log_to_file && was_init {
|
||||
//File drain
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.append(config.log_file_append)
|
||||
.truncate(false)
|
||||
.open(&config.log_file_path)
|
||||
.unwrap();
|
||||
|
||||
let file_decorator = slog_term::PlainDecorator::new(file);
|
||||
let file_drain = slog_term::FullFormat::new(file_decorator).build().fuse();
|
||||
let file_drain = LevelFilter::new(file_drain, slog_level_file).fuse();
|
||||
let file_drain_final = slog_async::Async::new(file_drain).build().fuse();
|
||||
|
||||
let composite_drain = Duplicate::new(terminal_drain, file_drain_final).fuse();
|
||||
|
||||
Logger::root(composite_drain, o!())
|
||||
} else {
|
||||
Logger::root(terminal_drain, o!())
|
||||
}
|
||||
};
|
||||
Response::Reject
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the logger with the given configuration
|
||||
pub fn init_logger(config: Option<LoggingConfig>) {
|
||||
if let Some(c) = config {
|
||||
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_minimum;
|
||||
|
||||
// Determine minimum logging level for Root logger
|
||||
if level_stdout > level_file {
|
||||
level_minimum = level_stdout;
|
||||
} else {
|
||||
level_minimum = level_file;
|
||||
}
|
||||
|
||||
// Start logger
|
||||
let stdout = ConsoleAppender::builder()
|
||||
.encoder(Box::new(PatternEncoder::default()))
|
||||
.build();
|
||||
|
||||
let mut root = Root::builder();
|
||||
|
||||
let mut appenders = vec![];
|
||||
|
||||
if c.log_to_stdout {
|
||||
let filter = Box::new(ThresholdFilter::new(level_stdout));
|
||||
appenders.push(
|
||||
Appender::builder()
|
||||
.filter(filter)
|
||||
.filter(Box::new(GrinFilter))
|
||||
.build("stdout", Box::new(stdout)),
|
||||
);
|
||||
|
||||
root = root.appender("stdout");
|
||||
}
|
||||
|
||||
if c.log_to_file {
|
||||
// If maximum log size is specified, use rolling file appender
|
||||
// or use basic one otherwise
|
||||
let filter = Box::new(ThresholdFilter::new(level_file));
|
||||
let file: Box<Append> = {
|
||||
if let Some(size) = c.log_max_size {
|
||||
let roller = FixedWindowRoller::builder()
|
||||
.build(&format!("{}.{{}}.gz", c.log_file_path), 32)
|
||||
.unwrap();
|
||||
let trigger = SizeTrigger::new(size);
|
||||
|
||||
let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller));
|
||||
|
||||
Box::new(
|
||||
RollingFileAppender::builder()
|
||||
.append(c.log_file_append)
|
||||
.encoder(Box::new(PatternEncoder::new("{d} {l} {M} - {m}{n}")))
|
||||
.build(c.log_file_path, Box::new(policy))
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
Box::new(
|
||||
FileAppender::builder()
|
||||
.append(c.log_file_append)
|
||||
.encoder(Box::new(PatternEncoder::new("{d} {l} {M} - {m}{n}")))
|
||||
.build(c.log_file_path)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
appenders.push(
|
||||
Appender::builder()
|
||||
.filter(filter)
|
||||
.filter(Box::new(GrinFilter))
|
||||
.build("file", file),
|
||||
);
|
||||
root = root.appender("file");
|
||||
}
|
||||
|
||||
let config = Config::builder()
|
||||
.appenders(appenders)
|
||||
.build(root.build(level_minimum))
|
||||
.unwrap();
|
||||
|
||||
let _ = log4rs::init_config(config).unwrap();
|
||||
|
||||
info!(
|
||||
"log4rs is initialized, file level: {:?}, stdout level: {:?}, min. level: {:?}",
|
||||
level_file, level_stdout, level_minimum
|
||||
);
|
||||
|
||||
// Logger configuration successfully injected into LOGGING_CONFIG...
|
||||
let mut was_init_ref = WAS_INIT.lock();
|
||||
*was_init_ref = true;
|
||||
// .. allow logging, having ensured that paths etc are immutable
|
||||
}
|
||||
send_panic_to_log();
|
||||
}
|
||||
@@ -134,7 +198,6 @@ fn send_panic_to_log() {
|
||||
match info.location() {
|
||||
Some(location) => {
|
||||
error!(
|
||||
LOGGER,
|
||||
"\nthread '{}' panicked at '{}': {}:{}{:?}\n\n",
|
||||
thread,
|
||||
msg,
|
||||
@@ -143,10 +206,7 @@ fn send_panic_to_log() {
|
||||
backtrace
|
||||
);
|
||||
}
|
||||
None => error!(
|
||||
LOGGER,
|
||||
"thread '{}' panicked at '{}'{:?}", thread, msg, backtrace
|
||||
),
|
||||
None => error!("thread '{}' panicked at '{}'{:?}", thread, msg, backtrace),
|
||||
}
|
||||
//also print to stderr
|
||||
let tui_running = TUI_RUNNING.lock().clone();
|
||||
|
||||
+4
-3
@@ -14,11 +14,9 @@
|
||||
|
||||
//! Logging configuration types
|
||||
|
||||
/// Log level types, as slog's don't implement serialize
|
||||
/// Log level types
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum LogLevel {
|
||||
/// Critical
|
||||
Critical,
|
||||
/// Error
|
||||
Error,
|
||||
/// Warning
|
||||
@@ -46,6 +44,8 @@ pub struct LoggingConfig {
|
||||
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>,
|
||||
/// Whether the tui is running (optional)
|
||||
pub tui_running: Option<bool>,
|
||||
}
|
||||
@@ -59,6 +59,7 @@ impl Default for LoggingConfig {
|
||||
file_log_level: LogLevel::Debug,
|
||||
log_file_path: String::from("grin.log"),
|
||||
log_file_append: true,
|
||||
log_max_size: Some(1024 * 1024 * 16), // 16 megabytes default
|
||||
tui_running: None,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user