1
//! Field formatters for [`tracing_subscriber`].
2

            
3
use std::error::Error;
4
use std::fmt::Debug;
5

            
6
use tracing::field::Field;
7
use tracing_subscriber::field::{RecordFields, Visit, VisitOutput};
8
use tracing_subscriber::fmt::format::{DefaultVisitor, FormatFields, Writer};
9

            
10
/// Visits only `dyn Error`.
11
struct 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)]
16
impl<'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`.
48
struct 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)]
53
impl<'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.
88
pub(crate) struct ErrorsLastFieldFormatter;
89

            
90
impl<'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
}