Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[package]
name = "log4rs"
version = "1.3.0"
authors = ["Steven Fackler <sfackler@gmail.com>", "Evan Simmons <esims89@gmail.com>"]
authors = [
Comment thread
estk marked this conversation as resolved.
"Steven Fackler <sfackler@gmail.com>",
"Evan Simmons <esims89@gmail.com>",
]
description = "A highly configurable multi-output logging implementation for the `log` facade"
license = "MIT OR Apache-2.0"
repository = "https://github.com/estk/log4rs"
Expand All @@ -13,7 +16,13 @@ rust-version = "1.69"
[features]
default = ["all_components", "config_parsing", "yaml_format"]

config_parsing = ["humantime", "serde", "serde-value", "typemap-ors", "log/serde"]
config_parsing = [
"humantime",
"serde",
"serde-value",
"typemap-ors",
"log/serde",
]
yaml_format = ["serde_yaml"]
json_format = ["serde_json"]
toml_format = ["toml"]
Expand All @@ -27,13 +36,21 @@ fixed_window_roller = []
size_trigger = []
time_trigger = ["rand"]
onstartup_trigger = []
json_encoder = ["serde", "serde_json", "chrono", "log-mdc", "log/serde", "thread-id"]
json_encoder = [
"serde",
"serde_json",
"chrono",
"log-mdc",
"log/serde",
"thread-id",
]
pattern_encoder = ["chrono", "log-mdc", "thread-id"]
ansi_writer = []
console_writer = ["ansi_writer", "libc", "winapi"]
simple_writer = []
threshold_filter = []
background_rotation = []
log_kv = ["log/kv"]
Comment thread
gauntl3t12 marked this conversation as resolved.
Comment thread
ellttBen marked this conversation as resolved.

all_components = [
"console_appender",
Expand All @@ -47,7 +64,7 @@ all_components = [
"onstartup_trigger",
"json_encoder",
"pattern_encoder",
"threshold_filter"
"threshold_filter",
]

gzip = ["flate2"]
Expand All @@ -58,11 +75,13 @@ harness = false

[dependencies]
arc-swap = "1.6"
chrono = { version = "0.4.23", optional = true, features = ["clock"], default-features = false }
chrono = { version = "0.4.23", optional = true, features = [
"clock",
], default-features = false }
flate2 = { version = "1.0", optional = true }
fnv = "1.0"
humantime = { version = "2.1", optional = true }
log = { version = "0.4.20", features = ["std"] }
log = { version = "0.4.21", features = ["std"] }
log-mdc = { version = "0.1", optional = true }
serde = { version = "1.0.196", optional = true, features = ["derive"] }
serde-value = { version = "0.7", optional = true }
Expand All @@ -72,14 +91,20 @@ serde_json = { version = "1.0", optional = true }
serde_yaml = { version = "0.9", optional = true }
toml = { version = "<0.8.10", optional = true }
parking_lot = { version = "0.12.0", optional = true }
rand = { version = "0.8", optional = true}
rand = { version = "0.8", optional = true }
thiserror = "1.0.15"
anyhow = "1.0.28"
derivative = "2.2"
once_cell = "1.17.1"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", optional = true, features = ["handleapi", "minwindef", "processenv", "winbase", "wincon"] }
winapi = { version = "0.3", optional = true, features = [
"handleapi",
"minwindef",
"processenv",
"winbase",
"wincon",
] }

[target.'cfg(not(windows))'.dependencies]
libc = { version = "0.2", optional = true }
Expand All @@ -98,7 +123,11 @@ required-features = ["json_encoder", "console_appender"]

[[example]]
name = "log_to_file"
required-features = ["console_appender", "file_appender", "rolling_file_appender"]
required-features = [
"console_appender",
"file_appender",
"rolling_file_appender",
]

[[example]]
name = "compile_time_config"
Expand Down
65 changes: 56 additions & 9 deletions src/encode/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
//! }
//! }
//! ```
//! If the `log_kv` feature is enabled, an additional `attributes` field will
//! contain a map of the record's [log::kv][log_kv] structured logging
//! attributes.
//!
//! [log_kv]: https://docs.rs/log/latest/log/kv/index.html

use chrono::{
format::{DelayedFormat, Fixed, Item},
Expand Down Expand Up @@ -76,6 +81,8 @@ impl JsonEncoder {
thread: thread.name(),
thread_id: thread_id::get(),
mdc: Mdc,
#[cfg(feature = "log_kv")]
attributes: kv::get_attributes(record.key_values())?,
};
message.serialize(&mut serde_json::Serializer::new(&mut *w))?;
w.write_all(NEWLINE.as_bytes())?;
Expand Down Expand Up @@ -106,6 +113,8 @@ struct Message<'a> {
thread: Option<&'a str>,
thread_id: usize,
mdc: Mdc,
#[cfg(feature = "log_kv")]
attributes: kv::Map,
}

fn ser_display<T, S>(v: &T, s: S) -> Result<S::Ok, S::Error>
Expand Down Expand Up @@ -162,6 +171,34 @@ impl Deserialize for JsonEncoderDeserializer {
Ok(Box::<JsonEncoder>::default())
}
}
#[cfg(feature = "log_kv")]
mod kv {
use log::kv::VisitSource;
use std::collections::BTreeMap;

pub(crate) type Map = BTreeMap<String, String>;

pub(crate) fn get_attributes(source: &dyn log::kv::Source) -> anyhow::Result<Map> {
struct Visitor {
inner: Map,
}
Comment thread
ellttBen marked this conversation as resolved.
impl<'kvs> VisitSource<'kvs> for Visitor {
fn visit_pair(
&mut self,
key: log::kv::Key<'kvs>,
value: log::kv::Value<'kvs>,
) -> Result<(), log::kv::Error> {
self.inner.insert(format!("{key}"), format!("{value}"));
Ok(())
}
}
Comment thread
gauntl3t12 marked this conversation as resolved.
Outdated
let mut visitor = Visitor {
inner: BTreeMap::new(),
};
source.visit(&mut visitor)?;
Ok(visitor.inner)
}
}

