From 02ab539bb1632b967ee2be00231df00e9a9b58a6 Mon Sep 17 00:00:00 2001 From: Asuna Date: Sun, 24 Nov 2024 18:09:51 +0800 Subject: [PATCH] `FullFormatter` outputs key-values --- spdlog/src/formatter/full_formatter.rs | 26 ++++++++++-- spdlog/src/kv.rs | 55 +++++++++++++++++++++++++- spdlog/src/record.rs | 16 +++++--- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/spdlog/src/formatter/full_formatter.rs b/spdlog/src/formatter/full_formatter.rs index c81ad4da..ff25d4a3 100644 --- a/spdlog/src/formatter/full_formatter.rs +++ b/spdlog/src/formatter/full_formatter.rs @@ -94,6 +94,22 @@ impl FullFormatter { dest.write_str("] ")?; dest.write_str(record.payload())?; + let kvs = record.key_values(); + if !kvs.is_empty() { + dest.write_str(" { ")?; + + let mut iter = kvs.peekable(); + while let Some((key, value)) = iter.next() { + dest.write_str(key.as_str())?; + dest.write_str("=")?; + write!(dest, "{}", value)?; + if iter.peek().is_some() { + dest.write_str(", ")?; + } + } + dest.write_str(" }")?; + } + if self.with_eol { dest.write_str(__EOL)?; } @@ -126,11 +142,15 @@ mod tests { use chrono::prelude::*; use super::*; - use crate::{Level, __EOL}; + use crate::{kv, Level, __EOL}; #[test] fn format() { - let record = Record::new(Level::Warn, "test log content", None, None, &[]); + let kvs = [ + (kv::Key::__from_static_str("k1"), kv::Value::from(114)), + (kv::Key::__from_static_str("k2"), kv::Value::from("514")), + ]; + let record = Record::new(Level::Warn, "test log content", None, None, &kvs); let mut buf = StringBuf::new(); let mut ctx = FormatterContext::new(); FullFormatter::new() @@ -140,7 +160,7 @@ mod tests { let local_time: DateTime = record.time().into(); assert_eq!( format!( - "[{}] [warn] test log content{}", + "[{}] [warn] test log content {{ k1=114, k2=514 }}{}", local_time.format("%Y-%m-%d %H:%M:%S.%3f"), __EOL ), diff --git a/spdlog/src/kv.rs b/spdlog/src/kv.rs index 387f1069..53175b09 100644 --- a/spdlog/src/kv.rs +++ b/spdlog/src/kv.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, marker::PhantomData}; use value_bag::{OwnedValueBag, ValueBag}; @@ -14,6 +14,15 @@ pub(crate) enum KeyInner<'a> { #[derive(Debug, Clone)] pub struct Key<'a>(KeyInner<'a>); +impl<'a> Key<'a> { + pub fn as_str(&self) -> &str { + match &self.0 { + KeyInner::Str(s) => s, + KeyInner::StaticStr(s) => s, + } + } +} + impl<'a> Key<'a> { #[doc(hidden)] pub fn __from_static_str(key: &'static str) -> Self { @@ -61,6 +70,50 @@ impl KeyOwned { pub type Value<'a> = ValueBag<'a>; pub(crate) type ValueOwned = OwnedValueBag; +pub struct KeyValuesIter<'a, I> { + iter: I, + len: usize, + phantom: PhantomData<&'a ()>, +} + +impl KeyValuesIter<'_, I> { + pub fn len(&self) -> usize { + self.len + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } +} + +impl<'a, I> KeyValuesIter<'a, I> +where + I: Iterator, Value<'a>)>, +{ + pub(crate) fn new(iter: I, len: usize) -> Self { + Self { + iter, + len, + phantom: PhantomData, + } + } +} + +impl<'a, I> Iterator for KeyValuesIter<'a, I> +where + I: Iterator, Value<'a>)>, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + pub(crate) type Pair<'a> = (Key<'a>, Value<'a>); #[cfg(feature = "log")] diff --git a/spdlog/src/record.rs b/spdlog/src/record.rs index 61660ac3..18ce1d7f 100644 --- a/spdlog/src/record.rs +++ b/spdlog/src/record.rs @@ -110,9 +110,12 @@ impl<'a> Record<'a> { } #[must_use] - pub fn key_values(&self) -> impl IntoIterator { - // The 2 clones should be cheap - self.kvs.iter().map(|(k, v)| (k.clone(), v.clone())) + pub fn key_values(&self) -> kv::KeyValuesIter> { + kv::KeyValuesIter::new( + // The 2 clones should be cheap + self.kvs.iter().map(|(k, v)| (k.clone(), v.clone())), + self.kvs.len(), + ) } // When adding more getters, also add to `RecordOwned` @@ -239,8 +242,11 @@ impl RecordOwned { } #[must_use] - pub fn key_values(&self) -> impl IntoIterator { - self.kvs.iter().map(|(k, v)| (k.as_ref(), v.by_ref())) + pub fn key_values(&self) -> kv::KeyValuesIter> { + kv::KeyValuesIter::new( + self.kvs.iter().map(|(k, v)| (k.as_ref(), v.by_ref())), + self.kvs.len(), + ) } // When adding more getters, also add to `Record`