tracing_human_layer/
style.rs

1use std::fmt::Display;
2
3use owo_colors::Style as OwoStyle;
4use tracing::Level;
5
6use super::SpanInfo;
7
8#[derive(Debug)]
9pub struct Style {
10    /// First-line indent text.
11    indent_text: &'static str,
12
13    /// Subsequent indent text.
14    pub subsequent_indent: &'static str,
15
16    /// Should output be colored?
17    color: bool,
18
19    /// Style for first-line indent text.
20    indent: OwoStyle,
21
22    /// Style for message text.
23    text: OwoStyle,
24
25    /// Style for field names.
26    field_name: OwoStyle,
27
28    /// Style for field values.
29    field_value: OwoStyle,
30
31    /// Style for span names.
32    span_name: OwoStyle,
33
34    /// Style for `in` in `in span ...`
35    span_in: OwoStyle,
36}
37
38impl Style {
39    pub fn new(level: Level, color: bool) -> Self {
40        let indent_text;
41        let span_in = OwoStyle::new().dimmed();
42        let mut indent = OwoStyle::new();
43        let mut text = OwoStyle::new();
44        let mut field_name = OwoStyle::new().bold();
45        let mut field_value = OwoStyle::new();
46        let mut span_name = OwoStyle::new();
47
48        match level {
49            Level::TRACE => {
50                indent_text = "TRACE ";
51                indent = indent.purple();
52                text = text.dimmed();
53                field_name = field_name.dimmed();
54                field_value = field_value.dimmed();
55                span_name = span_name.dimmed();
56            }
57            Level::DEBUG => {
58                indent_text = "DEBUG ";
59                indent = indent.blue();
60                text = text.dimmed();
61                field_name = field_name.dimmed();
62                field_value = field_value.dimmed();
63                span_name = span_name.dimmed();
64            }
65            Level::INFO => {
66                indent_text = "• ";
67                indent = indent.green();
68            }
69            Level::WARN => {
70                indent_text = "⚠ ";
71                indent = indent.yellow();
72                text = text.yellow();
73            }
74            Level::ERROR => {
75                indent_text = "⚠ ";
76                indent = indent.red();
77                text = text.red();
78            }
79        }
80
81        Self {
82            indent_text,
83            subsequent_indent: "  ",
84            color,
85            indent,
86            text,
87            field_name,
88            field_value,
89            span_name,
90            span_in,
91        }
92    }
93
94    pub fn style_field(&self, name: &str, value: &str) -> String {
95        format!(
96            "{name}{value}",
97            name = name.colored(self.color, self.field_name),
98            value = format!("={value}").colored(self.color, self.field_value),
99        )
100    }
101
102    pub fn indent_colored(&self) -> String {
103        self.indent_text
104            .colored(self.color, self.indent)
105            .to_string()
106    }
107
108    pub fn style_message(&self, message: &str) -> String {
109        message.colored(self.color, self.text).to_string()
110    }
111
112    pub fn style_span_name(&self, name: &str) -> String {
113        name.colored(self.color, self.span_name).to_string()
114    }
115
116    pub fn style_span(&self, span: &SpanInfo) -> String {
117        format!(
118            "{in_}{name}{fields}",
119            in_ = "in ".colored(self.color, self.span_in),
120            name = span.name.colored(self.color, self.span_name),
121            fields = span.fields,
122        )
123    }
124}
125
126trait IntoConditionalColor: Display {
127    fn colored(&self, color: bool, style: OwoStyle) -> ConditionalColor<&Self> {
128        ConditionalColor::new(self).color(color).style(style)
129    }
130}
131
132impl<T> IntoConditionalColor for T where T: Display {}
133
134/// Like `if_supports_color`, but I control it :)
135struct ConditionalColor<T> {
136    color: bool,
137    style: OwoStyle,
138    inner: T,
139}
140
141impl<T> ConditionalColor<T> {
142    pub fn new(inner: T) -> Self {
143        Self {
144            color: true,
145            style: OwoStyle::new(),
146            inner,
147        }
148    }
149
150    pub fn color(mut self, color: bool) -> Self {
151        self.color = color;
152        self
153    }
154
155    pub fn style(mut self, style: OwoStyle) -> Self {
156        self.style = style;
157        self
158    }
159}
160
161impl<T> Display for ConditionalColor<T>
162where
163    T: Display,
164{
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        if self.color {
167            self.style.style(&self.inner).fmt(f)
168        } else {
169            self.inner.fmt(f)
170        }
171    }
172}