tracing_helpers/
formatter.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Formatter for use with tracing crate.
5
6use std::fmt;
7use std::io;
8use std::io::Write;
9use tracing::field::Visit;
10use tracing_subscriber::field::RecordFields;
11use tracing_subscriber::fmt::FormatFields;
12use tracing_subscriber::fmt::format::Writer;
13
14struct FieldFormatterVisitor<'a> {
15    writer: Writer<'a>,
16    is_empty: bool,
17    result: fmt::Result,
18}
19
20impl FieldFormatterVisitor<'_> {
21    fn maybe_pad(&mut self) {
22        if self.is_empty {
23            self.is_empty = false;
24        } else {
25            self.result = write!(self.writer, " ");
26        }
27    }
28
29    fn record_display(&mut self, field: &tracing::field::Field, value: &dyn fmt::Display) {
30        if self.result.is_err() {
31            return;
32        }
33
34        self.maybe_pad();
35        self.result = match field.name() {
36            "message" => write!(self.writer, "{}", value),
37            // Skip fields that are actually log metadata that have already been handled
38            name if name.starts_with("log.") => Ok(()),
39            name if name.starts_with("r#") => write!(self.writer, "{}={}", &name[2..], value),
40            name => write!(self.writer, "{}={}", name, value),
41        };
42    }
43}
44
45impl Visit for FieldFormatterVisitor<'_> {
46    fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
47        self.record_display(field, &value)
48    }
49
50    fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
51        self.record_display(field, &value)
52    }
53
54    fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
55        // Use hex encoding for better readability for most values.
56        self.record_display(field, &format_args!("{:#x}", value))
57    }
58
59    fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
60        self.record_display(field, &value)
61    }
62
63    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
64        self.record_display(field, &format_args!("{:?}", value))
65    }
66
67    fn record_error(
68        &mut self,
69        field: &tracing::field::Field,
70        mut value: &(dyn std::error::Error + 'static),
71    ) {
72        self.record_debug(field, &format_args!("{}", value));
73        while let Some(s) = value.source() {
74            value = s;
75            if self.result.is_err() {
76                return;
77            }
78            self.result = write!(self.writer, ": {}", value);
79        }
80    }
81
82    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) {
83        // Use hex encoding for better readability for most values.
84        self.record_display(field, &format_args!("{:x?}", value))
85    }
86}
87
88/// Field formatter that fixes a few issues with the default formatter:
89///
90/// 1. Displays the full error source chain, not just the first error.
91/// 2. Displays unsigned values as hex instead of decimal, improving readability
92///    for values that we tend to log in HvLite.
93pub struct FieldFormatter;
94
95impl FormatFields<'_> for FieldFormatter {
96    fn format_fields<R: RecordFields>(&self, writer: Writer<'_>, fields: R) -> fmt::Result {
97        let mut visitor = FieldFormatterVisitor {
98            writer,
99            is_empty: false,
100            result: Ok(()),
101        };
102        fields.record(&mut visitor);
103        visitor.result
104    }
105}
106
107/// A Write implementation that wraps `T` and converts LFs into CRLFs.
108pub struct CrlfWriter<T> {
109    inner: T,
110    write_lf: bool,
111}
112
113impl<T: Write> CrlfWriter<T> {
114    /// Creates a new writer around `t`.
115    pub fn new(t: T) -> Self {
116        CrlfWriter {
117            inner: t,
118            write_lf: false,
119        }
120    }
121
122    fn flush_lf(&mut self) -> io::Result<()> {
123        if self.write_lf {
124            if self.inner.write(b"\n")? == 0 {
125                return Err(io::ErrorKind::WriteZero.into());
126            }
127            self.write_lf = false;
128        }
129        Ok(())
130    }
131}
132
133impl<T: Write> Write for CrlfWriter<T> {
134    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
135        self.flush_lf()?;
136        if buf.first() == Some(&b'\n') {
137            return Ok(match self.inner.write(b"\r\n")? {
138                0 => 0,
139                1 => {
140                    self.write_lf = true;
141                    1
142                }
143                _ => 1,
144            });
145        }
146
147        let len = buf.iter().position(|x| *x == b'\n').unwrap_or(buf.len());
148        self.inner.write(&buf[..len])
149    }
150
151    fn flush(&mut self) -> io::Result<()> {
152        self.flush_lf()?;
153        self.inner.flush()
154    }
155}