Skip to content

Commit d5be263

Browse files
committed
Support forwarding structured data to log implementation
See: rust-lang/log#328
1 parent 252b6bb commit d5be263

File tree

2 files changed

+113
-25
lines changed

2 files changed

+113
-25
lines changed

kv.rs

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use log::kv::value::Error as ValueError;
2-
use slog::{Record, Serializer};
2+
use slog::{Record, Serializer, KV};
3+
4+
use std::fmt::Arguments;
35

46
struct Visitor<'s> {
57
serializer: &'s mut dyn Serializer,
@@ -60,7 +62,7 @@ impl<'s> log::kv::value::Visit<'s> for KeyVisit<'s> {
6062
visit_to_emit!(&(dyn std::error::Error + 'static): visit_error -> emit_error);
6163
}
6264

63-
impl slog::KV for SourceKV<'_> {
65+
impl KV for SourceKV<'_> {
6466
fn serialize(&self, _record: &Record, serializer: &mut dyn Serializer) -> slog::Result {
6567
// Unfortunately, there isn't a way for use to pass the original error through.
6668
self.0
@@ -79,4 +81,96 @@ fn to_value_err(err: slog::Error) -> ValueError {
7981
}
8082
}
8183

82-
// TODO: support going the other way
84+
/// Create a [`log::kv::Source`] for the key-value pairs for a slog record.
85+
pub(crate) fn get_kv_source<'a>(
86+
record: &'a slog::Record<'a>,
87+
logger_kv: &'a slog::OwnedKVList,
88+
) -> std::io::Result<Vec<(String, OwnedValue)>> {
89+
let mut serialized_source = LogSerializer(vec![]);
90+
91+
record.kv().serialize(record, &mut serialized_source)?;
92+
logger_kv.serialize(record, &mut serialized_source)?;
93+
Ok(serialized_source.0)
94+
}
95+
96+
/// A wrapper around [`log::kv::Value`], that owns the data included.
97+
///
98+
/// In particular this is necessary for strings, and large integers (u128, and i128), because the
99+
/// `Value` type itself only supports references, which must survive for the lifetime of the
100+
/// visitor.
101+
pub(crate) enum OwnedValue {
102+
Value(log::kv::Value<'static>),
103+
Str(String),
104+
U128(Box<u128>),
105+
I128(Box<i128>),
106+
}
107+
108+
impl log::kv::value::ToValue for OwnedValue {
109+
fn to_value(&self) -> log::kv::Value<'_> {
110+
use OwnedValue::*;
111+
112+
match self {
113+
Value(v) => v.to_value(),
114+
Str(s) => s.to_value(),
115+
U128(v) => v.to_value(),
116+
I128(v) => v.to_value(),
117+
}
118+
}
119+
}
120+
121+
struct LogSerializer(Vec<(String, OwnedValue)>);
122+
123+
impl LogSerializer {
124+
fn add(&mut self, key: slog::Key, val: OwnedValue) -> slog::Result {
125+
self.0.push((key.into(), val));
126+
Ok(())
127+
}
128+
}
129+
130+
macro_rules! emit_to_value {
131+
($f:ident : $t:ty) => {
132+
fn $f(&mut self, key: slog::Key, val: $t) -> slog::Result {
133+
self.add(key, OwnedValue::Value(val.into()))
134+
}
135+
};
136+
}
137+
138+
impl Serializer for LogSerializer {
139+
fn emit_arguments(&mut self, key: slog::Key, val: &Arguments<'_>) -> slog::Result {
140+
self.add(key, OwnedValue::Str(val.to_string()))
141+
}
142+
143+
emit_to_value!(emit_usize: usize);
144+
emit_to_value!(emit_isize: isize);
145+
emit_to_value!(emit_bool: bool);
146+
emit_to_value!(emit_char: char);
147+
emit_to_value!(emit_u8: u8);
148+
emit_to_value!(emit_i8: i8);
149+
emit_to_value!(emit_u16: u16);
150+
emit_to_value!(emit_i16: i16);
151+
emit_to_value!(emit_u32: u32);
152+
emit_to_value!(emit_i32: i32);
153+
emit_to_value!(emit_f32: f32);
154+
emit_to_value!(emit_f64: f64);
155+
156+
fn emit_u128(&mut self, key: slog::Key, val: u128) -> slog::Result {
157+
self.add(key, OwnedValue::U128(Box::new(val)))
158+
}
159+
160+
fn emit_i128(&mut self, key: slog::Key, val: i128) -> slog::Result {
161+
self.add(key, OwnedValue::I128(Box::new(val)))
162+
}
163+
164+
fn emit_str(&mut self, key: slog::Key, val: &str) -> slog::Result {
165+
self.add(key, OwnedValue::Str(val.to_string()))
166+
}
167+
168+
fn emit_unit(&mut self, key: slog::Key) -> slog::Result {
169+
use log::kv::ToValue;
170+
self.add(key, OwnedValue::Value(().to_value()))
171+
}
172+
173+
fn emit_none(&mut self, key: slog::Key) -> slog::Result {
174+
self.emit_unit(key)
175+
}
176+
}

lib.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -251,31 +251,25 @@ impl slog::Drain for StdLog {
251251

252252
let lazy = LazyLogString::new(info, logger_values);
253253
/*
254-
* TODO: Support `log` crate key_values here.
255-
*
256-
* This requires the log/kv_unstable feature here.
257-
*
258-
* Not supporting this feature is backwards compatible
259-
* and it shouldn't break anything (because we've never had),
260-
* but is undesirable from a feature-completeness point of view.
261-
*
262-
* However, this is most likely not as powerful as slog's own
263-
* notion of key/value pairs, so I would humbly suggest using `slog`
264-
* directly if this feature is important to you ;)
265-
*
266254
* This avoids using the private log::__private_api_log api function,
267255
* which is just a thin wrapper around a `RecordBuilder`.
268256
*/
269-
log::logger().log(
270-
&log::Record::builder()
271-
.args(format_args!("{}", lazy))
272-
.level(level)
273-
.target(target)
274-
.module_path_static(Some(info.module()))
275-
.file_static(Some(info.file()))
276-
.line(Some(info.line()))
277-
.build(),
278-
);
257+
let mut record_builder = log::Record::builder();
258+
record_builder
259+
.level(level)
260+
.target(target)
261+
.module_path_static(Some(info.module()))
262+
.file_static(Some(info.file()))
263+
.line(Some(info.line()));
264+
#[cfg(feature = "kv_unstable")]
265+
let source = kv::get_kv_source(info, logger_values)?;
266+
#[cfg(feature = "kv_unstable")]
267+
record_builder.key_values(&source);
268+
269+
// Due to https://github.com/rust-lang/rust/issues/92698, it is necessary to wait until we
270+
// actually call `.build()` to use `format_args`. And we split that out so that we can
271+
// conditionally call `key_values` based on the kv_unstable feature flag
272+
log::logger().log(&record_builder.args(format_args!("{}", lazy)).build());
279273

280274
Ok(())
281275
}

0 commit comments

Comments
 (0)