Add multi-destination logging to logger

The logger was not able to handle multiple destinations before. Now it
is possible for the logger.

The file must be held behind an Arc<Mutex<_>> so we can use the logging
from multiple threads but also because we need to borrow mutably, so
that bit changes whith this commit.
This commit is contained in:
Matthias Beyer 2017-10-08 18:06:43 +02:00
parent e5155cacca
commit f266791142
2 changed files with 50 additions and 8 deletions

View file

@ -25,6 +25,8 @@ This section contains the changelog from the last release to the next release.
* `imag-view` uses the configuration file now to find the command to call * `imag-view` uses the configuration file now to find the command to call
for viewing the entry. This way one can view the entry in an editor or the for viewing the entry. This way one can view the entry in an editor or the
browser or on the toaster. browser or on the toaster.
* The logger is now able to handle multiple destinations (file and "-" for
stderr)
## 0.4.0 ## 0.4.0

View file

@ -20,6 +20,9 @@
use std::io::Write; use std::io::Write;
use std::io::stderr; use std::io::stderr;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::ops::Deref;
use configuration::Configuration; use configuration::Configuration;
use error::RuntimeErrorKind as EK; use error::RuntimeErrorKind as EK;
@ -38,7 +41,7 @@ type Result<T> = ::std::result::Result<T, RE>;
enum LogDestination { enum LogDestination {
Stderr, Stderr,
File(::std::fs::File), File(Arc<Mutex<::std::fs::File>>),
} }
impl Default for LogDestination { impl Default for LogDestination {
@ -163,22 +166,57 @@ impl Log for ImagLogger {
.render(&format!("{}", record.level()), &data) .render(&format!("{}", record.level()), &data)
.unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e)); .unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e));
let log_to_destination = |d: &LogDestination| match d {
&LogDestination::Stderr => {
let _ = write!(stderr(), "{}\n", logtext);
},
&LogDestination::File(ref arc_mutex_logdest) => {
// if there is an error in the lock, we cannot do anything. So we ignore it here.
let _ = arc_mutex_logdest
.deref()
.lock()
.map(|mut logdest| {
write!(logdest, "{}\n", logtext)
});
}
};
// hack to get the right target configuration.
// If there is no element here, we use the empty string which automatically drops through
// to the unwrap_or_else() case
let record_target = record
.target()
.split("::")
.next()
.unwrap_or("");
self.module_settings self.module_settings
.get(record.target()) .get(record_target)
.map(|module_setting| { .map(|module_setting| {
let set = module_setting.enabled && let set = module_setting.enabled &&
module_setting.level.unwrap_or(self.global_loglevel) >= record.level(); module_setting.level.unwrap_or(self.global_loglevel) >= record.level();
if set { if set {
let _ = write!(stderr(), "{}\n", logtext); module_setting.destinations.as_ref().map(|destinations| for d in destinations {
// If there's an error, we cannot do anything, can we?
let _ = log_to_destination(&d);
});
for d in self.global_destinations.iter() {
// If there's an error, we cannot do anything, can we?
let _ = log_to_destination(&d);
}
} }
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
if self.global_loglevel >= record.level() { if self.global_loglevel >= record.level() {
// Yes, we log // Yes, we log
let _ = write!(stderr(), "{}\n", logtext); for d in self.global_destinations.iter() {
// If there's an error, we cannot do anything, can we?
let _ = log_to_destination(&d);
} }
}); }
});
} }
} }
@ -230,6 +268,8 @@ fn translate_destination(raw: &str) -> Result<LogDestination> {
.append(true) .append(true)
.create(true) .create(true)
.open(other) .open(other)
.map(Mutex::new)
.map(Arc::new)
.map(LogDestination::File) .map(LogDestination::File)
.chain_err(|| EK::IOLogFileOpenError) .chain_err(|| EK::IOLogFileOpenError)
} }