Skip to content

Commit 66a66cf

Browse files
committed
feat(kv): Change KV format to allow arbitrary function.
1 parent 5277afa commit 66a66cf

File tree

3 files changed

+76
-136
lines changed

3 files changed

+76
-136
lines changed

src/fmt/kv.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::io::{self, Write};
2+
3+
use super::Formatter;
4+
use log::kv::{source::Source, Error, Key, Value, Visitor};
5+
6+
/// Format function for serializing key/value pairs
7+
///
8+
/// This trait determines how key/value pairs for structured logs are serialized within the default
9+
/// format.
10+
pub(crate) type KvFormatFn = dyn Fn(&mut Formatter, &dyn Source) -> io::Result<()> + Sync + Send;
11+
12+
/// Null Key Value Format
13+
///
14+
/// This function is intended to be passed to [`env_logger::Builder::format_key_values`].
15+
///
16+
/// This key value format simply ignores any key/value fields and doesn't include them in the
17+
/// output.
18+
pub fn hidden_kv_format(_formatter: &mut Formatter, _fields: &dyn Source) -> io::Result<()> {
19+
Ok(())
20+
}
21+
22+
/// Defualt Key Value Format
23+
///
24+
/// This function is intended to be passed to [`env_logger::Builder::format_key_values`].
25+
///
26+
/// This is the default key/value format. Which uses an "=" as the separator between the key and
27+
/// value and a " " between each pair.
28+
///
29+
/// For example: `ip=127.0.0.1 port=123456 path=/example`
30+
pub fn default_kv_format(formatter: &mut Formatter, fields: &dyn Source) -> io::Result<()> {
31+
fields
32+
.visit(&mut DefaultVisitor(formatter))
33+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
34+
}
35+
36+
struct DefaultVisitor<'a>(&'a mut Formatter);
37+
38+
impl<'a, 'kvs> Visitor<'kvs> for DefaultVisitor<'a> {
39+
fn visit_pair(&mut self, key: Key, value: Value<'kvs>) -> Result<(), Error> {
40+
// TODO: add styling
41+
// tracing-subscriber uses italic for the key and dimmed for the =
42+
write!(self.0, " {}={}", key, value)?;
43+
Ok(())
44+
}
45+
}

src/fmt/mod.rs

Lines changed: 26 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,18 @@ use std::io::prelude::*;
5252
use std::rc::Rc;
5353
use std::{fmt, io, mem};
5454

55-
#[cfg(feature = "unstable-kv")]
56-
use log::kv;
5755
use log::Record;
5856

5957
#[cfg(feature = "humantime")]
6058
mod humantime;
59+
#[cfg(feature = "unstable-kv")]
60+
mod kv;
6161
pub(crate) mod writer;
6262

6363
#[cfg(feature = "humantime")]
6464
pub use self::humantime::Timestamp;
65+
#[cfg(feature = "unstable-kv")]
66+
pub use self::kv::*;
6567
pub use self::writer::glob::*;
6668

6769
use self::writer::{Buffer, Writer};
@@ -200,7 +202,7 @@ pub(crate) struct Builder {
200202
pub custom_format: Option<FormatFn>,
201203
pub format_suffix: &'static str,
202204
#[cfg(feature = "unstable-kv")]
203-
pub key_value_style: KVStyle,
205+
pub kv_format: Option<Box<KvFormatFn>>,
204206
built: bool,
205207
}
206208

@@ -234,7 +236,7 @@ impl Builder {
234236
indent: built.format_indent,
235237
suffix: built.format_suffix,
236238
#[cfg(feature = "unstable-kv")]
237-
key_value_style: built.key_value_style,
239+
kv_format: built.kv_format.as_deref().unwrap_or(&default_kv_format),
238240
buf,
239241
};
240242

@@ -255,7 +257,7 @@ impl Default for Builder {
255257
custom_format: None,
256258
format_suffix: "\n",
257259
#[cfg(feature = "unstable-kv")]
258-
key_value_style: KVStyle::Multiline,
260+
kv_format: None,
259261
built: false,
260262
}
261263
}
@@ -279,7 +281,7 @@ struct DefaultFormat<'a> {
279281
buf: &'a mut Formatter,
280282
suffix: &'a str,
281283
#[cfg(feature = "unstable-kv")]
282-
key_value_style: KVStyle,
284+
kv_format: &'a KvFormatFn,
283285
}
284286

