Skip to content

Commit

Permalink
Support forwarding structured data to log implementation
Browse files Browse the repository at this point in the history
See: #328
  • Loading branch information
tmccombs committed Dec 29, 2022
1 parent c6eba10 commit 2f0a241
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 12 deletions.
100 changes: 98 additions & 2 deletions kv.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use slog::{Record, Serializer};
use slog::{Record, Serializer, KV};

use std::fmt::Arguments;

pub(crate) struct Visitor {
kvs: Vec<(String, String)>,
Expand All @@ -25,11 +27,105 @@ impl<'kvs, 'a> log::kv::Visitor<'kvs> for Visitor {
}
}

impl slog::KV for Visitor {
impl KV for Visitor {
fn serialize(&self, _record: &Record, serializer: &mut dyn Serializer) -> slog::Result {
for (key, val) in &self.kvs {
serializer.emit_str(key.to_owned().into(), val.as_str())?;
}
Ok(())
}
}

/// Create a [`log::kv::Source`] for the key-value pairs for a slog record.
pub(crate) fn get_kv_source<'a>(
record: &'a slog::Record<'a>,
logger_kv: &'a slog::OwnedKVList,
) -> std::io::Result<Vec<(String, OwnedValue)>> {
let mut serialized_source = LogSerializer(vec![]);

record.kv().serialize(record, &mut serialized_source)?;
logger_kv.serialize(record, &mut serialized_source)?;
Ok(serialized_source.0)
}

/// A wrapper around [`log::kv::Value`], that owns the data included.
///
/// In particular this is necessary for strings, and large integers (u128, and i128), because the
/// `Value` type itself only supports references, which must survive for the lifetime of the
/// visitor.
pub(crate) enum OwnedValue {
Value(log::kv::Value<'static>),
Str(String),
U128(Box<u128>),
I128(Box<i128>),
}

impl log::kv::value::ToValue for OwnedValue {
fn to_value(&self) -> log::kv::Value<'_> {
use OwnedValue::*;

match self {
Value(v) => v.to_value(),
Str(s) => s.to_value(),
U128(v) => v.to_value(),
I128(v) => v.to_value(),
}
}
}

struct LogSerializer(Vec<(String, OwnedValue)>);

impl LogSerializer {
fn add(&mut self, key: slog::Key, val: OwnedValue) -> slog::Result {
self.0.push((key.into(), val));
Ok(())
}
}

macro_rules! emit_to_value {
($f:ident : $t:ty) => {
fn $f(&mut self, key: slog::Key, val: $t) -> slog::Result {
self.add(key, OwnedValue::Value(val.into()))
}
};
}

impl Serializer for LogSerializer {
fn emit_arguments(&mut self, key: slog::Key, val: &Arguments<'_>) -> slog::Result {
self.add(key, OwnedValue::Str(val.to_string()))
}

emit_to_value!(emit_usize: usize);
emit_to_value!(emit_isize: isize);
emit_to_value!(emit_bool: bool);
emit_to_value!(emit_char: char);
emit_to_value!(emit_u8: u8);
emit_to_value!(emit_i8: i8);
emit_to_value!(emit_u16: u16);
emit_to_value!(emit_i16: i16);
emit_to_value!(emit_u32: u32);
emit_to_value!(emit_i32: i32);
emit_to_value!(emit_f32: f32);
emit_to_value!(emit_f64: f64);

fn emit_u128(&mut self, key: slog::Key, val: u128) -> slog::Result {
self.add(key, OwnedValue::U128(Box::new(val)))
}

fn emit_i128(&mut self, key: slog::Key, val: i128) -> slog::Result {
self.add(key, OwnedValue::I128(Box::new(val)))
}

fn emit_str(&mut self, key: slog::Key, val: &str) -> slog::Result {
self.add(key, OwnedValue::Str(val.to_string()))
}

fn emit_unit(&mut self, key: slog::Key) -> slog::Result {
use log::kv::ToValue;
self.add(key, OwnedValue::Value(().to_value()))
}

fn emit_none(&mut self, key: slog::Key) -> slog::Result {
self.emit_unit(key)
}
}
23 changes: 13 additions & 10 deletions lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,16 +266,19 @@ impl slog::Drain for StdLog {
* This avoids using the private log::__private_api_log api function,
* which is just a thin wrapper around a `RecordBuilder`.
*/
log::logger().log(
&log::Record::builder()
.args(format_args!("{}", lazy))
.level(level)
.target(target)
.module_path_static(Some(info.module()))
.file_static(Some(info.file()))
.line(Some(info.line()))
.build(),
);
let mut record_builder = log::Record::builder();
record_builder
.level(level)
.target(target)
.module_path_static(Some(info.module()))
.file_static(Some(info.file()))
.line(Some(info.line()));
#[cfg(feature = "kv_unstable")]
let source = kv::get_kv_source(info, logger_values)?;
#[cfg(feature = "kv_unstable")]
record_builder.key_values(&source);

log::logger().log(&record_builder.args(format_args!("{}", lazy)).build());

Ok(())
}
Expand Down

0 comments on commit 2f0a241

Please sign in to comment.