From 937c5d7cf08f5829651e3e9f99227c92e159fa82 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 20 Oct 2021 16:35:22 -0700 Subject: [PATCH] subscriber: replace `dyn Write` with a `Writer` type (#1661) ## Motivation Currently, the `FormatEvent` and `FormatFields` traits in `tracing-subscriber` are passed a `&mut dyn fmt::Write` trait object to write formatted representations of events and fields to. This is fine, but it doesn't give us the ability to easily provide additional configuration to the formatter, such as whether ANSI color codes are supported. Issue #1651 describes some approaches for adding a way to expose the ANSI color code configuration to custom formatters. In particular, the proposed solution involves wrapping the `fmt::Write` trait object in an opaque struct, which can then implement additional methods for exposing information like "are ANSI colors enabled" to the formatter. Since this changes the signature of the `FormatEvent::format_event` and `FormatFields::format_fields` methods, it's a breaking change. Therefore, we need to make this change _now_ if we want to get the API change in for `tracing-subscriber` 0.3. ## Solution This branch adds a `Writer` struct that wraps the `&mut dyn fmt::Write` trait object, and changes the various method signatures as appropriate. It does **not** actually implement the change related to ANSI color formatting. Once we change these methods' signatures to accept a `Writer` struct, we can add as many methods to that struct as we like without making additional breaking API changes. Therefore, this branch _just_ makes the breaking change that's necessary to get in before v0.3 is released. Propagating the ANSI color configuration can be implemented in a future branch. Signed-off-by: Eliza Weisman --- tracing-error/src/subscriber.rs | 7 +- tracing-subscriber/src/fmt/fmt_subscriber.rs | 70 +++-- tracing-subscriber/src/fmt/format/json.rs | 76 +++--- tracing-subscriber/src/fmt/format/mod.rs | 252 +++++++++++++----- tracing-subscriber/src/fmt/format/pretty.rs | 29 +- tracing-subscriber/src/fmt/time/mod.rs | 15 +- tracing-subscriber/src/fmt/time/time_crate.rs | 8 +- 7 files changed, 296 insertions(+), 161 deletions(-) diff --git a/tracing-error/src/subscriber.rs b/tracing-error/src/subscriber.rs index 8874401a22..2e81d2d921 100644 --- a/tracing-error/src/subscriber.rs +++ b/tracing-error/src/subscriber.rs @@ -53,10 +53,9 @@ where if span.extensions().get::>().is_some() { return; } - let mut fields = String::new(); - if self.format.format_fields(&mut fields, attrs).is_ok() { - span.extensions_mut() - .insert(FormattedFields::::new(fields)); + let mut fields = FormattedFields::::new(String::new()); + if self.format.format_fields(fields.as_writer(), attrs).is_ok() { + span.extensions_mut().insert(fields); } } diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index 23209b949f..c41daedc3d 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -485,37 +485,46 @@ where /// /// [extensions]: crate::registry::Extensions #[derive(Default)] -pub struct FormattedFields { - _format_event: PhantomData, +pub struct FormattedFields { + _format_fields: PhantomData, /// The formatted fields of a span. pub fields: String, } -impl FormattedFields { +impl FormattedFields { /// Returns a new `FormattedFields`. pub fn new(fields: String) -> Self { Self { fields, - _format_event: PhantomData, + _format_fields: PhantomData, } } + + /// Returns a new [`format::Writer`] for writing to this `FormattedFields`. + /// + /// The returned [`format::Writer`] can be used with the + /// [`FormatFields::format_fields`] method. + pub fn as_writer(&mut self) -> format::Writer<'_> { + format::Writer::new(&mut self.fields) + } } -impl fmt::Debug for FormattedFields { +impl fmt::Debug for FormattedFields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FormattedFields") .field("fields", &self.fields) + .field("formatter", &format_args!("{}", std::any::type_name::())) .finish() } } -impl fmt::Display for FormattedFields { +impl fmt::Display for FormattedFields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.fields) + fmt::Display::fmt(&self.fields, f) } } -impl Deref for FormattedFields { +impl Deref for FormattedFields { type Target = String; fn deref(&self) -> &Self::Target { &self.fields @@ -552,13 +561,13 @@ where let mut extensions = span.extensions_mut(); if extensions.get_mut::>().is_none() { - let mut buf = String::new(); - if self.fmt_fields.format_fields(&mut buf, attrs).is_ok() { - let fmt_fields = FormattedFields { - fields: buf, - _format_event: PhantomData::, - }; - extensions.insert(fmt_fields); + let mut fields = FormattedFields::::new(String::new()); + if self + .fmt_fields + .format_fields(fields.as_writer(), attrs) + .is_ok() + { + extensions.insert(fields); } } @@ -581,19 +590,18 @@ where fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) { let span = ctx.span(id).expect("Span not found, this is a bug"); let mut extensions = span.extensions_mut(); - if let Some(FormattedFields { ref mut fields, .. }) = - extensions.get_mut::>() - { + if let Some(fields) = extensions.get_mut::>() { let _ = self.fmt_fields.add_fields(fields, values); - } else { - let mut buf = String::new(); - if self.fmt_fields.format_fields(&mut buf, values).is_ok() { - let fmt_fields = FormattedFields { - fields: buf, - _format_event: PhantomData::, - }; - extensions.insert(fmt_fields); - } + return; + } + + let mut fields = FormattedFields::::new(String::new()); + if self + .fmt_fields + .format_fields(fields.as_writer(), values) + .is_ok() + { + extensions.insert(fields); } } @@ -695,7 +703,11 @@ where }; let ctx = self.make_ctx(ctx); - if self.fmt_event.format_event(&ctx, &mut buf, event).is_ok() { + if self + .fmt_event + .format_event(&ctx, format::Writer::new(&mut buf), event) + .is_ok() + { let mut writer = self.make_writer.make_writer_for(event.metadata()); let _ = io::Write::write_all(&mut writer, buf.as_bytes()); } @@ -738,7 +750,7 @@ where { fn format_fields( &self, - writer: &'writer mut dyn fmt::Write, + writer: format::Writer<'writer>, fields: R, ) -> fmt::Result { self.fmt_fields.format_fields(writer, fields) diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index 98beff6f46..3c085855fc 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -1,4 +1,4 @@ -use super::{Format, FormatEvent, FormatFields, FormatTime}; +use super::{Format, FormatEvent, FormatFields, FormatTime, Writer}; use crate::{ field::{RecordFields, VisitOutput}, fmt::{ @@ -185,14 +185,14 @@ where fn format_event( &self, ctx: &FmtContext<'_, C, N>, - writer: &mut dyn fmt::Write, + mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result where C: Collect + for<'a> LookupSpan<'a>, { let mut timestamp = String::new(); - self.timer.format_time(&mut timestamp)?; + self.timer.format_time(&mut Writer::new(&mut timestamp))?; #[cfg(feature = "tracing-log")] let normalized_meta = event.normalized_metadata(); @@ -202,7 +202,7 @@ where let meta = event.metadata(); let mut visit = || { - let mut serializer = Serializer::new(WriteAdaptor::new(writer)); + let mut serializer = Serializer::new(WriteAdaptor::new(&mut writer)); let mut serializer = serializer.serialize_map(None)?; @@ -318,12 +318,8 @@ impl Default for JsonFields { impl<'a> FormatFields<'a> for JsonFields { /// Format the provided `fields` to the provided `writer`, returning a result. - fn format_fields( - &self, - writer: &'a mut dyn fmt::Write, - fields: R, - ) -> fmt::Result { - let mut v = JsonVisitor::new(writer); + fn format_fields(&self, mut writer: Writer<'_>, fields: R) -> fmt::Result { + let mut v = JsonVisitor::new(&mut writer); fields.record(&mut v); v.finish() } @@ -333,38 +329,44 @@ impl<'a> FormatFields<'a> for JsonFields { /// By default, this appends a space to the current set of fields if it is /// non-empty, and then calls `self.format_fields`. If different behavior is /// required, the default implementation of this method can be overridden. - fn add_fields(&self, current: &'a mut String, fields: &Record<'_>) -> fmt::Result { - if !current.is_empty() { - // If fields were previously recorded on this span, we need to parse - // the current set of fields as JSON, add the new fields, and - // re-serialize them. Otherwise, if we just appended the new fields - // to a previously serialized JSON object, we would end up with - // malformed JSON. - // - // XXX(eliza): this is far from efficient, but unfortunately, it is - // necessary as long as the JSON formatter is implemented on top of - // an interface that stores all formatted fields as strings. - // - // We should consider reimplementing the JSON formatter as a - // separate layer, rather than a formatter for the `fmt` layer — - // then, we could store fields as JSON values, and add to them - // without having to parse and re-serialize. - let mut new = String::new(); - let map: BTreeMap<&'_ str, serde_json::Value> = - serde_json::from_str(current).map_err(|_| fmt::Error)?; - let mut v = JsonVisitor::new(&mut new); - v.values = map; - fields.record(&mut v); - v.finish()?; - *current = new; - } else { + fn add_fields( + &self, + current: &'a mut FormattedFields, + fields: &Record<'_>, + ) -> fmt::Result { + if current.is_empty() { // If there are no previously recorded fields, we can just reuse the // existing string. - let mut v = JsonVisitor::new(current); + let mut writer = current.as_writer(); + let mut v = JsonVisitor::new(&mut writer); fields.record(&mut v); v.finish()?; + return Ok(()); } + // If fields were previously recorded on this span, we need to parse + // the current set of fields as JSON, add the new fields, and + // re-serialize them. Otherwise, if we just appended the new fields + // to a previously serialized JSON object, we would end up with + // malformed JSON. + // + // XXX(eliza): this is far from efficient, but unfortunately, it is + // necessary as long as the JSON formatter is implemented on top of + // an interface that stores all formatted fields as strings. + // + // We should consider reimplementing the JSON formatter as a + // separate layer, rather than a formatter for the `fmt` layer — + // then, we could store fields as JSON values, and add to them + // without having to parse and re-serialize. + let mut new = String::new(); + let map: BTreeMap<&'_ str, serde_json::Value> = + serde_json::from_str(current).map_err(|_| fmt::Error)?; + let mut v = JsonVisitor::new(&mut new); + v.values = map; + fields.record(&mut v); + v.finish()?; + current.fields = new; + Ok(()) } } @@ -484,7 +486,7 @@ mod test { struct MockTime; impl FormatTime for MockTime { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { write!(w, "fake time") } } diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index 458fed7f8d..c8ca8f8b1d 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -7,10 +7,7 @@ use crate::{ registry::Scope, }; -use std::{ - fmt::{self, Write}, - marker::PhantomData, -}; +use std::{fmt, marker::PhantomData}; use tracing_core::{ field::{self, Field, Visit}, span, Collect, Event, Level, @@ -36,7 +33,7 @@ pub use pretty::*; use fmt::{Debug, Display}; -/// A type that can format a tracing `Event` for a `fmt::Write`. +/// A type that can format a tracing [`Event`] to a [`Writer`]. /// /// `FormatEvent` is primarily used in the context of [`fmt::Collector`] or [`fmt::Subscriber`]. /// Each time an event is dispatched to [`fmt::Collector`] or [`fmt::Subscriber`], @@ -48,9 +45,13 @@ use fmt::{Debug, Display}; /// # Examples /// /// ```rust -/// use std::fmt::{self, Write}; +/// use std::fmt; /// use tracing_core::{Collect, Event}; -/// use tracing_subscriber::fmt::{FormatEvent, FormatFields, FmtContext, FormattedFields}; +/// use tracing_subscriber::fmt::{ +/// format::{self, FormatEvent, FormatFields}, +/// FmtContext, +/// FormattedFields, +/// }; /// use tracing_subscriber::registry::LookupSpan; /// /// struct MyFormatter; @@ -63,7 +64,7 @@ use fmt::{Debug, Display}; /// fn format_event( /// &self, /// ctx: &FmtContext<'_, C, N>, -/// writer: &mut dyn fmt::Write, +/// mut writer: format::Writer<'_>, /// event: &Event<'_>, /// ) -> fmt::Result { /// // Write level and target @@ -100,7 +101,7 @@ use fmt::{Debug, Display}; /// })?; /// /// // Write fields on the event -/// ctx.field_format().format_fields(writer, event)?; +/// ctx.field_format().format_fields(writer.by_ref(), event)?; /// /// writeln!(writer) /// } @@ -115,22 +116,23 @@ use fmt::{Debug, Display}; /// /// [`fmt::Collector`]: super::Collector /// [`fmt::Subscriber`]: super::Subscriber +/// [`Event`]: tracing::Event pub trait FormatEvent where C: Collect + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, { - /// Write a log message for `Event` in `Context` to the given `Write`. + /// Write a log message for `Event` in `Context` to the given [`Writer`]. fn format_event( &self, ctx: &FmtContext<'_, C, N>, - writer: &mut dyn fmt::Write, + writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result; } impl FormatEvent - for fn(ctx: &FmtContext<'_, C, N>, &mut dyn fmt::Write, &Event<'_>) -> fmt::Result + for fn(ctx: &FmtContext<'_, C, N>, Writer<'_>, &Event<'_>) -> fmt::Result where C: Collect + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, @@ -138,13 +140,13 @@ where fn format_event( &self, ctx: &FmtContext<'_, C, N>, - writer: &mut dyn fmt::Write, + writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { (*self)(ctx, writer, event) } } -/// A type that can format a [set of fields] to a `fmt::Write`. +/// A type that can format a [set of fields] to a [`Writer`]. /// /// `FormatFields` is primarily used in the context of [`fmt::Subscriber`]. Each /// time a span or event with fields is recorded, the subscriber will format @@ -153,23 +155,23 @@ where /// [set of fields]: RecordFields /// [`fmt::Subscriber`]: super::Subscriber pub trait FormatFields<'writer> { - /// Format the provided `fields` to the provided `writer`, returning a result. - fn format_fields( - &self, - writer: &'writer mut dyn fmt::Write, - fields: R, - ) -> fmt::Result; + /// Format the provided `fields` to the provided [`Writer`], returning a result. + fn format_fields(&self, writer: Writer<'writer>, fields: R) -> fmt::Result; /// Record additional field(s) on an existing span. /// /// By default, this appends a space to the current set of fields if it is /// non-empty, and then calls `self.format_fields`. If different behavior is /// required, the default implementation of this method can be overridden. - fn add_fields(&self, current: &'writer mut String, fields: &span::Record<'_>) -> fmt::Result { - if !current.is_empty() { - current.push(' '); + fn add_fields( + &self, + current: &'writer mut FormattedFields, + fields: &span::Record<'_>, + ) -> fmt::Result { + if !current.fields.is_empty() { + current.fields.push(' '); } - self.format_fields(current, fields) + self.format_fields(current.as_writer(), fields) } } @@ -209,11 +211,29 @@ pub fn json() -> Format { /// pub fn debug_fn(f: F) -> FieldFn where - F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, + F: Fn(&mut Writer<'_>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, { FieldFn(f) } +/// A writer to which formatted representations of spans and events are written. +/// +/// This type is provided as input to the [`FormatEvent::format_event`] and +/// [`FormatFields::format_fields`] methods, which will write formatted +/// representations of [`Event`]s and [fields] to the `Writer`. +/// +/// This type implements the [`std::fmt::Write`] trait, allowing it to be used +/// with any function that takes an instance of [`std::fmt::Write`]. +/// Additionally, it can be used with the standard library's [`std::write!`] and +/// [`std::writeln!`] macros. +/// +/// Additionally, a `Writer` may expose additional `tracing`-specific +/// information to the formatter implementation. +pub struct Writer<'writer> { + writer: &'writer mut dyn fmt::Write, + // TODO(eliza): add ANSI support +} + /// A [`FormatFields`] implementation that formats fields by calling a function /// or closure. /// @@ -225,7 +245,7 @@ pub struct FieldFn(F); /// [`MakeVisitor`]: super::super::field::MakeVisitor pub struct FieldFnVisitor<'a, F> { f: F, - writer: &'a mut dyn fmt::Write, + writer: Writer<'a>, result: fmt::Result, } /// Marker for `Format` that indicates that the compact log format should be used. @@ -259,6 +279,115 @@ pub struct Format { pub(crate) display_thread_name: bool, } +// === impl Writer === + +impl<'writer> Writer<'writer> { + // TODO(eliza): consider making this a public API? + // We may not want to do that if we choose to expose specialized + // constructors instead (e.g. `from_string` that stores whether the string + // is empty...?) + pub(crate) fn new(writer: &'writer mut impl fmt::Write) -> Self { + Self { + writer: writer as &mut dyn fmt::Write, + } + } + + /// Return a new `Writer` that mutably borrows `self`. + /// + /// This can be used to temporarily borrow a `Writer` to pass a new `Writer` + /// to a function that takes a `Writer` by value, allowing the original writer + /// to still be used once that function returns. + pub fn by_ref(&mut self) -> Writer<'_> { + Writer::new(self) + } + + /// Writes a string slice into this `Writer`, returning whether the write succeeded. + /// + /// This method can only succeed if the entire string slice was successfully + /// written, and this method will not return until all data has been written + /// or an error occurs. + /// + /// This is identical to calling the [`write_str` method] from the `Writer`'s + /// [`std::fmt::Write`] implementation. However, it is also provided as an + /// inherent method, so that `Writer`s can be used without needing to import the + /// [`std::fmt::Write`] trait. + /// + /// # Errors + /// + /// This function will return an instance of [`std::fmt::Error`] on error. + /// + /// [`write_str` method]: std::fmt::Write::write_str + #[inline] + pub fn write_str(&mut self, s: &str) -> fmt::Result { + self.writer.write_str(s) + } + + /// Writes a [`char`] into this writer, returning whether the write succeeded. + /// + /// A single [`char`] may be encoded as more than one byte. + /// This method can only succeed if the entire byte sequence was successfully + /// written, and this method will not return until all data has been + /// written or an error occurs. + /// + /// This is identical to calling the [`write_char` method] from the `Writer`'s + /// [`std::fmt::Write`] implementation. However, it is also provided as an + /// inherent method, so that `Writer`s can be used without needing to import the + /// [`std::fmt::Write`] trait. + /// + /// # Errors + /// + /// This function will return an instance of [`std::fmt::Error`] on error. + /// + /// [`write_char` method]: std::fmt::Write::write_char + #[inline] + pub fn write_char(&mut self, c: char) -> fmt::Result { + self.writer.write_char(c) + } + + /// Glue for usage of the [`write!`] macro with `Wrriter`s. + /// + /// This method should generally not be invoked manually, but rather through + /// the [`write!`] macro itself. + /// + /// This is identical to calling the [`write_fmt` method] from the `Writer`'s + /// [`std::fmt::Write`] implementation. However, it is also provided as an + /// inherent method, so that `Writer`s can be used with the [`write!` macro] + /// without needing to import the + /// [`std::fmt::Write`] trait. + /// + /// [`write_fmt` method]: std::fmt::Write::write_fmt + pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { + self.writer.write_fmt(args) + } +} + +impl fmt::Write for Writer<'_> { + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + Writer::write_str(self, s) + } + + #[inline] + fn write_char(&mut self, c: char) -> fmt::Result { + Writer::write_char(self, c) + } + + #[inline] + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { + Writer::write_fmt(self, args) + } +} + +impl fmt::Debug for Writer<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Writer") + .field("writer", &format_args!("<&mut dyn fmt::Write>")) + .finish() + } +} + +// === impl Format === + impl Default for Format { fn default() -> Self { Format { @@ -464,7 +593,7 @@ impl Format { } #[inline] - fn format_timestamp(&self, writer: &mut dyn fmt::Write) -> fmt::Result + fn format_timestamp(&self, writer: &mut Writer<'_>) -> fmt::Result where T: FormatTime, { @@ -553,7 +682,7 @@ where fn format_event( &self, ctx: &FmtContext<'_, C, N>, - writer: &mut dyn fmt::Write, + mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { #[cfg(feature = "tracing-log")] @@ -563,8 +692,8 @@ where #[cfg(not(feature = "tracing-log"))] let meta = event.metadata(); - self.format_timestamp(writer)?; - self.format_level(*meta.level(), writer)?; + self.format_timestamp(&mut writer)?; + self.format_level(*meta.level(), &mut writer)?; if self.display_thread_name { let current_thread = std::thread::current(); @@ -610,7 +739,7 @@ where write!(writer, "{}: ", meta.target())?; } - ctx.format_fields(writer, event)?; + ctx.format_fields(writer.by_ref(), event)?; writeln!(writer) } } @@ -624,7 +753,7 @@ where fn format_event( &self, ctx: &FmtContext<'_, C, N>, - writer: &mut dyn fmt::Write, + mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { #[cfg(feature = "tracing-log")] @@ -634,8 +763,8 @@ where #[cfg(not(feature = "tracing-log"))] let meta = event.metadata(); - self.format_timestamp(writer)?; - self.format_level(*meta.level(), writer)?; + self.format_timestamp(&mut writer)?; + self.format_level(*meta.level(), &mut writer)?; if self.display_thread_name { let current_thread = std::thread::current(); @@ -667,7 +796,7 @@ where write!(writer, "{}:", target)?; } - ctx.format_fields(writer, event)?; + ctx.format_fields(writer.by_ref(), event)?; #[cfg(feature = "ansi")] let dimmed = if self.ansi { @@ -697,22 +826,18 @@ where } // === impl FormatFields === - impl<'writer, M> FormatFields<'writer> for M where - M: MakeOutput<&'writer mut dyn fmt::Write, fmt::Result>, + M: MakeOutput, fmt::Result>, M::Visitor: VisitFmt + VisitOutput, { - fn format_fields( - &self, - writer: &'writer mut dyn fmt::Write, - fields: R, - ) -> fmt::Result { + fn format_fields(&self, writer: Writer<'writer>, fields: R) -> fmt::Result { let mut v = self.make_visitor(writer); fields.record(&mut v); v.finish() } } + /// The default [`FormatFields`] implementation. /// #[derive(Debug)] @@ -726,8 +851,9 @@ pub struct DefaultFields { /// /// [visitor]: super::super::field::Visit /// [`MakeVisitor`]: super::super::field::MakeVisitor +#[derive(Debug)] pub struct DefaultVisitor<'a> { - writer: &'a mut dyn Write, + writer: Writer<'a>, is_empty: bool, result: fmt::Result, } @@ -746,11 +872,11 @@ impl Default for DefaultFields { } } -impl<'a> MakeVisitor<&'a mut dyn Write> for DefaultFields { +impl<'a> MakeVisitor> for DefaultFields { type Visitor = DefaultVisitor<'a>; #[inline] - fn make_visitor(&self, target: &'a mut dyn Write) -> Self::Visitor { + fn make_visitor(&self, target: Writer<'a>) -> Self::Visitor { DefaultVisitor::new(target, true) } } @@ -764,7 +890,7 @@ impl<'a> DefaultVisitor<'a> { /// - `writer`: the writer to format to. /// - `is_empty`: whether or not any fields have been previously written to /// that writer. - pub fn new(writer: &'a mut dyn Write, is_empty: bool) -> Self { + pub fn new(writer: Writer<'a>, is_empty: bool) -> Self { Self { writer, is_empty, @@ -830,17 +956,7 @@ impl<'a> crate::field::VisitOutput for DefaultVisitor<'a> { impl<'a> crate::field::VisitFmt for DefaultVisitor<'a> { fn writer(&mut self) -> &mut dyn fmt::Write { - self.writer - } -} - -impl<'a> fmt::Debug for DefaultVisitor<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DefaultVisitor") - .field("writer", &format_args!("")) - .field("is_empty", &self.is_empty) - .field("result", &self.result) - .finish() + &mut self.writer } } @@ -998,13 +1114,13 @@ impl<'a, F: LevelNames> fmt::Display for FmtLevel { // === impl FieldFn === -impl<'a, F> MakeVisitor<&'a mut dyn fmt::Write> for FieldFn +impl<'a, F> MakeVisitor> for FieldFn where - F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, + F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, { type Visitor = FieldFnVisitor<'a, F>; - fn make_visitor(&self, writer: &'a mut dyn fmt::Write) -> Self::Visitor { + fn make_visitor(&self, writer: Writer<'a>) -> Self::Visitor { FieldFnVisitor { writer, f: self.0.clone(), @@ -1015,7 +1131,7 @@ where impl<'a, F> Visit for FieldFnVisitor<'a, F> where - F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result, + F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result, { fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { if self.result.is_ok() { @@ -1026,7 +1142,7 @@ where impl<'a, F> VisitOutput for FieldFnVisitor<'a, F> where - F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result, + F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result, { fn finish(self) -> fmt::Result { self.result @@ -1035,18 +1151,18 @@ where impl<'a, F> VisitFmt for FieldFnVisitor<'a, F> where - F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result, + F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result, { fn writer(&mut self) -> &mut dyn fmt::Write { - &mut *self.writer + &mut self.writer } } impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FieldFnVisitor") - .field("f", &format_args!("")) - .field("writer", &format_args!("")) + .field("f", &format_args!("{}", std::any::type_name::())) + .field("writer", &self.writer) .field("result", &self.result) .finish() } @@ -1217,12 +1333,12 @@ pub(super) mod test { dispatch::{set_default, Dispatch}, }; - use super::{FmtSpan, TimingDisplay}; + use super::{FmtSpan, TimingDisplay, Writer}; use std::fmt; pub(crate) struct MockTime; impl FormatTime for MockTime { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { write!(w, "fake time") } } diff --git a/tracing-subscriber/src/fmt/format/pretty.rs b/tracing-subscriber/src/fmt/format/pretty.rs index a22fdd58a9..78acbcc04e 100644 --- a/tracing-subscriber/src/fmt/format/pretty.rs +++ b/tracing-subscriber/src/fmt/format/pretty.rs @@ -94,7 +94,7 @@ where fn format_event( &self, ctx: &FmtContext<'_, C, N>, - writer: &mut dyn fmt::Write, + mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { #[cfg(feature = "tracing-log")] @@ -103,9 +103,9 @@ where let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); #[cfg(not(feature = "tracing-log"))] let meta = event.metadata(); - write!(writer, " ")?; + write!(&mut writer, " ")?; - self.format_timestamp(writer)?; + self.format_timestamp(&mut writer)?; let style = if self.display_level && self.ansi { Pretty::style_for(meta.level()) @@ -114,7 +114,7 @@ where }; if self.display_level { - self.format_level(*meta.level(), writer)?; + self.format_level(*meta.level(), &mut writer)?; } if self.display_target { @@ -127,7 +127,7 @@ where target_style.infix(style) )?; } - let mut v = PrettyVisitor::new(writer, true) + let mut v = PrettyVisitor::new(&mut writer, true) .with_style(style) .with_ansi(self.ansi); event.record(&mut v); @@ -221,17 +221,22 @@ where impl<'writer> FormatFields<'writer> for Pretty { fn format_fields( &self, - writer: &'writer mut dyn fmt::Write, + mut writer: Writer<'writer>, fields: R, ) -> fmt::Result { - let mut v = PrettyVisitor::new(writer, true); + let mut v = PrettyVisitor::new(&mut writer, false); fields.record(&mut v); v.finish() } - fn add_fields(&self, current: &'writer mut String, fields: &span::Record<'_>) -> fmt::Result { + fn add_fields( + &self, + current: &'writer mut FormattedFields, + fields: &span::Record<'_>, + ) -> fmt::Result { let empty = current.is_empty(); - let mut v = PrettyVisitor::new(current, empty); + let mut writer = current.as_writer(); + let mut v = PrettyVisitor::new(&mut writer, empty); fields.record(&mut v); v.finish() } @@ -257,12 +262,12 @@ impl PrettyFields { } } -impl<'a> MakeVisitor<&'a mut dyn Write> for PrettyFields { +impl<'a> MakeVisitor> for PrettyFields { type Visitor = PrettyVisitor<'a>; #[inline] - fn make_visitor(&self, target: &'a mut dyn Write) -> Self::Visitor { - PrettyVisitor::new(target, true).with_ansi(self.ansi) + fn make_visitor(&self, target: Writer<'a>) -> Self::Visitor { + PrettyVisitor::new(target.writer, true).with_ansi(self.ansi) } } diff --git a/tracing-subscriber/src/fmt/time/mod.rs b/tracing-subscriber/src/fmt/time/mod.rs index 52e789fc60..5e253aa107 100644 --- a/tracing-subscriber/src/fmt/time/mod.rs +++ b/tracing-subscriber/src/fmt/time/mod.rs @@ -1,4 +1,5 @@ //! Formatters for event timestamps. +use crate::fmt::format::Writer; use std::fmt; use std::time::Instant; @@ -29,7 +30,7 @@ pub trait FormatTime { /// When `format_time` is called, implementors should get the current time using their desired /// mechanism, and write it out to the given `fmt::Write`. Implementors must insert a trailing /// space themselves if they wish to separate the time from subsequent log message text. - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result; + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result; } /// Returns a new `SystemTime` timestamp provider. @@ -69,19 +70,19 @@ impl<'a, F> FormatTime for &'a F where F: FormatTime, { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { (*self).format_time(w) } } impl FormatTime for () { - fn format_time(&self, _: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, _: &mut Writer<'_>) -> fmt::Result { Ok(()) } } -impl FormatTime for fn(&mut dyn fmt::Write) -> fmt::Result { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { +impl FormatTime for fn(&mut Writer<'_>) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { (*self)(w) } } @@ -113,7 +114,7 @@ impl From for Uptime { } impl FormatTime for SystemTime { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { write!( w, "{}", @@ -123,7 +124,7 @@ impl FormatTime for SystemTime { } impl FormatTime for Uptime { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { let e = self.epoch.elapsed(); write!(w, "{:4}.{:09}s", e.as_secs(), e.subsec_nanos()) } diff --git a/tracing-subscriber/src/fmt/time/time_crate.rs b/tracing-subscriber/src/fmt/time/time_crate.rs index 360764f5bb..bb5fdd0ed4 100644 --- a/tracing-subscriber/src/fmt/time/time_crate.rs +++ b/tracing-subscriber/src/fmt/time/time_crate.rs @@ -1,4 +1,4 @@ -use crate::fmt::{time::FormatTime, writer::WriteAdaptor}; +use crate::fmt::{format::Writer, time::FormatTime, writer::WriteAdaptor}; use std::fmt; use time::{format_description::well_known, formatting::Formattable, OffsetDateTime}; @@ -134,7 +134,7 @@ impl FormatTime for LocalTime where F: Formattable, { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?; format_datetime(now, w, &self.format) } @@ -250,7 +250,7 @@ impl FormatTime for UtcTime where F: Formattable, { - fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { + fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { format_datetime(OffsetDateTime::now_utc(), w, &self.format) } } @@ -266,7 +266,7 @@ where fn format_datetime( now: OffsetDateTime, - into: &mut dyn fmt::Write, + into: &mut Writer<'_>, fmt: &impl Formattable, ) -> fmt::Result { let mut into = WriteAdaptor::new(into);