285287
impl<'a> DefaultFormat<'a> {
@@ -454,55 +456,8 @@ impl<'a> DefaultFormat<'a> {
454456

455457
#[cfg(feature = "unstable-kv")]
456458
fn write_kv(&mut self, record: &Record) -> io::Result<()> {
457-
match self.key_value_style {
458-
KVStyle::Hidden => Ok(()),
459-
KVStyle::Multiline => record
460-
.key_values()
461-
.visit(&mut KVMultilineVisitor(self))
462-
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)),
463-
KVStyle::Inline => {
464-
let kvs = record.key_values();
465-
if kvs.count() > 0 {
466-
write!(self.buf, " {}", self.subtle_style("{"))?;
467-
468-
kvs.visit(&mut KVInlineVisitor(self.buf))
469-
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
470-
write!(self.buf, "{}", self.subtle_style(" }"))?;
471-
}
472-
Ok(())
473-
}
474-
}
475-
}
476-
}
477-
478-
#[cfg(feature = "unstable-kv")]
479-
struct KVInlineVisitor<'a>(&'a mut Formatter);
480-
481-
#[cfg(feature = "unstable-kv")]
482-
impl<'a, 'kvs> kv::Visitor<'kvs> for KVInlineVisitor<'a> {
483-
fn visit_pair(&mut self, key: kv::Key<'kvs>, value: kv::Value<'kvs>) -> Result<(), kv::Error> {
484-
write!(self.0, " {}={}", key, value).map_err(|e| e.into())
485-
}
486-
}
487-
488-
#[cfg(feature = "unstable-kv")]
489-
struct KVMultilineVisitor<'a, 'fmt>(&'a mut DefaultFormat<'fmt>);
490-
491-
#[cfg(feature = "unstable-kv")]
492-
impl<'a, 'kvs, 'fmt> kv::Visitor<'kvs> for KVMultilineVisitor<'a, 'fmt> {
493-
fn visit_pair(&mut self, key: kv::Key<'kvs>, value: kv::Value<'kvs>) -> Result<(), kv::Error> {
494-
let indent = self.0.indent.unwrap_or(0);
495-
write!(
496-
self.0.buf,
497-
// Always use a newline here, because suffix could be something else
498-
// to separate entire records.
499-
"\n{:width$}{}: {}",
500-
"",
501-
key,
502-
value,
503-
width = indent
504-
)?;
505-
Ok(())
459+
let format = self.kv_format;
460+
format(self.buf, record.key_values())
506461
}
507462
}
508463

