From 2f0a241a3e37b44307df82bf9812aeff2952acc2 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Wed, 28 Dec 2022 22:55:51 -0700 Subject: [PATCH] Support forwarding structured data to log implementation See: #328 --- kv.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib.rs | 23 +++++++------ 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/kv.rs b/kv.rs index 2172843..3af2fa8 100644 --- a/kv.rs +++ b/kv.rs @@ -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)>, @@ -25,7 +27,7 @@ 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())?; @@ -33,3 +35,97 @@ impl slog::KV for Visitor { 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> { + 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), + I128(Box), +} + +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) + } +} diff --git a/lib.rs b/lib.rs index 4b9f8e9..4cedb2f 100644 --- a/lib.rs +++ b/lib.rs @@ -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(()) }