mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-02 01:11:09 +03:00
2b2e13be63
`Async<Discard>` means "drain that will send each logging record to an async thread, that will discard it". This means logging record is sent to another thread for no reason.
158 lines
5 KiB
Rust
158 lines
5 KiB
Rust
// Copyright 2018 The Grin Developers
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Logging wrapper to be used throughout all crates in the workspace
|
|
use std::fs::OpenOptions;
|
|
use std::sync::Mutex;
|
|
use std::ops::Deref;
|
|
use slog::{Discard, Drain, Duplicate, Level, LevelFilter, Logger};
|
|
use slog_term;
|
|
use slog_async;
|
|
|
|
use backtrace::Backtrace;
|
|
use std::{panic, thread};
|
|
|
|
use types::{LogLevel, LoggingConfig};
|
|
|
|
fn convert_log_level(in_level: &LogLevel) -> Level {
|
|
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,
|
|
}
|
|
}
|
|
|
|
lazy_static! {
|
|
/// Flag to observe whether logging was explicitly initialised (don't output otherwise)
|
|
static ref WAS_INIT: Mutex<bool> = Mutex::new(false);
|
|
/// Flag to observe whether tui is running, and we therefore don't want to attempt to write
|
|
/// panics to stdout
|
|
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().unwrap().clone();
|
|
let config = LOGGING_CONFIG.lock().unwrap();
|
|
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().unwrap();
|
|
*tui_running_ref = true;
|
|
}
|
|
|
|
//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!())
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Initialises 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().unwrap();
|
|
*config_ref = c.clone();
|
|
// Logger configuration successfully injected into LOGGING_CONFIG...
|
|
let mut was_init_ref = WAS_INIT.lock().unwrap();
|
|
*was_init_ref = true;
|
|
// .. allow logging, having ensured that paths etc are immutable
|
|
}
|
|
send_panic_to_log();
|
|
}
|
|
|
|
/// Initializes the logger for unit and integration tests
|
|
pub fn init_test_logger() {
|
|
let mut was_init_ref = WAS_INIT.lock().unwrap();
|
|
if *was_init_ref.deref() {
|
|
return;
|
|
}
|
|
let mut config_ref = LOGGING_CONFIG.lock().unwrap();
|
|
*config_ref = LoggingConfig::default();
|
|
*was_init_ref = true;
|
|
send_panic_to_log();
|
|
}
|
|
|
|
/// hook to send panics to logs as well as stderr
|
|
fn send_panic_to_log() {
|
|
panic::set_hook(Box::new(|info| {
|
|
let backtrace = Backtrace::new();
|
|
|
|
let thread = thread::current();
|
|
let thread = thread.name().unwrap_or("unnamed");
|
|
|
|
let msg = match info.payload().downcast_ref::<&'static str>() {
|
|
Some(s) => *s,
|
|
None => match info.payload().downcast_ref::<String>() {
|
|
Some(s) => &**s,
|
|
None => "Box<Any>",
|
|
},
|
|
};
|
|
|
|
match info.location() {
|
|
Some(location) => {
|
|
error!(
|
|
LOGGER,
|
|
"\nthread '{}' panicked at '{}': {}:{}{:?}\n\n",
|
|
thread,
|
|
msg,
|
|
location.file(),
|
|
location.line(),
|
|
backtrace
|
|
);
|
|
}
|
|
None => error!(
|
|
LOGGER,
|
|
"thread '{}' panicked at '{}'{:?}", thread, msg, backtrace
|
|
),
|
|
}
|
|
//also print to stderr
|
|
let tui_running = TUI_RUNNING.lock().unwrap().clone();
|
|
if !tui_running {
|
|
eprintln!(
|
|
"Thread '{}' panicked with message:\n\"{}\"\nSee grin.log for further details.",
|
|
thread, msg
|
|
);
|
|
}
|
|
}));
|
|
}
|