1use crate::printer::Formatter;
2use crate::tree::{Event, Shared, Span, Tree};
3use crate::Tag;
4use std::fmt::{self, Write};
5
6#[cfg(feature = "smallvec")]
7type IndentVec = smallvec::SmallVec<[Indent; 32]>;
8#[cfg(not(feature = "smallvec"))]
9type IndentVec = Vec<Indent>;
10
11#[cfg(feature = "ansi")]
12use ansi_term::Color;
13#[cfg(feature = "ansi")]
14use tracing::Level;
15
16#[derive(Debug)]
62pub struct Pretty;
63
64impl Formatter for Pretty {
65 type Error = fmt::Error;
66
67 fn fmt(&self, tree: &Tree) -> Result<String, fmt::Error> {
68 let mut writer = String::with_capacity(256);
69
70 Pretty::format_tree(tree, None, &mut IndentVec::new(), &mut writer)?;
71
72 Ok(writer)
73 }
74}
75
76impl Pretty {
77 fn format_tree(
78 tree: &Tree,
79 duration_root: Option<f64>,
80 indent: &mut IndentVec,
81 writer: &mut String,
82 ) -> fmt::Result {
83 match tree {
84 Tree::Event(event) => {
85 Pretty::format_shared(&event.shared, writer)?;
86 Pretty::format_indent(indent, writer)?;
87 Pretty::format_event(event, writer)
88 }
89 Tree::Span(span) => {
90 Pretty::format_shared(&span.shared, writer)?;
91 Pretty::format_indent(indent, writer)?;
92 Pretty::format_span(span, duration_root, indent, writer)
93 }
94 }
95 }
96
97 fn format_shared(shared: &Shared, writer: &mut String) -> fmt::Result {
98 #[cfg(feature = "uuid")]
99 write!(writer, "{} ", shared.uuid)?;
100
101 #[cfg(feature = "chrono")]
102 write!(writer, "{:<36} ", shared.timestamp.to_rfc3339())?;
103
104 #[cfg(feature = "ansi")]
105 return write!(writer, "{:<8} ", ColorLevel(shared.level));
106
107 #[cfg(not(feature = "ansi"))]
108 return write!(writer, "{:<8} ", shared.level);
109 }
110
111 fn format_indent(indent: &[Indent], writer: &mut String) -> fmt::Result {
112 for indent in indent {
113 writer.write_str(indent.repr())?;
114 }
115 Ok(())
116 }
117
118 fn format_event(event: &Event, writer: &mut String) -> fmt::Result {
119 let tag = event.tag().unwrap_or_else(|| Tag::from(event.level()));
120
121 write!(writer, "{} [{}]: ", tag.icon(), tag)?;
122
123 if let Some(message) = event.message() {
124 writer.write_str(message)?;
125 }
126
127 for field in event.fields() {
128 write!(writer, " | {}: {}", FieldKey(field.key()), field.value())?;
129 }
130
131 writeln!(writer)
132 }
133
134 fn format_span(
135 span: &Span,
136 duration_root: Option<f64>,
137 indent: &mut IndentVec,
138 writer: &mut String,
139 ) -> fmt::Result {
140 if !span.should_render() {
141 return Ok(());
142 }
143
144 let total_duration = span.total_duration().as_nanos() as f64;
145 let inner_duration = span.inner_duration().as_nanos() as f64;
146 let root_duration = duration_root.unwrap_or(total_duration);
147 let percent_total_of_root_duration = 100.0 * total_duration / root_duration;
148
149 write!(
150 writer,
151 "{} [ {} | ",
152 span.name(),
153 DurationDisplay(total_duration)
154 )?;
155
156 if inner_duration > 0.0 {
157 let base_duration = span.base_duration().as_nanos() as f64;
158 let percent_base_of_root_duration = 100.0 * base_duration / root_duration;
159 write!(writer, "{percent_base_of_root_duration:.2}% / ")?;
160 }
161
162 write!(writer, "{percent_total_of_root_duration:.2}% ]")?;
163
164 for (n, field) in span.shared.fields.iter().enumerate() {
165 write!(
166 writer,
167 "{} {}: {}",
168 if n == 0 { "" } else { " |" },
169 FieldKey(field.key()),
170 field.value()
171 )?;
172 }
173 writeln!(writer)?;
174
175 let nodes: Vec<_> = span
178 .nodes()
179 .iter()
180 .filter(|node| node.should_render())
181 .collect();
182
183 if let Some((last, remaining)) = nodes.split_last() {
184 match indent.last_mut() {
185 Some(edge @ Indent::Turn) => *edge = Indent::Null,
186 Some(edge @ Indent::Fork) => *edge = Indent::Line,
187 _ => {}
188 }
189
190 indent.push(Indent::Fork);
191
192 for tree in remaining {
193 if let Some(edge) = indent.last_mut() {
194 *edge = Indent::Fork;
195 }
196 Pretty::format_tree(tree, Some(root_duration), indent, writer)?;
197 }
198
199 if let Some(edge) = indent.last_mut() {
200 *edge = Indent::Turn;
201 }
202 Pretty::format_tree(last, Some(root_duration), indent, writer)?;
203
204 indent.pop();
205 }
206
207 Ok(())
208 }
209}
210
211enum Indent {
212 Null,
213 Line,
214 Fork,
215 Turn,
216}
217
218impl Indent {
219 fn repr(&self) -> &'static str {
220 match self {
221 Self::Null => " ",
222 Self::Line => "│ ",
223 Self::Fork => "┝━ ",
224 Self::Turn => "┕━ ",
225 }
226 }
227}
228
229struct DurationDisplay(f64);
230
231impl fmt::Display for DurationDisplay {
233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234 let mut t = self.0;
235 for unit in ["ns", "µs", "ms", "s"] {
236 if t < 10.0 {
237 return write!(f, "{t:.2}{unit}");
238 } else if t < 100.0 {
239 return write!(f, "{t:.1}{unit}");
240 } else if t < 1000.0 {
241 return write!(f, "{t:.0}{unit}");
242 }
243 t /= 1000.0;
244 }
245 write!(f, "{:.0}s", t * 1000.0)
246 }
247}
248
249struct FieldKey<'a>(&'a str);
251
252impl fmt::Display for FieldKey<'_> {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 #[cfg(feature = "ansi")]
255 {
256 let color = Color::White.dimmed();
257
258 write!(f, "{}{}{}", color.prefix(), self.0, color.suffix())
259 }
260 #[cfg(not(feature = "ansi"))]
261 {
262 f.write_str(self.0)
263 }
264 }
265}
266
267#[cfg(feature = "ansi")]
269struct ColorLevel(Level);
270
271#[cfg(feature = "ansi")]
272impl fmt::Display for ColorLevel {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 let color = match self.0 {
275 Level::TRACE => Color::Purple,
276 Level::DEBUG => Color::Blue,
277 Level::INFO => Color::Green,
278 Level::WARN => Color::RGB(252, 234, 160), Level::ERROR => Color::Red,
280 };
281 let style = color.bold();
282 write!(f, "{}", style.prefix())?;
283 f.pad(self.0.as_str())?;
284 write!(f, "{}", style.suffix())
285 }
286}