tracing_subscriber/fmt/format/
pretty.rs

1use super::*;
2use crate::{
3    field::{VisitFmt, VisitOutput},
4    fmt::fmt_layer::{FmtContext, FormattedFields},
5    registry::LookupSpan,
6};
7
8use std::fmt;
9use tracing_core::{
10    field::{self, Field},
11    Event, Level, Subscriber,
12};
13
14#[cfg(feature = "tracing-log")]
15use tracing_log::NormalizeEvent;
16
17use nu_ansi_term::{Color, Style};
18
19/// An excessively pretty, human-readable event formatter.
20///
21/// Unlike the [`Full`], [`Compact`], and [`Json`] formatters, this is a
22/// multi-line output format. Each individual event may output multiple lines of
23/// text.
24///
25/// # Example Output
26///
27/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-pretty
28/// <font color="#4E9A06"><b>    Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
29/// <font color="#4E9A06"><b>     Running</b></font> `target/debug/examples/fmt-pretty`
30///   2022-02-15T18:44:24.535324Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: preparing to shave yaks, </font><font color="#4E9A06"><b>number_of_yaks</b></font><font color="#4E9A06">: 3</font>
31///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:16 <font color="#AAAAAA"><i>on</i></font> main
32///
33///   2022-02-15T18:44:24.535403Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty::yak_shave</b></font><font color="#4E9A06">: shaving yaks</font>
34///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:41 <font color="#AAAAAA"><i>on</i></font> main
35///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
36///
37///   2022-02-15T18:44:24.535442Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
38///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
39///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
40///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
41///
42///   2022-02-15T18:44:24.535469Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
43///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
44///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
45///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
46///
47///   2022-02-15T18:44:24.535502Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 1, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
48///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
49///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
50///
51///   2022-02-15T18:44:24.535524Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 1</font>
52///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
53///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
54///
55///   2022-02-15T18:44:24.535551Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
56///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
57///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
58///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
59///
60///   2022-02-15T18:44:24.535573Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
61///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
62///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
63///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
64///
65///   2022-02-15T18:44:24.535600Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 2, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
66///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
67///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
68///
69///   2022-02-15T18:44:24.535618Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
70///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
71///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
72///
73///   2022-02-15T18:44:24.535644Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
74///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
75///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
76///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
77///
78///   2022-02-15T18:44:24.535670Z <font color="#C4A000"> WARN</font> <font color="#C4A000"><b>fmt_pretty::yak_shave</b></font><font color="#C4A000">: could not locate yak</font>
79///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:18 <font color="#AAAAAA"><i>on</i></font> main
80///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
81///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
82///
83///   2022-02-15T18:44:24.535698Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 3, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: false</font>
84///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
85///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
86///
87///   2022-02-15T18:44:24.535720Z <font color="#CC0000">ERROR</font> <font color="#CC0000"><b>fmt_pretty::yak_shave</b></font><font color="#CC0000">: failed to shave yak, </font><font color="#CC0000"><b>yak</b></font><font color="#CC0000">: 3, </font><font color="#CC0000"><b>error</b></font><font color="#CC0000">: missing yak, </font><font color="#CC0000"><b>error.sources</b></font><font color="#CC0000">: [out of space, out of cash]</font>
88///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:51 <font color="#AAAAAA"><i>on</i></font> main
89///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
90///
91///   2022-02-15T18:44:24.535742Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
92///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
93///     <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
94///
95///   2022-02-15T18:44:24.535765Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: yak shaving completed, </font><font color="#4E9A06"><b>all_yaks_shaved</b></font><font color="#4E9A06">: false</font>
96///     <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:19 <font color="#AAAAAA"><i>on</i></font> main
97/// </pre>
98#[derive(Debug, Clone, Eq, PartialEq)]
99pub struct Pretty {
100    display_location: bool,
101}
102
103/// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation.
104///
105/// [visitor]: field::Visit
106/// [`MakeVisitor`]: crate::field::MakeVisitor
107#[derive(Debug)]
108pub struct PrettyVisitor<'a> {
109    writer: Writer<'a>,
110    is_empty: bool,
111    style: Style,
112    result: fmt::Result,
113}
114
115/// An excessively pretty, human-readable [`MakeVisitor`] implementation.
116///
117/// [`MakeVisitor`]: crate::field::MakeVisitor
118#[derive(Debug)]
119pub struct PrettyFields {
120    /// A value to override the provided `Writer`'s ANSI formatting
121    /// configuration.
122    ///
123    /// If this is `Some`, we override the `Writer`'s ANSI setting. This is
124    /// necessary in order to continue supporting the deprecated
125    /// `PrettyFields::with_ansi` method. If it is `None`, we don't override the
126    /// ANSI formatting configuration (because the deprecated method was not
127    /// called).
128    // TODO: when `PrettyFields::with_ansi` is removed, we can get rid
129    // of this entirely.
130    ansi: Option<bool>,
131}
132
133// === impl Pretty ===
134
135impl Default for Pretty {
136    fn default() -> Self {
137        Self {
138            display_location: true,
139        }
140    }
141}
142
143impl Pretty {
144    fn style_for(level: &Level) -> Style {
145        match *level {
146            Level::TRACE => Style::new().fg(Color::Purple),
147            Level::DEBUG => Style::new().fg(Color::Blue),
148            Level::INFO => Style::new().fg(Color::Green),
149            Level::WARN => Style::new().fg(Color::Yellow),
150            Level::ERROR => Style::new().fg(Color::Red),
151        }
152    }
153
154    /// Sets whether the event's source code location is displayed.
155    ///
156    /// This defaults to `true`.
157    #[deprecated(
158        since = "0.3.6",
159        note = "all formatters now support configurable source locations. Use `Format::with_source_location` instead."
160    )]
161    pub fn with_source_location(self, display_location: bool) -> Self {
162        Self {
163            display_location,
164            ..self
165        }
166    }
167}
168
169impl<C, N, T> FormatEvent<C, N> for Format<Pretty, T>
170where
171    C: Subscriber + for<'a> LookupSpan<'a>,
172    N: for<'a> FormatFields<'a> + 'static,
173    T: FormatTime,
174{
175    fn format_event(
176        &self,
177        ctx: &FmtContext<'_, C, N>,
178        mut writer: Writer<'_>,
179        event: &Event<'_>,
180    ) -> fmt::Result {
181        #[cfg(feature = "tracing-log")]
182        let normalized_meta = event.normalized_metadata();
183        #[cfg(feature = "tracing-log")]
184        let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
185        #[cfg(not(feature = "tracing-log"))]
186        let meta = event.metadata();
187        write!(&mut writer, "  ")?;
188
189        // if the `Format` struct *also* has an ANSI color configuration,
190        // override the writer...the API for configuring ANSI color codes on the
191        // `Format` struct is deprecated, but we still need to honor those
192        // configurations.
193        if let Some(ansi) = self.ansi {
194            writer = writer.with_ansi(ansi);
195        }
196
197        self.format_timestamp(&mut writer)?;
198
199        let style = if self.display_level && writer.has_ansi_escapes() {
200            Pretty::style_for(meta.level())
201        } else {
202            Style::new()
203        };
204
205        if self.display_level {
206            write!(
207                writer,
208                "{} ",
209                super::FmtLevel::new(meta.level(), writer.has_ansi_escapes())
210            )?;
211        }
212
213        if self.display_target {
214            let target_style = if writer.has_ansi_escapes() {
215                style.bold()
216            } else {
217                style
218            };
219            write!(
220                writer,
221                "{}{}{}:",
222                target_style.prefix(),
223                meta.target(),
224                target_style.infix(style)
225            )?;
226        }
227        let line_number = if self.display_line_number {
228            meta.line()
229        } else {
230            None
231        };
232
233        // If the file name is disabled, format the line number right after the
234        // target. Otherwise, if we also display the file, it'll go on a
235        // separate line.
236        if let (Some(line_number), false, true) = (
237            line_number,
238            self.display_filename,
239            self.format.display_location,
240        ) {
241            write!(
242                writer,
243                "{}{}{}:",
244                style.prefix(),
245                line_number,
246                style.infix(style)
247            )?;
248        }
249
250        writer.write_char(' ')?;
251
252        let mut v = PrettyVisitor::new(writer.by_ref(), true).with_style(style);
253        event.record(&mut v);
254        v.finish()?;
255        writer.write_char('\n')?;
256
257        let dimmed = if writer.has_ansi_escapes() {
258            Style::new().dimmed().italic()
259        } else {
260            Style::new()
261        };
262        let thread = self.display_thread_name || self.display_thread_id;
263
264        if let (Some(file), true, true) = (
265            meta.file(),
266            self.format.display_location,
267            self.display_filename,
268        ) {
269            write!(writer, "    {} {}", dimmed.paint("at"), file,)?;
270
271            if let Some(line) = line_number {
272                write!(writer, ":{}", line)?;
273            }
274            writer.write_char(if thread { ' ' } else { '\n' })?;
275        } else if thread {
276            write!(writer, "    ")?;
277        };
278
279        if thread {
280            write!(writer, "{} ", dimmed.paint("on"))?;
281            let thread = std::thread::current();
282            if self.display_thread_name {
283                if let Some(name) = thread.name() {
284                    write!(writer, "{}", name)?;
285                    if self.display_thread_id {
286                        writer.write_char(' ')?;
287                    }
288                }
289            }
290            if self.display_thread_id {
291                write!(writer, "{:?}", thread.id())?;
292            }
293            writer.write_char('\n')?;
294        }
295
296        let bold = writer.bold();
297        let span = event
298            .parent()
299            .and_then(|id| ctx.span(id))
300            .or_else(|| ctx.lookup_current());
301
302        let scope = span.into_iter().flat_map(|span| span.scope());
303
304        for span in scope {
305            let meta = span.metadata();
306            if self.display_target {
307                write!(
308                    writer,
309                    "    {} {}::{}",
310                    dimmed.paint("in"),
311                    meta.target(),
312                    bold.paint(meta.name()),
313                )?;
314            } else {
315                write!(
316                    writer,
317                    "    {} {}",
318                    dimmed.paint("in"),
319                    bold.paint(meta.name()),
320                )?;
321            }
322
323            let ext = span.extensions();
324            let fields = &ext
325                .get::<FormattedFields<N>>()
326                .expect("Unable to find FormattedFields in extensions; this is a bug");
327            if !fields.is_empty() {
328                write!(writer, " {} {}", dimmed.paint("with"), fields)?;
329            }
330            writer.write_char('\n')?;
331        }
332
333        writer.write_char('\n')
334    }
335}
336
337impl<'writer> FormatFields<'writer> for Pretty {
338    fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result {
339        let mut v = PrettyVisitor::new(writer, true);
340        fields.record(&mut v);
341        v.finish()
342    }
343
344    fn add_fields(
345        &self,
346        current: &'writer mut FormattedFields<Self>,
347        fields: &span::Record<'_>,
348    ) -> fmt::Result {
349        let empty = current.is_empty();
350        let writer = current.as_writer();
351        let mut v = PrettyVisitor::new(writer, empty);
352        fields.record(&mut v);
353        v.finish()
354    }
355}
356
357// === impl PrettyFields ===
358
359impl Default for PrettyFields {
360    fn default() -> Self {
361        Self::new()
362    }
363}
364
365impl PrettyFields {
366    /// Returns a new default [`PrettyFields`] implementation.
367    pub fn new() -> Self {
368        // By default, don't override the `Writer`'s ANSI colors
369        // configuration. We'll only do this if the user calls the
370        // deprecated `PrettyFields::with_ansi` method.
371        Self { ansi: None }
372    }
373
374    /// Enable ANSI encoding for formatted fields.
375    #[deprecated(
376        since = "0.3.3",
377        note = "Use `fmt::Subscriber::with_ansi` or `fmt::Layer::with_ansi` instead."
378    )]
379    pub fn with_ansi(self, ansi: bool) -> Self {
380        Self {
381            ansi: Some(ansi),
382            ..self
383        }
384    }
385}
386
387impl<'a> MakeVisitor<Writer<'a>> for PrettyFields {
388    type Visitor = PrettyVisitor<'a>;
389
390    #[inline]
391    fn make_visitor(&self, mut target: Writer<'a>) -> Self::Visitor {
392        if let Some(ansi) = self.ansi {
393            target = target.with_ansi(ansi);
394        }
395        PrettyVisitor::new(target, true)
396    }
397}
398
399// === impl PrettyVisitor ===
400
401impl<'a> PrettyVisitor<'a> {
402    /// Returns a new default visitor that formats to the provided `writer`.
403    ///
404    /// # Arguments
405    /// - `writer`: the writer to format to.
406    /// - `is_empty`: whether or not any fields have been previously written to
407    ///   that writer.
408    pub fn new(writer: Writer<'a>, is_empty: bool) -> Self {
409        Self {
410            writer,
411            is_empty,
412            style: Style::default(),
413            result: Ok(()),
414        }
415    }
416
417    pub(crate) fn with_style(self, style: Style) -> Self {
418        Self { style, ..self }
419    }
420
421    fn write_padded(&mut self, value: &impl fmt::Debug) {
422        let padding = if self.is_empty {
423            self.is_empty = false;
424            ""
425        } else {
426            ", "
427        };
428        self.result = write!(self.writer, "{}{:?}", padding, value);
429    }
430
431    fn bold(&self) -> Style {
432        if self.writer.has_ansi_escapes() {
433            self.style.bold()
434        } else {
435            Style::new()
436        }
437    }
438}
439
440impl field::Visit for PrettyVisitor<'_> {
441    fn record_str(&mut self, field: &Field, value: &str) {
442        if self.result.is_err() {
443            return;
444        }
445
446        if field.name() == "message" {
447            self.record_debug(field, &format_args!("{}", value))
448        } else {
449            self.record_debug(field, &value)
450        }
451    }
452
453    fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
454        if let Some(source) = value.source() {
455            let bold = self.bold();
456            self.record_debug(
457                field,
458                &format_args!(
459                    "{}, {}{}.sources{}: {}",
460                    value,
461                    bold.prefix(),
462                    field,
463                    bold.infix(self.style),
464                    ErrorSourceList(source),
465                ),
466            )
467        } else {
468            self.record_debug(field, &format_args!("{}", value))
469        }
470    }
471
472    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
473        if self.result.is_err() {
474            return;
475        }
476        let bold = self.bold();
477        match field.name() {
478            "message" => self.write_padded(&format_args!("{}{:?}", self.style.prefix(), value,)),
479            // Skip fields that are actually log metadata that have already been handled
480            #[cfg(feature = "tracing-log")]
481            name if name.starts_with("log.") => self.result = Ok(()),
482            name if name.starts_with("r#") => self.write_padded(&format_args!(
483                "{}{}{}: {:?}",
484                bold.prefix(),
485                &name[2..],
486                bold.infix(self.style),
487                value
488            )),
489            name => self.write_padded(&format_args!(
490                "{}{}{}: {:?}",
491                bold.prefix(),
492                name,
493                bold.infix(self.style),
494                value
495            )),
496        };
497    }
498}
499
500impl VisitOutput<fmt::Result> for PrettyVisitor<'_> {
501    fn finish(mut self) -> fmt::Result {
502        write!(&mut self.writer, "{}", self.style.suffix())?;
503        self.result
504    }
505}
506
507impl VisitFmt for PrettyVisitor<'_> {
508    fn writer(&mut self) -> &mut dyn fmt::Write {
509        &mut self.writer
510    }
511}