logging: Add ability to convert between log level name and slog level

Added new functions to convert to/from a log level name (like `debug`)
to/from the equivalent `slog::Level::Debug`.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
This commit is contained in:
James O. D. Hunt 2020-06-04 14:16:21 +01:00
parent e80124ec0f
commit 2e53d237ce

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
use slog::{BorrowedKV, Drain, Key, OwnedKV, OwnedKVList, Record, KV}; use slog::{o, record_static, BorrowedKV, Drain, Key, OwnedKV, OwnedKVList, Record, KV};
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
@ -11,6 +11,15 @@ use std::process;
use std::result; use std::result;
use std::sync::Mutex; use std::sync::Mutex;
const LOG_LEVELS: &[(&str, slog::Level)] = &[
("trace", slog::Level::Trace),
("debug", slog::Level::Debug),
("info", slog::Level::Info),
("warn", slog::Level::Warning),
("error", slog::Level::Error),
("critical", slog::Level::Critical),
];
// XXX: 'writer' param used to make testing possible. // XXX: 'writer' param used to make testing possible.
pub fn create_logger<W>(name: &str, source: &str, level: slog::Level, writer: W) -> slog::Logger pub fn create_logger<W>(name: &str, source: &str, level: slog::Level, writer: W) -> slog::Logger
where where
@ -41,7 +50,34 @@ where
) )
} }
pub fn get_log_levels() -> Vec<&'static str> {
let result: Vec<&str> = LOG_LEVELS.iter().map(|value| value.0).collect();
result
}
pub fn level_name_to_slog_level(level_name: &str) -> Result<slog::Level, String> {
for tuple in LOG_LEVELS {
if tuple.0 == level_name {
return Ok(tuple.1);
}
}
Err("invalid level name".to_string())
}
pub fn slog_level_to_level_name(level: slog::Level) -> Result<&'static str, &'static str> {
for tuple in LOG_LEVELS {
if tuple.1 == level {
return Ok(tuple.0);
}
}
Err("invalid slog level")
}
// Used to convert an slog::OwnedKVList into a hash map. // Used to convert an slog::OwnedKVList into a hash map.
#[derive(Debug)]
struct HashSerializer { struct HashSerializer {
fields: HashMap<String, String>, fields: HashMap<String, String>,
} }
@ -146,6 +182,12 @@ impl<D> RuntimeLevelFilter<D> {
level: Mutex::new(level), level: Mutex::new(level),
} }
} }
fn set_level(&self, level: slog::Level) {
let mut log_level = self.level.lock().unwrap();
*log_level = level;
}
} }
impl<D> Drain for RuntimeLevelFilter<D> impl<D> Drain for RuntimeLevelFilter<D>
@ -174,9 +216,149 @@ where
mod tests { mod tests {
use super::*; use super::*;
use serde_json::Value; use serde_json::Value;
use slog::info;
use std::io::prelude::*; use std::io::prelude::*;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
#[test]
fn test_get_log_levels() {
let expected = vec!["trace", "debug", "info", "warn", "error", "critical"];
let log_levels = get_log_levels();
assert_eq!(log_levels, expected);
}
#[test]
fn test_level_name_to_slog_level() {
#[derive(Debug)]
struct TestData<'a> {
name: &'a str,
result: Result<slog::Level, &'a str>,
}
let invalid_msg = "invalid level name";
let tests = &[
TestData {
name: "",
result: Err(invalid_msg),
},
TestData {
name: "foo",
result: Err(invalid_msg),
},
TestData {
name: "x",
result: Err(invalid_msg),
},
TestData {
name: ".",
result: Err(invalid_msg),
},
TestData {
name: "trace",
result: Ok(slog::Level::Trace),
},
TestData {
name: "debug",
result: Ok(slog::Level::Debug),
},
TestData {
name: "info",
result: Ok(slog::Level::Info),
},
TestData {
name: "warn",
result: Ok(slog::Level::Warning),
},
TestData {
name: "error",
result: Ok(slog::Level::Error),
},
TestData {
name: "critical",
result: Ok(slog::Level::Critical),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = level_name_to_slog_level(d.name);
let msg = format!("{}, result: {:?}", msg, result);
if d.result.is_ok() {
assert!(result.is_ok());
let result_level = result.unwrap();
let expected_level = d.result.unwrap();
assert!(result_level == expected_level, msg);
continue;
} else {
assert!(result.is_err(), msg);
}
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
let actual_error = format!("{}", result.unwrap_err());
assert!(actual_error == expected_error, msg);
}
}
#[test]
fn test_slog_level_to_level_name() {
#[derive(Debug)]
struct TestData<'a> {
level: slog::Level,
result: Result<&'a str, &'a str>,
}
let tests = &[
TestData {
level: slog::Level::Trace,
result: Ok("trace"),
},
TestData {
level: slog::Level::Debug,
result: Ok("debug"),
},
TestData {
level: slog::Level::Info,
result: Ok("info"),
},
TestData {
level: slog::Level::Warning,
result: Ok("warn"),
},
TestData {
level: slog::Level::Error,
result: Ok("error"),
},
TestData {
level: slog::Level::Critical,
result: Ok("critical"),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = slog_level_to_level_name(d.level);
let msg = format!("{}, result: {:?}", msg, result);
if d.result.is_ok() {
assert!(result == d.result, msg);
continue;
}
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
let actual_error = format!("{}", result.unwrap_err());
assert!(actual_error == expected_error, msg);
}
}
#[test] #[test]
fn test_create_logger_write_to_tmpfile() { fn test_create_logger_write_to_tmpfile() {
// Create a writer for the logger drain to use // Create a writer for the logger drain to use