summaryrefslogtreecommitdiffstats
path: root/lib/core/libimagrt/src/logger.rs
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-09-04 23:02:45 +0200
committerMatthias Beyer <mail@beyermatthias.de>2017-09-04 23:02:45 +0200
commitc115215fa4d4e460b08dd7854a5c711a3e819c80 (patch)
tree23a8be2e09100cefb7ed1577edb07af1b06ab5d6 /lib/core/libimagrt/src/logger.rs
parent13af22ac16717235043de25b67194992c0c3a846 (diff)
parent6d1dab31179eb4414b45d9a94f453833ddc558ce (diff)
Merge branch 'master' into libimagerror/integration
This merge solved a _LOT_ of conflicts and was a rather complicated one, as parts of the conflict-resolution involved rewriting of half the stuff. This merge commit fixes all the things so a `cargo check --all` succeeds, but I did not yet check whether tests run without failure.
Diffstat (limited to 'lib/core/libimagrt/src/logger.rs')
-rw-r--r--lib/core/libimagrt/src/logger.rs578
1 files changed, 505 insertions, 73 deletions
diff --git a/lib/core/libimagrt/src/logger.rs b/lib/core/libimagrt/src/logger.rs
index 992a9d94..8785e27b 100644
--- a/lib/core/libimagrt/src/logger.rs
+++ b/lib/core/libimagrt/src/logger.rs
@@ -19,115 +19,547 @@
use std::io::Write;
use std::io::stderr;
+use std::collections::BTreeMap;
+use configuration::Configuration;
+use error::RuntimeErrorKind as EK;
+use error::RuntimeError as RE;
+use error::ResultExt;
+use runtime::Runtime;
+
+use clap::ArgMatches;
use log::{Log, LogLevel, LogRecord, LogMetadata};
+use toml::Value;
+use toml_query::read::TomlValueReadExt;
+use handlebars::Handlebars;
+
+type ModuleName = String;
+type Result<T> = ::std::result::Result<T, RE>;
+
+enum LogDestination {
+ Stderr,
+ File(::std::fs::File),
+}
+
+impl Default for LogDestination {
+ fn default() -> LogDestination {
+ LogDestination::Stderr
+ }
+}
+
+struct ModuleSettings {
+ enabled: bool,
+ level: Option<LogLevel>,
-use ansi_term::Style;
-use ansi_term::Colour;
-use ansi_term::ANSIString;
+ #[allow(unused)]
+ destinations: Option<Vec<LogDestination>>,
+}
/// Logger implementation for `log` crate.
pub struct ImagLogger {
- prefix: String,
- dbg_fileline: bool,
- lvl: LogLevel,
- color_enabled: bool,
+ global_loglevel : LogLevel,
+
+ #[allow(unused)]
+ global_destinations : Vec<LogDestination>,
+ // global_format_trace : ,
+ // global_format_debug : ,
+ // global_format_info : ,
+ // global_format_warn : ,
+ // global_format_error : ,
+ module_settings : BTreeMap<ModuleName, ModuleSettings>,
+
+ handlebars: Handlebars,
}
impl ImagLogger {
/// Create a new ImagLogger object with a certain level
- pub fn new(lvl: LogLevel) -> ImagLogger {
- ImagLogger {
- prefix: "[imag]".to_owned(),
- dbg_fileline: true,
- lvl: lvl,
- color_enabled: true
+ pub fn new(matches: &ArgMatches, config: Option<&Configuration>) -> Result<ImagLogger> {
+ let mut handlebars = Handlebars::new();
+
+ handlebars.register_helper("black" , Box::new(self::template_helpers::ColorizeBlackHelper));
+ handlebars.register_helper("blue" , Box::new(self::template_helpers::ColorizeBlueHelper));
+ handlebars.register_helper("cyan" , Box::new(self::template_helpers::ColorizeCyanHelper));
+ handlebars.register_helper("green" , Box::new(self::template_helpers::ColorizeGreenHelper));
+ handlebars.register_helper("purple" , Box::new(self::template_helpers::ColorizePurpleHelper));
+ handlebars.register_helper("red" , Box::new(self::template_helpers::ColorizeRedHelper));
+ handlebars.register_helper("white" , Box::new(self::template_helpers::ColorizeWhiteHelper));
+ handlebars.register_helper("yellow" , Box::new(self::template_helpers::ColorizeYellowHelper));
+
+ handlebars.register_helper("underline" , Box::new(self::template_helpers::UnderlineHelper));
+ handlebars.register_helper("bold" , Box::new(self::template_helpers::BoldHelper));
+ handlebars.register_helper("blink" , Box::new(self::template_helpers::BlinkHelper));
+ handlebars.register_helper("strikethrough" , Box::new(self::template_helpers::StrikethroughHelper));
+
+ {
+ let fmt = try!(aggregate_global_format_trace(matches, config));
+ try!(handlebars.register_template_string("TRACE", fmt) // name must be uppercase
+ .chain_err(|| EK::TemplateStringRegistrationError));
+ }
+ {
+ let fmt = try!(aggregate_global_format_debug(matches, config));
+ try!(handlebars.register_template_string("DEBUG", fmt) // name must be uppercase
+ .chain_err(|| EK::TemplateStringRegistrationError));
+ }
+ {
+ let fmt = try!(aggregate_global_format_info(matches, config));
+ try!(handlebars.register_template_string("INFO", fmt) // name must be uppercase
+ .chain_err(|| EK::TemplateStringRegistrationError));
}
+ {
+ let fmt = try!(aggregate_global_format_warn(matches, config));
+ try!(handlebars.register_template_string("WARN", fmt) // name must be uppercase
+ .chain_err(|| EK::TemplateStringRegistrationError));
+ }
+ {
+ let fmt = try!(aggregate_global_format_error(matches, config));
+ try!(handlebars.register_template_string("ERROR", fmt) // name must be uppercase
+ .chain_err(|| EK::TemplateStringRegistrationError));
+ }
+
+ Ok(ImagLogger {
+ global_loglevel : try!(aggregate_global_loglevel(matches, config)),
+ global_destinations : try!(aggregate_global_destinations(matches, config)),
+ module_settings : try!(aggregate_module_settings(matches, config)),
+ handlebars : handlebars,
+ })
+ }
+
+ pub fn global_loglevel(&self) -> LogLevel {
+ self.global_loglevel
}
- /// Set debugging to include file and line
- pub fn with_dbg_file_and_line(mut self, b: bool) -> ImagLogger {
- self.dbg_fileline = b;
- self
+}
+
+impl Log for ImagLogger {
+
+ fn enabled(&self, metadata: &LogMetadata) -> bool {
+ metadata.level() <= self.global_loglevel
}
- /// Set debugging to include prefix
- pub fn with_prefix(mut self, pref: String) -> ImagLogger {
- self.prefix = pref;
- self
+ fn log(&self, record: &LogRecord) {
+ if record.location().module_path().starts_with("handlebars") {
+ // This is a ugly, yet necessary hack. When logging, we use handlebars for templating.
+ // But as the handlebars library itselfs logs via a normal logging macro ("debug!()"),
+ // we have a recursion in our chain.
+ //
+ // To prevent this recursion, we return here.
+ //
+ // (As of handlebars 0.29.0 - please check whether you can update handlebars if you see
+ // this. Hopefully the next version has a compiletime flag to disable logging)
+ return;
+ }
+
+ let mut data = BTreeMap::new();
+
+ {
+ data.insert("level", format!("{}", record.level()));
+ data.insert("module_path", String::from(record.location().module_path()));
+ data.insert("file", String::from(record.location().file()));
+ data.insert("line", format!("{}", record.location().line()));
+ data.insert("target", String::from(record.target()));
+ data.insert("message", format!("{}", record.args()));
+ }
+
+ let logtext = self
+ .handlebars
+ .render(&format!("{}", record.level()), &data)
+ .unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e));
+
+ self.module_settings
+ .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);
+ }
+ })
+ .unwrap_or_else(|| {
+ if self.global_loglevel >= record.level() {
+ // Yes, we log
+ let _ = write!(stderr(), "{}\n", logtext);
+ }
+ });
}
+}
- /// Set debugging to have color
- pub fn with_color(mut self, b: bool) -> ImagLogger {
- self.color_enabled = b;
- self
+fn match_log_level_str(s: &str) -> Result<LogLevel> {
+ match s {
+ "trace" => Ok(LogLevel::Trace),
+ "debug" => Ok(LogLevel::Debug),
+ "info" => Ok(LogLevel::Info),
+ "warn" => Ok(LogLevel::Warn),
+ "error" => Ok(LogLevel::Error),
+ _ => return Err(RE::from_kind(EK::InvalidLogLevelSpec)),
}
+}
- /// Helper function to colorize a string with a certain Style
- fn style_or_not(&self, c: Style, s: String) -> ANSIString {
- if self.color_enabled {
- c.paint(s)
- } else {
- ANSIString::from(s)
+fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<LogLevel>
+{
+ match config {
+ Some(cfg) => match cfg
+ .read("imag.logging.level")
+ .chain_err(|| EK::ConfigReadError)
+ {
+ Ok(Some(&Value::String(ref s))) => match_log_level_str(s),
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Ok(None) => Err(RE::from_kind(EK::GlobalLogLevelConfigMissing)),
+ Err(e) => Err(e)
+ },
+ None => {
+ if matches.is_present(Runtime::arg_debugging_name()) {
+ return Ok(LogLevel::Debug)
+ }
+
+ matches
+ .value_of(Runtime::arg_verbosity_name())
+ .map(match_log_level_str)
+ .unwrap_or(Ok(LogLevel::Info))
}
}
+}
+
+fn translate_destination(raw: &str) -> Result<LogDestination> {
+ use std::fs::OpenOptions;
- /// Helper function to colorize a string with a certain Color
- fn color_or_not(&self, c: Colour, s: String) -> ANSIString {
- if self.color_enabled {
- c.paint(s)
- } else {
- ANSIString::from(s)
+ match raw {
+ "-" => Ok(LogDestination::Stderr),
+ other => {
+ OpenOptions::new()
+ .append(true)
+ .create(true)
+ .open(other)
+ .map(LogDestination::File)
+ .chain_err(|| EK::IOLogFileOpenError)
}
}
+}
+
+fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
+ raw.iter()
+ .fold(Ok(vec![]), |acc, val| {
+ acc.and_then(|mut v| {
+ let dest = match *val {
+ Value::String(ref s) => try!(translate_destination(s)),
+ _ => return Err(RE::from_kind(EK::ConfigTypeError)),
+ };
+ v.push(dest);
+ Ok(v)
+ })
+ })
}
-impl Log for ImagLogger {
+fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<Vec<LogDestination>>
+{
- fn enabled(&self, metadata: &LogMetadata) -> bool {
- metadata.level() <= self.lvl
+ match config {
+ Some(cfg) => match cfg
+ .read("imag.logging.destinations")
+ .chain_err(|| EK::ConfigReadError)
+ {
+ Ok(Some(&Value::Array(ref a))) => translate_destinations(a),
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Ok(None) => Err(RE::from_kind(EK::GlobalDestinationConfigMissing)),
+ Err(e) => Err(e)
+ },
+ None => {
+ if let Some(values) = matches.value_of(Runtime::arg_logdest_name()) {
+ // parse logdest specification from commandline
+
+ values.split(",")
+ .fold(Ok(vec![]), move |acc, dest| {
+ acc.and_then(|mut v| {
+ v.push(try!(translate_destination(dest)));
+ Ok(v)
+ })
+ })
+ } else {
+ Ok(vec![ LogDestination::default() ])
+ }
+ }
+ }
+}
+
+#[inline]
+fn aggregate_global_format(
+ read_str: &str,
+ cli_match_name: &str,
+ error_kind_if_missing: EK,
+ matches: &ArgMatches,
+ config: Option<&Configuration>
+ )
+-> Result<String>
+{
+ match config {
+ Some(cfg) => match cfg
+ .read(read_str)
+ .chain_err(|| EK::ConfigReadError)
+ {
+ Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Ok(None) => Err(RE::from_kind(error_kind_if_missing)),
+ Err(e) => Err(e)
+ },
+ None => match matches.value_of(cli_match_name).map(String::from) {
+ Some(s) => Ok(s),
+ None => Err(RE::from_kind(error_kind_if_missing))
+ }
}
+}
- fn log(&self, record: &LogRecord) {
- use ansi_term::Colour::Red;
- use ansi_term::Colour::Yellow;
- use ansi_term::Colour::Cyan;
-
- if self.enabled(record.metadata()) {
- // TODO: This is just simple logging. Maybe we can enhance this lateron
- let loc = record.location();
- match record.metadata().level() {
- LogLevel::Debug => {
- let lvl = self.color_or_not(Cyan, format!("{}", record.level()));
- let args = self.color_or_not(Cyan, format!("{}", record.args()));
- if self.dbg_fileline {
- let file = self.color_or_not(Cyan, format!("{}", loc.file()));
- let ln = self.color_or_not(Cyan, format!("{}", loc.line()));
-
- writeln!(stderr(), "{}[{: <5}][{}][{: >5}]: {}", self.prefix, lvl, file, ln, args).ok();
- } else {
- writeln!(stderr(), "{}[{: <5}]: {}", self.prefix, lvl, args).ok();
- }
- },
- LogLevel::Warn | LogLevel::Error => {
- let lvl = self.style_or_not(Red.blink(), format!("{}", record.level()));
- let args = self.color_or_not(Red, format!("{}", record.args()));
+fn aggregate_global_format_trace(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<String>
+{
+ aggregate_global_format("imag.logging.format.trace",
+ Runtime::arg_override_trace_logging_format(),
+ EK::ConfigMissingLoggingFormatTrace,
+ matches,
+ config)
+}
- writeln!(stderr(), "{}[{: <5}]: {}", self.prefix, lvl, args).ok();
- },
- LogLevel::Info => {
- let lvl = self.color_or_not(Yellow, format!("{}", record.level()));
- let args = self.color_or_not(Yellow, format!("{}", record.args()));
+fn aggregate_global_format_debug(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<String>
+{
+ aggregate_global_format("imag.logging.format.debug",
+ Runtime::arg_override_debug_logging_format(),
+ EK::ConfigMissingLoggingFormatDebug,
+ matches,
+ config)
+}
+
+fn aggregate_global_format_info(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<String>
+{
+ aggregate_global_format("imag.logging.format.info",
+ Runtime::arg_override_info_logging_format(),
+ EK::ConfigMissingLoggingFormatInfo,
+ matches,
+ config)
+}
+
+fn aggregate_global_format_warn(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<String>
+{
+ aggregate_global_format("imag.logging.format.warn",
+ Runtime::arg_override_warn_logging_format(),
+ EK::ConfigMissingLoggingFormatWarn,
+ matches,
+ config)
+}
+
+fn aggregate_global_format_error(matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<String>
+{
+ aggregate_global_format("imag.logging.format.error",
+ Runtime::arg_override_error_logging_format(),
+ EK::ConfigMissingLoggingFormatError,
+ matches,
+ config)
+}
- writeln!(stderr(), "{}[{: <5}]: {}", self.prefix, lvl, args).ok();
+fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Configuration>)
+ -> Result<BTreeMap<ModuleName, ModuleSettings>>
+{
+ match config {
+ Some(cfg) => match cfg
+ .read("imag.logging.modules")
+ .chain_err(|| EK::ConfigReadError)
+ {
+ Ok(Some(&Value::Table(ref t))) => {
+ // translate the module settings from the table `t`
+ let mut settings = BTreeMap::new();
+
+ for (module_name, v) in t {
+ let destinations = try!(match v.read("destinations") {
+ Ok(Some(&Value::Array(ref a))) => translate_destinations(a).map(Some),
+ Ok(None) => Ok(None),
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Err(e) => Err(e).chain_err(|| EK::TomlReadError),
+ });
+
+ let level = try!(match v.read("level") {
+ Ok(Some(&Value::String(ref s))) => match_log_level_str(s).map(Some),
+ Ok(None) => Ok(None),
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Err(e) => Err(e).chain_err(|| EK::TomlReadError),
+ });
+
+ let enabled = try!(match v.read("enabled") {
+ Ok(Some(&Value::Boolean(b))) => Ok(b),
+ Ok(None) => Ok(false),
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Err(e) => Err(e).chain_err(|| EK::TomlReadError),
+ });
+
+ let module_settings = ModuleSettings {
+ enabled: enabled,
+ level: level,
+ destinations: destinations,
+ };
+
+ // We don't care whether there was a value, we override it.
+ let _ = settings.insert(module_name.to_owned(), module_settings);
+ }
+
+ Ok(settings)
},
- _ => {
- writeln!(stderr(), "{}[{: <5}]: {}", self.prefix, record.level(), record.args()).ok();
+ Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError)),
+ Ok(None) => {
+ // No modules configured. This is okay!
+ Ok(BTreeMap::new())
},
- }
+ Err(e) => Err(e),
+ },
+ None => {
+ write!(stderr(), "No Configuration.").ok();
+ write!(stderr(), "cannot find module-settings for logging.").ok();
+ write!(stderr(), "Will use global defaults").ok();
+
+ Ok(BTreeMap::new())
+ }
+ }
+}
+
+mod template_helpers {
+ use handlebars::{Handlebars, HelperDef, JsonRender, RenderError, RenderContext, Helper};
+ use ansi_term::Colour;
+ use ansi_term::Style;
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeBlackHelper;
+
+ impl HelperDef for ColorizeBlackHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Black, h, hb, rc)
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeBlueHelper;
+
+ impl HelperDef for ColorizeBlueHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Blue, h, hb, rc)
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeCyanHelper;
+
+ impl HelperDef for ColorizeCyanHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Cyan, h, hb, rc)
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeGreenHelper;
+
+ impl HelperDef for ColorizeGreenHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Green, h, hb, rc)
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizePurpleHelper;
+
+ impl HelperDef for ColorizePurpleHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Purple, h, hb, rc)
}
}
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeRedHelper;
+
+ impl HelperDef for ColorizeRedHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Red, h, hb, rc)
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeWhiteHelper;
+
+ impl HelperDef for ColorizeWhiteHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::White, h, hb, rc)
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct ColorizeYellowHelper;
+
+ impl HelperDef for ColorizeYellowHelper {
+ fn call(&self, h: &Helper, hb: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ colorize(Colour::Yellow, h, hb, rc)
+ }
+ }
+
+ fn colorize(color: Colour, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+ let p = try!(h.param(0).ok_or(RenderError::new("Too few arguments")));
+
+ try!(write!(rc.writer(), "{}", color.paint(p.value().render())));
+ Ok(())
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct UnderlineHelper;
+
+ impl HelperDef for UnderlineHelper {
+ fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(),
+ RenderError> {
+ let p = try!(h.param(0).ok_or(RenderError::new("Too few arguments")));
+ let s = Style::new().underline();
+ try!(write!(rc.writer(), "{}", s.paint(p.value().render())));
+ Ok(())
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct BoldHelper;
+
+ impl HelperDef for BoldHelper {
+ fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(),
+ RenderError> {
+ let p = try!(h.param(0).ok_or(RenderError::new("Too few arguments")));
+ let s = Style::new().bold();
+ try!(write!(rc.writer(), "{}", s.paint(p.value().render())));
+ Ok(())
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct BlinkHelper;
+
+ impl HelperDef for BlinkHelper {
+ fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(),
+ RenderError> {
+ let p = try!(h.param(0).ok_or(RenderError::new("Too few arguments")));
+ let s = Style::new().blink();
+ try!(write!(rc.writer(), "{}", s.paint(p.value().render())));
+ Ok(())
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ pub struct StrikethroughHelper;
+
+ impl HelperDef for StrikethroughHelper {
+ fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(),
+ RenderError> {
+ let p = try!(h.param(0).ok_or(RenderError::new("Too few arguments")));
+ let s = Style::new().strikethrough();
+ try!(write!(rc.writer(), "{}", s.paint(p.value().render())));
+ Ok(())
+ }
+ }
+
}