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 Mar 30, 2022
1 parent 52a3e69 commit 568ffff
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 48 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ harness = false

[features]
default = ["termcolor", "atty", "humantime", "regex"]
kv_unstable = ["log/kv_unstable"]
146 changes: 98 additions & 48 deletions src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ use std::io::prelude::*;
use std::rc::Rc;
use std::{fmt, io, mem};

#[cfg(feature = "kv_unstable")]
use log::kv;
use log::Record;

mod humantime;
Expand Down Expand Up @@ -144,6 +146,7 @@ pub(crate) struct Builder {
pub format_target: bool,
pub format_level: bool,
pub format_indent: Option<usize>,
pub format_key_values: bool,
pub custom_format: Option<FormatFn>,
pub format_suffix: &'static str,
built: bool,
Expand All @@ -157,6 +160,7 @@ impl Default for Builder {
format_target: true,
format_level: true,
format_indent: Some(4),
format_key_values: true,
custom_format: None,
format_suffix: "\n",
built: false,
Expand Down Expand Up @@ -190,6 +194,7 @@ impl Builder {
module_path: built.format_module_path,
target: built.format_target,
level: built.format_level,
key_values: built.format_key_values,
written_header_value: false,
indent: built.format_indent,
suffix: built.format_suffix,
Expand Down Expand Up @@ -217,6 +222,7 @@ struct DefaultFormat<'a> {
level: bool,
written_header_value: bool,
indent: Option<usize>,
key_values: bool,
buf: &'a mut Formatter,
suffix: &'a str,
}
Expand All @@ -226,6 +232,7 @@ impl<'a> DefaultFormat<'a> {
self.write_timestamp()?;
self.write_level(record)?;
self.write_module_path(record)?;
self.write_kv(record)?;
self.write_target(record)?;
self.finish_header()?;

Expand Down Expand Up @@ -389,6 +396,40 @@ impl<'a> DefaultFormat<'a> {
}
}
}

fn write_kv(&mut self, record: &Record) -> io::Result<()> {
#[cfg(feature = "kv_unstable")]
{
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)?;
}
kvs.visit(&mut KeyValueVisitor(self.buf))
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
#[cfg(not(feature = "kv_unstable"))]
{
// avoid dead code lints
let _ = record;
let _ = self.key_values;
Ok(())
}
}
}

#[cfg(feature = "kv_unstable")]
struct KeyValueVisitor<'a>(&'a mut Formatter);

#[cfg(feature = "kv_unstable")]
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(|e| e.into())
}
}

#[cfg(test)]
Expand Down Expand Up @@ -424,19 +465,24 @@ mod tests {
write_target("", fmt)
}

#[test]
fn format_with_header() {
fn formatter() -> Formatter {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
Formatter::new(&writer)
}

#[test]
fn format_with_header() {
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: true,
target: false,
level: true,
key_values: false,
written_header_value: false,
indent: None,
suffix: "\n",
Expand All @@ -448,17 +494,14 @@ mod tests {

#[test]
fn format_no_header() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: false,
target: false,
level: false,
key_values: false,
written_header_value: false,
indent: None,
suffix: "\n",
Expand All @@ -470,17 +513,14 @@ mod tests {

#[test]
fn format_indent_spaces() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: true,
target: false,
level: true,
key_values: false,
written_header_value: false,
indent: Some(4),
suffix: "\n",
Expand All @@ -492,17 +532,14 @@ mod tests {

#[test]
fn format_indent_zero_spaces() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: true,
target: false,
level: true,
key_values: false,
written_header_value: false,
indent: Some(0),
suffix: "\n",
Expand All @@ -514,17 +551,14 @@ mod tests {

#[test]
fn format_indent_spaces_no_header() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: false,
target: false,
level: false,
key_values: false,
written_header_value: false,
indent: Some(4),
suffix: "\n",
Expand All @@ -536,17 +570,14 @@ mod tests {

#[test]
fn format_suffix() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: false,
target: false,
level: false,
key_values: false,
written_header_value: false,
indent: None,
suffix: "\n\n",
Expand All @@ -558,17 +589,14 @@ mod tests {

#[test]
fn format_suffix_with_indent() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: false,
target: false,
level: false,
key_values: false,
written_header_value: false,
indent: Some(4),
suffix: "\n\n",
Expand All @@ -580,11 +608,7 @@ mod tests {

#[test]
fn format_target() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write_target(
"target",
Expand All @@ -593,6 +617,7 @@ mod tests {
module_path: true,
target: true,
level: true,
key_values: false,
written_header_value: false,
indent: None,
suffix: "\n",
Expand All @@ -605,17 +630,14 @@ mod tests {

#[test]
fn format_empty_target() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write(DefaultFormat {
timestamp: None,
module_path: true,
target: true,
level: true,
key_values: false,
written_header_value: false,
indent: None,
suffix: "\n",
Expand All @@ -627,11 +649,7 @@ mod tests {

#[test]
fn format_no_target() {
let writer = writer::Builder::new()
.write_style(WriteStyle::Never)
.build();

let mut f = Formatter::new(&writer);
let mut f = formatter();

let written = write_target(
"target",
Expand All @@ -640,6 +658,7 @@ mod tests {
module_path: true,
target: false,
level: true,
key_values: false,
written_header_value: false,
indent: None,
suffix: "\n",
Expand All @@ -649,4 +668,35 @@ mod tests {

assert_eq!("[INFO test::path] log\nmessage\n", written);
}

#[cfg(feature = "kv_unstable")]
#[test]
fn format_kv() {
let kvs = &[("a", 1u32), ("b", 2u32)][..];
let mut f = formatter();
let record = Record::builder()
.args(format_args!("log\nmessage"))
.level(Level::Info)
.module_path(Some("test::path"))
.key_values(&kvs)
.build();

let written = write_record(
record,
DefaultFormat {
timestamp: None,
module_path: false,
target: false,
level: true,
key_values: true,
written_header_value: false,
indent: None,
suffix: "\n",
buf: &mut f,
},
);

#[cfg(feature = "kv_unstable")]
assert_eq!("[INFO a=1 b=2] log\nmessage\n", written);
}
}
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ impl Builder {
self
}

/// Whether or not to write key-value pairs in the default format.
#[cfg(feature = "kv_unstable")]
pub fn format_key_values(&mut self, write: bool) -> &mut Self {
self.format.format_key_values = write;
self
}

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

0 comments on commit 568ffff

Please sign in to comment.