From f266791142bcd77c77243c547e6354bfcfcd1d76 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Oct 2017 18:06:43 +0200 Subject: [PATCH] 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> so we can use the logging from multiple threads but also because we need to borrow mutably, so that bit changes whith this commit. --- doc/src/09020-changelog.md | 2 ++ lib/core/libimagrt/src/logger.rs | 56 +++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/doc/src/09020-changelog.md b/doc/src/09020-changelog.md index 79edabf9..974d94b6 100644 --- a/doc/src/09020-changelog.md +++ b/doc/src/09020-changelog.md @@ -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 for viewing the entry. This way one can view the entry in an editor or the browser or on the toaster. + * The logger is now able to handle multiple destinations (file and "-" for + stderr) ## 0.4.0 diff --git a/lib/core/libimagrt/src/logger.rs b/lib/core/libimagrt/src/logger.rs index 46086cfd..9bdc097f 100644 --- a/lib/core/libimagrt/src/logger.rs +++ b/lib/core/libimagrt/src/logger.rs @@ -20,6 +20,9 @@ use std::io::Write; use std::io::stderr; use std::collections::BTreeMap; +use std::sync::Arc; +use std::sync::Mutex; +use std::ops::Deref; use configuration::Configuration; use error::RuntimeErrorKind as EK; @@ -38,7 +41,7 @@ type Result = ::std::result::Result; enum LogDestination { Stderr, - File(::std::fs::File), + File(Arc>), } impl Default for LogDestination { @@ -163,22 +166,57 @@ impl Log for ImagLogger { .render(&format!("{}", record.level()), &data) .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 - .get(record.target()) + .get(record_target) .map(|module_setting| { let set = module_setting.enabled && module_setting.level.unwrap_or(self.global_loglevel) >= record.level(); 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(|| { - if self.global_loglevel >= record.level() { - // Yes, we log - let _ = write!(stderr(), "{}\n", logtext); + .unwrap_or_else(|| { + if self.global_loglevel >= record.level() { + // Yes, we log + 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 { .append(true) .create(true) .open(other) + .map(Mutex::new) + .map(Arc::new) .map(LogDestination::File) .chain_err(|| EK::IOLogFileOpenError) }