1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Prints error messages and exceptional states.
#[cfg(feature = "message-box")]
use crate::alert_service::AlertService;
#[cfg(feature = "message-box")]
use crate::alert_service::MESSAGE_CHANNEL;
#[cfg(feature = "message-box")]
use crate::config::ARGS;
#[cfg(feature = "message-box")]
use crate::config::RUNS_ON_CONSOLE;
use lazy_static::lazy_static;
use log::LevelFilter;
use log::{Level, Metadata, Record};
use std::sync::RwLock;
pub struct AppLogger {
/// If `true`, all future log events will trigger the opening of a popup
/// alert window. Otherwise only `Level::Error` will do.
popup_always_enabled: RwLock<bool>,
}
lazy_static! {
static ref APP_LOGGER: AppLogger = AppLogger {
popup_always_enabled: RwLock::new(false)
};
}
/// Initialize logger.
impl AppLogger {
#[inline]
pub fn init() {
// Setup the `AlertService`
#[cfg(feature = "message-box")]
AlertService::init();
// Setup console logger.
log::set_logger(&*APP_LOGGER).unwrap();
log::set_max_level(LevelFilter::Error);
}
/// Sets the maximum level debug events must have to be logged.
#[allow(dead_code)]
pub fn set_max_level(level: LevelFilter) {
log::set_max_level(level);
}
/// If called with `true`, all debug events will also trigger the appearance of
/// a popup alert window.
#[allow(dead_code)]
pub fn set_popup_always_enabled(popup: bool) {
// This blocks if ever another thread wants to write. As we are the only ones to write
// here, this lock can never get poisoned and we will can safely `unwrap()` here.
let mut lock = APP_LOGGER.popup_always_enabled.write().unwrap();
*lock = popup;
}
/// Blocks until the `AlertService` is not busy any more.
/// This should be executed before quitting the application
/// because there might be still queued error messages
/// the uses has not seen yet.
pub fn flush() {
#[cfg(feature = "message-box")]
if !*RUNS_ON_CONSOLE && !ARGS.batch {
// If ever there is still a message window open, this will block.
AlertService::flush();
}
}
}
/// Trait defining the logging format and destination.
impl log::Log for AppLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Trace
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
// Log this to `stderr`.
eprintln!("*** {}: {}", record.level(), record.args());
// Eventually also log as popup alert window.
#[cfg(feature = "message-box")]
if !*RUNS_ON_CONSOLE
&& !ARGS.batch
&& ((record.metadata().level() == LevelFilter::Error)
// This lock can never get poisoned, so `unwrap()` is safe here.
|| *(APP_LOGGER.popup_always_enabled.read().unwrap()))
{
let msg = if record.metadata().level() == Level::Error {
format!(
"{}:\n{}",
record.level(),
&AlertService::format_error(&record.args().to_string())
)
} else {
format!("{}:\n{}", record.level(), &record.args().to_string())
};
let (tx, _) = &*MESSAGE_CHANNEL;
let tx = tx.clone();
tx.send(msg).unwrap();
};
}
}
fn flush(&self) {}
}