#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]
use lazy_static::lazy_static;
use log::{kv, Level, LevelFilter, Metadata};
use std::collections::{HashMap, VecDeque};
use std::iter::Iterator;
use std::sync::Mutex;
#[derive(Debug, PartialEq, Eq)]
pub struct Record {
args: String,
level: Level,
target: String,
key_values: HashMap<String, String>,
}
impl Record {
pub fn args(&self) -> &str {
&self.args
}
pub fn level(&self) -> Level {
self.level
}
pub fn target(&self) -> &str {
&self.target
}
pub fn key_values(&self) -> Vec<(String, String)> {
self.key_values
.iter()
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.collect()
}
}
lazy_static! {
static ref EVENTS: Mutex<VecDeque<Record>> = Mutex::new(VecDeque::new());
}
struct Visitor {
pairs: HashMap<String, String>,
}
impl<'kvs> kv::Visitor<'kvs> for Visitor {
fn visit_pair(&mut self, key: kv::Key<'kvs>, val: kv::Value<'kvs>) -> Result<(), kv::Error> {
self.pairs.insert(format!("{}", key), val.to_string());
Ok(())
}
}
#[derive(Debug)]
struct LoggerInternal;
impl log::Log for LoggerInternal {
fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
true
}
fn log(&self, record: &log::Record<'_>) {
if self.enabled(record.metadata()) {
let mut visitor = Visitor {
pairs: HashMap::new(),
};
record
.key_values()
.visit(&mut visitor)
.expect("could not visit kv pairs");
EVENTS.lock().unwrap().push_back(Record {
args: format!("{}", record.args()),
level: record.level(),
target: record.target().to_owned(),
key_values: visitor.pairs,
});
}
}
fn flush(&self) {}
}
#[derive(Debug)]
pub struct Logger;
impl Logger {
pub fn start() -> Self {
log::set_logger(&LoggerInternal).unwrap();
log::set_max_level(LevelFilter::Trace);
Self {}
}
#[must_use]
pub fn pop(&mut self) -> Option<Record> {
EVENTS.lock().unwrap().pop_front()
}
pub fn len(&mut self) -> usize {
EVENTS.lock().unwrap().len()
}
pub fn is_empty(&mut self) -> bool {
EVENTS.lock().unwrap().is_empty()
}
}
pub fn start() -> Logger {
Logger::start()
}
impl Iterator for Logger {
type Item = Record;
fn next(&mut self) -> Option<Self::Item> {
self.pop()
}
}