#[cfg(test)]
#[cfg(feature = "simple_writer")]
Expand Down Expand Up @@ -189,26 +226,35 @@ mod test {

let encoder = JsonEncoder::new();

let mut record_builder = Record::builder();
record_builder
.level(level)
.target(target)
.module_path(Some(module_path))
.file(Some(file))
.line(Some(line));

#[cfg(feature = "log_kv")]
record_builder.key_values(&[("log_foo", "log_bar")]);

let mut buf = vec![];
encoder
.encode_inner(
&mut SimpleWriter(&mut buf),
time,
&Record::builder()
.level(level)
.target(target)
.module_path(Some(module_path))
.file(Some(file))
.line(Some(line))
.args(format_args!("{}", message))
.build(),
&record_builder.args(format_args!("{}", message)).build(),
)
.unwrap();

#[cfg(feature = "log_kv")]
let expected_attributes = ",\"attributes\":{\"log_foo\":\"log_bar\"}";
#[cfg(not(feature = "log_kv"))]
let expected_attributes = "";

let expected = format!(
"{{\"time\":\"{}\",\"level\":\"{}\",\"message\":\"{}\",\"module_path\":\"{}\",\
\"file\":\"{}\",\"line\":{},\"target\":\"{}\",\
\"thread\":\"{}\",\"thread_id\":{},\"mdc\":{{\"foo\":\"bar\"}}}}",
\"thread\":\"{}\",\"thread_id\":{},\"mdc\":{{\"foo\":\"bar\"}}{}}}",
time.to_rfc3339(),
level,
message,
Expand All @@ -218,6 +264,7 @@ mod test {
target,
thread,
thread_id::get(),
expected_attributes
);
assert_eq!(expected, String::from_utf8(buf).unwrap().trim());
}
Expand Down
Loading