Skip to content

Commit

Permalink
Add support for Key-Value data in log records.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmccombs committed Jul 1, 2019
1 parent 89bcabf commit 2c6d7dd
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 4 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Expand Up @@ -19,12 +19,17 @@ members = [
]

[dependencies]
log = { version = "0.4", features = ["std"] }
# log = { version = "0.4", features = ["std"] }
regex = { version = "1.0.3", optional = true }
termcolor = { version = "1.0.2", optional = true }
humantime = { version = "1.1", optional = true }
atty = { version = "0.2.5", optional = true }

[dependencies.log]
git = 'https://github.com/rust-lang-nursery/log'
branch = 'master'
features = ['std', 'kv_unstable']

[[test]]
name = "regexp_filter"
harness = false
Expand Down
37 changes: 34 additions & 3 deletions src/fmt/mod.rs
Expand Up @@ -35,7 +35,7 @@ use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::Display;

use log::Record;
use log::{kv, Record};

pub(crate) mod writer;
mod humantime;
Expand Down Expand Up @@ -117,6 +117,7 @@ pub(crate) struct Builder {
pub default_format_timestamp_nanos: bool,
pub default_format_module_path: bool,
pub default_format_level: bool,
pub default_format_key_values: bool,
pub custom_format: Option<Box<Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>>,
built: bool,
}
Expand All @@ -128,6 +129,7 @@ impl Default for Builder {
default_format_timestamp_nanos: false,
default_format_module_path: true,
default_format_level: true,
default_format_key_values: true,
custom_format: None,
built: false,
}
Expand Down Expand Up @@ -158,6 +160,7 @@ impl Builder {
timestamp_nanos: built.default_format_timestamp_nanos,
module_path: built.default_format_module_path,
level: built.default_format_level,
key_values: built.default_format_key_values,
written_header_value: false,
buf,
};
Expand All @@ -182,6 +185,7 @@ struct DefaultFormat<'a> {
level: bool,
timestamp_nanos: bool,
written_header_value: bool,
key_values: bool,
buf: &'a mut Formatter,
}

Expand All @@ -190,6 +194,7 @@ impl<'a> DefaultFormat<'a> {
self.write_timestamp()?;
self.write_level(record)?;
self.write_module_path(record)?;
self.write_kv(record)?;
self.finish_header()?;

self.write_args(record)
Expand Down Expand Up @@ -289,6 +294,28 @@ impl<'a> DefaultFormat<'a> {
fn write_args(&mut self, record: &Record) -> io::Result<()> {
writeln!(self.buf, "{}", record.args())
}

fn write_kv(&mut self, record: &Record) -> io::Result<()> {
if !self.key_values {
return Ok(())
}
let kvs = record.key_values();
if !self.written_header_value && kvs.count() > 0 {
self.written_header_value = true;
let open_brace = self.subtle_style("[");
write!(self.buf, "{}", open_brace)?;
}
// Ideally we would be able to transport the original io::Error through the visit
kvs.visit(&mut KeyValueVisitor(self.buf)).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
}

struct KeyValueVisitor<'a>(&'a mut Formatter);

impl<'a, 'kvs> kv::Visitor<'kvs> for KeyValueVisitor<'a> {
fn visit_pair(&mut self, key: kv::Key<'kvs>, value: kv::Value<'kvs>) -> Result<(), kv::Error> {
write!(self.0, " {}={}", key, value).map_err(|_| kv::Error::msg("printing failed"))
}
}

#[cfg(test)]
Expand All @@ -299,9 +326,11 @@ mod tests {

fn write(fmt: DefaultFormat) -> String {
let buf = fmt.buf.buf.clone();
let kvs = &[("a", 1u32), ("b", 2u32)][..];

let record = Record::builder()
.args(format_args!("log message"))
.key_values(&kvs)
.level(Level::Info)
.file(Some("test.rs"))
.line(Some(144))
Expand All @@ -327,11 +356,12 @@ mod tests {
timestamp_nanos: false,
module_path: true,
level: true,
key_values: true,
written_header_value: false,
buf: &mut f,
});

assert_eq!("[INFO test::path] log message\n", written);
assert_eq!("[INFO test::path a=1 b=2] log message\n", written);
}

#[test]
Expand All @@ -347,10 +377,11 @@ mod tests {
timestamp_nanos: false,
module_path: false,
level: false,
key_values: false,
written_header_value: false,
buf: &mut f,
});

assert_eq!("log message\n", written);
}
}
}
6 changes: 6 additions & 0 deletions src/lib.rs
Expand Up @@ -528,6 +528,12 @@ impl Builder {
self
}

/// Whether or not to write key-value pairs in the default format.
pub fn default_format_key_values(&mut self, write: bool) -> &mut Self {
self.format.default_format_key_values = write;
self
}

/// Adds a directive to the filter for a specific module.
///
/// # Examples
Expand Down

0 comments on commit 2c6d7dd

Please sign in to comment.