Skip to content

Commit e55af6f

Browse files
authored
Merge pull request #137 from tmccombs/structured
Add support for Key-Value data in log records.
2 parents 8962096 + f6e2d45 commit e55af6f

File tree

6 files changed

+250
-67
lines changed

6 files changed

+250
-67
lines changed

Cargo.lock

Lines changed: 2 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ color = ["dep:anstream", "dep:anstyle"]
5252
auto-color = ["color", "anstream/auto"]
5353
humantime = ["dep:humantime"]
5454
regex = ["env_filter/regex"]
55+
unstable-kv = ["log/kv"]
5556

5657
[dependencies]
57-
log = { version = "0.4.8", features = ["std"] }
58+
log = { version = "0.4.21", features = ["std"] }
5859
env_filter = { version = "0.1.0", path = "crates/env_filter", default-features = false }
5960
humantime = { version = "2.0.0", optional = true }
6061
anstream = { version = "0.6.11", default-features = false, features = ["wincon"], optional = true }

examples/direct_logger.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,27 @@ use env_logger::{Builder, WriteStyle};
88

99
use log::{Level, LevelFilter, Log, MetadataBuilder, Record};
1010

11+
#[cfg(feature = "unstable-kv")]
12+
static KVS: (&str, &str) = ("test", "something");
13+
1114
fn record() -> Record<'static> {
1215
let error_metadata = MetadataBuilder::new()
1316
.target("myApp")
1417
.level(Level::Error)
1518
.build();
1619

17-
Record::builder()
20+
let mut builder = Record::builder();
21+
builder
1822
.metadata(error_metadata)
1923
.args(format_args!("Error!"))
2024
.line(Some(433))
2125
.file(Some("app.rs"))
22-
.module_path(Some("server"))
23-
.build()
26+
.module_path(Some("server"));
27+
#[cfg(feature = "unstable-kv")]
28+
{
29+
builder.key_values(&KVS);
30+
}
31+
builder.build()
2432
}
2533

2634
fn main() {

src/fmt/kv.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use std::io::{self, Write};
2+
3+
#[cfg(feature = "color")]
4+
use super::WriteStyle;
5+
use super::{Formatter, StyledValue};
6+
#[cfg(feature = "color")]
7+
use anstyle::Style;
8+
use log::kv::{Error, Key, Source, Value, VisitSource};
9+
10+
/// Format function for serializing key/value pairs
11+
///
12+
/// This function determines how key/value pairs for structured logs are serialized within the default
13+
/// format.
14+
pub(crate) type KvFormatFn = dyn Fn(&mut Formatter, &dyn Source) -> io::Result<()> + Sync + Send;
15+
16+
/// Null Key Value Format
17+
///
18+
/// This function is intended to be passed to
19+
/// [`Builder::format_key_values`](crate::Builder::format_key_values).
20+
///
21+
/// This key value format simply ignores any key/value fields and doesn't include them in the
22+
/// output.
23+
pub fn hidden_kv_format(_formatter: &mut Formatter, _fields: &dyn Source) -> io::Result<()> {
24+
Ok(())
25+
}
26+
27+
/// Default Key Value Format
28+
///
29+
/// This function is intended to be passed to
30+
/// [`Builder::format_key_values`](crate::Builder::format_key_values).
31+
///
32+
/// This is the default key/value format. Which uses an "=" as the separator between the key and
33+
/// value and a " " between each pair.
34+
///
35+
/// For example: `ip=127.0.0.1 port=123456 path=/example`
36+
pub fn default_kv_format(formatter: &mut Formatter, fields: &dyn Source) -> io::Result<()> {
37+
fields
38+
.visit(&mut DefaultVisitSource(formatter))
39+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
40+
}
41+
42+
struct DefaultVisitSource<'a>(&'a mut Formatter);
43+
44+
impl<'a, 'kvs> VisitSource<'kvs> for DefaultVisitSource<'a> {
45+
fn visit_pair(&mut self, key: Key, value: Value<'kvs>) -> Result<(), Error> {
46+
write!(self.0, " {}={}", self.style_key(key), value)?;
47+
Ok(())
48+
}
49+
}
50+
51+
impl DefaultVisitSource<'_> {
52+
fn style_key<'k>(&self, text: Key<'k>) -> StyledValue<Key<'k>> {
53+
#[cfg(feature = "color")]
54+
{
55+
StyledValue {
56+
style: if self.0.write_style == WriteStyle::Never {
57+
Style::new()
58+
} else {
59+
Style::new().italic()
60+
},
61+
value: text,
62+
}
63+
}
64+
#[cfg(not(feature = "color"))]
65+
{
66+
text
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)