@@ -557,7 +512,7 @@ mod tests {
557512
target: false,
558513
level: true,
559514
#[cfg(feature = "unstable-kv")]
560-
key_value_style: KVStyle::Hidden,
515+
kv_format: &hidden_kv_format,
561516
written_header_value: false,
562517
indent: None,
563518
suffix: "\n",
@@ -577,7 +532,7 @@ mod tests {
577532
target: false,
578533
level: false,
579534
#[cfg(feature = "unstable-kv")]
580-
key_value_style: KVStyle::Hidden,
535+
kv_format: &hidden_kv_format,
581536
written_header_value: false,
582537
indent: None,
583538
suffix: "\n",
@@ -597,7 +552,7 @@ mod tests {
597552
target: false,
598553
level: true,
599554
#[cfg(feature = "unstable-kv")]
600-
key_value_style: KVStyle::Hidden,
555+
kv_format: &hidden_kv_format,
601556
written_header_value: false,
602557
indent: Some(4),
603558
suffix: "\n",
@@ -617,7 +572,7 @@ mod tests {
617572
target: false,
618573
level: true,
619574
#[cfg(feature = "unstable-kv")]
620-
key_value_style: KVStyle::Hidden,
575+
kv_format: &hidden_kv_format,
621576
written_header_value: false,
622577
indent: Some(0),
623578
suffix: "\n",
@@ -637,7 +592,7 @@ mod tests {
637592
target: false,
638593
level: false,
639594
#[cfg(feature = "unstable-kv")]
640-
key_value_style: KVStyle::Hidden,
595+
kv_format: &hidden_kv_format,
641596
written_header_value: false,
642597
indent: Some(4),
643598
suffix: "\n",
@@ -657,7 +612,7 @@ mod tests {
657612
target: false,
658613
level: false,
659614
#[cfg(feature = "unstable-kv")]
660-
key_value_style: KVStyle::Hidden,
615+
kv_format: &hidden_kv_format,
661616
written_header_value: false,
662617
indent: None,
663618
suffix: "\n\n",
@@ -677,7 +632,7 @@ mod tests {
677632
target: false,
678633
level: false,
679634
#[cfg(feature = "unstable-kv")]
680-
key_value_style: KVStyle::Hidden,
635+
kv_format: &hidden_kv_format,
681636
written_header_value: false,
682637
indent: Some(4),
683638
suffix: "\n\n",
@@ -699,7 +654,7 @@ mod tests {
699654
target: true,
700655
level: true,
701656
#[cfg(feature = "unstable-kv")]
702-
key_value_style: KVStyle::Hidden,
657+
kv_format: &hidden_kv_format,
703658
written_header_value: false,
704659
indent: None,
705660
suffix: "\n",
@@ -720,7 +675,7 @@ mod tests {
720675
target: true,
721676
level: true,
722677
#[cfg(feature = "unstable-kv")]
723-
key_value_style: KVStyle::Hidden,
678+
kv_format: &hidden_kv_format,
724679
written_header_value: false,
725680
indent: None,
726681
suffix: "\n",
@@ -742,7 +697,7 @@ mod tests {
742697
target: false,
743698
level: true,
744699
#[cfg(feature = "unstable-kv")]
745-
key_value_style: KVStyle::Hidden,
700+
kv_format: &hidden_kv_format,
746701
written_header_value: false,
747702
indent: None,
748703
suffix: "\n",
@@ -755,7 +710,7 @@ mod tests {
755710

756711
#[cfg(feature = "unstable-kv")]
757712
#[test]
758-
fn format_kv_trailer() {
713+
fn format_kv_default() {
759714
let kvs = &[("a", 1u32), ("b", 2u32)][..];
760715
let mut f = formatter();
761716
let record = Record::builder()
@@ -772,20 +727,20 @@ mod tests {
772727
module_path: false,
773728
target: false,
774729
level: true,
775-
key_value_style: KVStyle::Inline,
730+
kv_format: &default_kv_format,
776731
written_header_value: false,
777732
indent: None,
778733
suffix: "\n",
779734
buf: &mut f,
780735
},
781736
);
782737

783-
assert_eq!("[INFO ] log message { a=1 b=2 }\n", written);
738+
assert_eq!("[INFO ] log message a=1 b=2\n", written);
784739
}
785740

786741
#[cfg(feature = "unstable-kv")]
787742
#[test]
788-
fn format_kv_trailer_full() {
743+
fn format_kv_default_full() {
789744
let kvs = &[("a", 1u32), ("b", 2u32)][..];
790745
let mut f = formatter();
791746
let record = Record::builder()
@@ -805,77 +760,14 @@ mod tests {
805760
module_path: true,
806761
target: true,
807762
level: true,
808-
key_value_style: KVStyle::Inline,
763+
kv_format: &default_kv_format,
809764
written_header_value: false,
810765
indent: None,
811766
suffix: "\n",
812767
buf: &mut f,
813768
},
814769
);
815770

816-
assert_eq!(
817-
"[INFO test::path target] log\nmessage { a=1 b=2 }\n",
818-
written
819-
);
820-
}
821-
822-
#[cfg(feature = "unstable-kv")]
823-
#[test]
824-
fn format_kv_multiline() {
825-
let kvs = &[("a", 1u32), ("b", 2u32)][..];
826-
let mut f = formatter();
827-
let record = Record::builder()
828-
.args(format_args!("log\nmessage"))
829-
.level(Level::Info)
830-
.module_path(Some("test::path"))
831-
.key_values(&kvs)
832-
.build();
833-
834-
let written = write_record(
835-
record,
836-
DefaultFormat {
837-
timestamp: None,
838-
module_path: false,
839-
target: false,
840-
level: true,
841-
key_value_style: KVStyle::Multiline,
842-
written_header_value: false,
843-
indent: None,
844-
suffix: "\n",
845-
buf: &mut f,
846-
},
847-
);
848-
849-
assert_eq!("[INFO ] log\nmessage\na: 1\nb: 2\n", written);
850-
}
851-
852-
#[cfg(feature = "unstable-kv")]
853-
#[test]
854-
fn format_kv_multiline_indented() {
855-
let kvs = &[("a", 1u32), ("b", 2u32)][..];
856-
let mut f = formatter();
857-
let record = Record::builder()
858-
.args(format_args!("log\nmessage"))
859-
.level(Level::Info)
860-
.module_path(Some("test::path"))
861-
.key_values(&kvs)
862-
.build();
863-
864-
let written = write_record(
865-
record,
866-
DefaultFormat {
867-
timestamp: None,
868-
module_path: false,
869-
target: false,
870-
level: true,
871-
key_value_style: KVStyle::Multiline,
872-
written_header_value: false,
873-
indent: Some(2),
874-
suffix: "\n",
875-
buf: &mut f,
876-
},
877-
);
878-
879-
assert_eq!("[INFO ] log\n message\n a: 1\n b: 2\n", written);
771+
assert_eq!("[INFO test::path target] log\nmessage a=1 b=2\n", written);
880772
}
881773
}

src/logger.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,11 @@ impl Builder {
314314

315315
/// Configure the style for writing key/value pairs
316316
#[cfg(feature = "unstable-kv")]
317-
pub fn format_key_value_style(&mut self, style: fmt::KVStyle) -> &mut Self {
318-
self.format.key_value_style = style;
317+
pub fn format_key_values<F: 'static>(&mut self, format: F) -> &mut Self
318+
where
319+
F: Fn(&mut Formatter, &dyn log::kv::source::Source) -> io::Result<()> + Sync + Send,
320+
{
321+
self.format.kv_format = Some(Box::new(format));
319322
self
320323
}
321324

0 commit comments

Comments
 (0)