diff --git a/spdlog/src/formatter/full_formatter.rs b/spdlog/src/formatter/full_formatter.rs index 125d81b8..8b37f675 100644 --- a/spdlog/src/formatter/full_formatter.rs +++ b/spdlog/src/formatter/full_formatter.rs @@ -98,21 +98,7 @@ 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(" }")?; - } + record.key_values().write_to(dest)?; if self.with_eol { dest.write_str(__EOL)?; diff --git a/spdlog/src/formatter/journald_formatter.rs b/spdlog/src/formatter/journald_formatter.rs index 0b24739f..e594eaa4 100644 --- a/spdlog/src/formatter/journald_formatter.rs +++ b/spdlog/src/formatter/journald_formatter.rs @@ -49,21 +49,7 @@ impl JournaldFormatter { 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(" }")?; - } + record.key_values().write_to(dest)?; dest.write_str(__EOL)?; diff --git a/spdlog/src/formatter/pattern_formatter/pattern/kv.rs b/spdlog/src/formatter/pattern_formatter/pattern/kv.rs index 7f72794e..448a57e5 100644 --- a/spdlog/src/formatter/pattern_formatter/pattern/kv.rs +++ b/spdlog/src/formatter/pattern_formatter/pattern/kv.rs @@ -1,5 +1,3 @@ -use std::fmt::{self, Write}; - use crate::{ formatter::pattern_formatter::{Pattern, PatternContext}, Error, Record, StringBuf, @@ -15,23 +13,9 @@ impl Pattern for KV { dest: &mut StringBuf, _ctx: &mut PatternContext, ) -> crate::Result<()> { - (|| -> Result<(), fmt::Error> { - 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(" }")?; - } - Ok(()) - })() - .map_err(Error::FormatRecord) + record + .key_values() + .write_to(dest) + .map_err(Error::FormatRecord) } } diff --git a/spdlog/src/kv.rs b/spdlog/src/kv.rs index 81515fb0..e2ec8203 100644 --- a/spdlog/src/kv.rs +++ b/spdlog/src/kv.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, marker::PhantomData}; +use std::{borrow::Cow, fmt, marker::PhantomData}; use value_bag::{OwnedValueBag, ValueBag}; @@ -95,6 +95,29 @@ where phantom: PhantomData, } } + + pub(crate) fn write_to(mut self, dest: &mut impl fmt::Write) -> fmt::Result { + let first = self.next(); + if let Some((key, value)) = first { + dest.write_str(" { ")?; + + // Reduce branch prediction misses for performance + // So we manually process the first KV pair + dest.write_str(key.as_str())?; + dest.write_str("=")?; + write!(dest, "{}", value)?; + + for (key, value) in self { + dest.write_str(", ")?; + dest.write_str(key.as_str())?; + dest.write_str("=")?; + write!(dest, "{}", value)?; + } + + dest.write_str(" }")?; + } + Ok(()) + } } impl<'a, I> Iterator for KeyValuesIter<'a, I>