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().iter() {
128 write!(writer, " | {}: {}", 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 let total_duration = span.total_duration().as_nanos() as f64;
141 let inner_duration = span.inner_duration().as_nanos() as f64;
142 let root_duration = duration_root.unwrap_or(total_duration);
143 let percent_total_of_root_duration = 100.0 * total_duration / root_duration;
144
145 write!(
146 writer,
147 "{} [ {} | ",
148 span.name(),
149 DurationDisplay(total_duration)
150 )?;
151
152 if inner_duration > 0.0 {
153 let base_duration = span.base_duration().as_nanos() as f64;
154 let percent_base_of_root_duration = 100.0 * base_duration / root_duration;
155 write!(writer, "{:.2}% / ", percent_base_of_root_duration)?;
156 }
157
158 write!(writer, "{:.2}% ]", percent_total_of_root_duration)?;
159
160 for (n, field) in span.shared.fields.iter().enumerate() {
161 write!(
162 writer,
163 "{} {}: {}",
164 if n == 0 { "" } else { " |" },
165 field.key(),
166 field.value()
167 )?;
168 }
169 writeln!(writer)?;
170
171 if let Some((last, remaining)) = span.nodes().split_last() {
172 match indent.last_mut() {
173 Some(edge @ Indent::Turn) => *edge = Indent::Null,
174 Some(edge @ Indent::Fork) => *edge = Indent::Line,
175 _ => {}
176 }
177
178 indent.push(Indent::Fork);
179
180 for tree in remaining {
181 if let Some(edge) = indent.last_mut() {
182 *edge = Indent::Fork;
183 }
184 Pretty::format_tree(tree, Some(root_duration), indent, writer)?;
185 }
186
187 if let Some(edge) = indent.last_mut() {
188 *edge = Indent::Turn;
189 }
190 Pretty::format_tree(last, Some(root_duration), indent, writer)?;
191
192 indent.pop();
193 }
194
195 Ok(())
196 }
197}
198
199enum Indent {
200 Null,
201 Line,
202 Fork,
203 Turn,
204}
205
206impl Indent {
207 fn repr(&self) -> &'static str {
208 match self {
209 Self::Null => " ",
210 Self::Line => "│ ",
211 Self::Fork => "┝━ ",
212 Self::Turn => "┕━ ",
213 }
214 }
215}
216
217struct DurationDisplay(f64);
218
219impl fmt::Display for DurationDisplay {
221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222 let mut t = self.0;
223 for unit in ["ns", "µs", "ms", "s"] {
224 if t < 10.0 {
225 return write!(f, "{:.2}{}", t, unit);
226 } else if t < 100.0 {
227 return write!(f, "{:.1}{}", t, unit);
228 } else if t < 1000.0 {
229 return write!(f, "{:.0}{}", t, unit);
230 }
231 t /= 1000.0;
232 }
233 write!(f, "{:.0}s", t * 1000.0)
234 }
235}
236
237#[cfg(feature = "ansi")]
239struct ColorLevel(Level);
240
241#[cfg(feature = "ansi")]
242impl fmt::Display for ColorLevel {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 let color = match self.0 {
245 Level::TRACE => Color::Purple,
246 Level::DEBUG => Color::Blue,
247 Level::INFO => Color::Green,
248 Level::WARN => Color::RGB(252, 234, 160), Level::ERROR => Color::Red,
250 };
251 let style = color.bold();
252 write!(f, "{}", style.prefix())?;
253 f.pad(self.0.as_str())?;
254 write!(f, "{}", style.suffix())
255 }
256}