Skip to main content

arti/logging/
fields.rs

1//! Field formatters for [`tracing_subscriber`].
2
3use std::error::Error;
4use std::fmt::Debug;
5
6use tracing::field::Field;
7use tracing_subscriber::field::{RecordFields, Visit, VisitOutput};
8use tracing_subscriber::fmt::format::{DefaultVisitor, FormatFields, Writer};
9
10/// Visits only `dyn Error`.
11struct ErrorVisitor<'a>(&'a mut dyn Visit);
12
13// this just wraps an existing visitor, so if the trait methods gain a return type in the future,
14// we want to just pass it through
15#[allow(clippy::semicolon_if_nothing_returned)]
16impl<'a> Visit for ErrorVisitor<'a> {
17    // do nothing
18    fn record_debug(&mut self, _field: &Field, _value: &dyn Debug) {}
19    fn record_f64(&mut self, _field: &Field, _value: f64) {}
20    fn record_i64(&mut self, _field: &Field, _value: i64) {}
21    fn record_u64(&mut self, _field: &Field, _value: u64) {}
22    fn record_i128(&mut self, _field: &Field, _value: i128) {}
23    fn record_u128(&mut self, _field: &Field, _value: u128) {}
24    fn record_bool(&mut self, _field: &Field, _value: bool) {}
25    fn record_str(&mut self, _field: &Field, _value: &str) {}
26    fn record_bytes(&mut self, _field: &Field, _value: &[u8]) {}
27
28    // format the error and use the inner visitor
29    fn record_error(&mut self, field: &Field, value: &(dyn Error + 'static)) {
30        use std::fmt;
31        use tor_error::ErrorReport as _;
32
33        /// Wrapper to add a `Debug` impl to something that implements `Display`.
34        struct DisplayToDebug<T: fmt::Display>(T);
35
36        impl<T: fmt::Display> fmt::Debug for DisplayToDebug<T> {
37            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38                fmt::Display::fmt(&self.0, f)
39            }
40        }
41
42        // use `ErrorReport` to format the error
43        self.0.record_debug(field, &DisplayToDebug(value.report()))
44    }
45}
46
47/// Visits everything but `dyn Error`.
48struct NonErrorVisitor<'a>(&'a mut dyn Visit);
49
50// this just wraps an existing visitor, so if the trait methods gain a return type in the future,
51// we want to just pass it through
52#[allow(clippy::semicolon_if_nothing_returned)]
53impl<'a> Visit for NonErrorVisitor<'a> {
54    // use the inner visitor
55    fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
56        self.0.record_debug(field, value)
57    }
58    fn record_f64(&mut self, field: &Field, value: f64) {
59        self.0.record_f64(field, value)
60    }
61    fn record_i64(&mut self, field: &Field, value: i64) {
62        self.0.record_i64(field, value)
63    }
64    fn record_u64(&mut self, field: &Field, value: u64) {
65        self.0.record_u64(field, value)
66    }
67    fn record_i128(&mut self, field: &Field, value: i128) {
68        self.0.record_i128(field, value)
69    }
70    fn record_u128(&mut self, field: &Field, value: u128) {
71        self.0.record_u128(field, value)
72    }
73    fn record_bool(&mut self, field: &Field, value: bool) {
74        self.0.record_bool(field, value)
75    }
76    fn record_str(&mut self, field: &Field, value: &str) {
77        self.0.record_str(field, value)
78    }
79    fn record_bytes(&mut self, field: &Field, value: &[u8]) {
80        self.0.record_bytes(field, value)
81    }
82
83    // do nothing
84    fn record_error(&mut self, _field: &Field, _value: &(dyn Error + 'static)) {}
85}
86
87/// Log error fields after other fields.
88pub(crate) struct ErrorsLastFieldFormatter;
89
90impl<'writer> FormatFields<'writer> for ErrorsLastFieldFormatter {
91    fn format_fields<R: RecordFields>(
92        &self,
93        mut writer: Writer<'writer>,
94        fields: R,
95    ) -> std::fmt::Result {
96        // we use a visitor from `tracing_subscriber` for formatting fields
97        let mut visitor = DefaultVisitor::new(writer.by_ref(), /* is_empty= */ true);
98
99        // record non-error fields first, then record error fields
100        fields.record(&mut NonErrorVisitor(&mut visitor));
101        fields.record(&mut ErrorVisitor(&mut visitor));
102
103        visitor.finish()?;
104
105        Ok(())
106    }
107}