1#![doc = include_str!("../README.md")]
2
3use {
4 ::core::fmt::{Debug, Write as _},
5 ::nu_ansi_term::{Color, Style},
6 ::std::{
7 io::{self, IsTerminal, Write},
8 sync::Mutex,
9 },
10 ::tracing_core::{
11 field::Visit,
12 span::{Attributes, Id, Record},
13 Event,
14 Field,
15 Level,
16 Metadata,
17 Subscriber,
18 },
19 ::tracing_subscriber::{layer::Context, registry::LookupSpan, Layer},
20};
21
22fn span_continue_marker(sym: bool) -> &'static str {
23 if sym {
24 "│ "
25 } else {
26 "| "
27 }
28}
29fn span_close_marker(sym: bool) -> &'static str {
30 if sym {
31 "╰·"
32 } else {
33 "\\-"
34 }
35}
36
37fn event_marker(level: Level, sym: bool) -> (Style, &'static str) {
38 (
39 match level {
40 Level::ERROR => Color::Red,
41 Level::WARN => Color::Yellow,
42 Level::INFO => Color::Blue,
43 Level::DEBUG => Color::Magenta,
44 Level::TRACE => Color::DarkGray,
45 }
46 .bold(),
47 if sym {
48 match level {
49 Level::ERROR => " ",
50 Level::WARN => " ",
51 Level::INFO => " ",
52 Level::DEBUG => " ",
53 Level::TRACE => " ",
54 }
55 } else {
56 match level {
57 Level::ERROR => "[err] ",
58 Level::WARN => "[wrn] ",
59 Level::INFO => "[inf] ",
60 Level::DEBUG => "[dbg] ",
61 Level::TRACE => "[trc] ",
62 }
63 },
64 )
65}
66
67pub struct FancyTree<O: Write + 'static> {
69 inner: Mutex<Inner<O>>,
70}
71
72impl<O: Write + 'static> FancyTree<O> {
73 pub fn new(out: O, ansi: bool, sym: bool) -> Self {
79 Self {
80 inner: Mutex::new(Inner {
81 ansi,
82 sym,
83 out,
84 indent: Vec::new(),
85 }),
86 }
87 }
88}
89
90impl Default for FancyTree<io::Stdout> {
91 fn default() -> Self {
92 let out = io::stdout();
93 let ansi = out.is_terminal();
94 Self::new(out, ansi, ansi)
95 }
96}
97
98impl<S: Subscriber + for<'span> LookupSpan<'span, Data: Debug> + Debug, O: Write + 'static> Layer<S>
99 for FancyTree<O>
100{
101 fn on_new_span(&self, span: &Attributes<'_>, _: &Id, _: Context<'_, S>) {
102 let mut inner = self.inner.lock().unwrap();
103
104 inner.line_prefix();
105
106 let level = *span.metadata().level();
107 let (marker_style, marker) = event_marker(level, inner.sym);
108 if inner.ansi {
109 let style = Color::Green.normal();
110 write!(
111 inner.out,
112 "{}{marker}{}{} {}({}){}",
113 marker_style.prefix(),
114 span.metadata().name(),
115 marker_style.suffix(),
116 style.prefix(),
117 span.metadata().target(),
118 style.suffix(),
119 )
120 .unwrap();
121 } else {
122 write!(
123 inner.out,
124 "{marker}{} ({})",
125 span.metadata().name(),
126 span.metadata().target(),
127 )
128 .unwrap();
129 }
130
131 write_file(&mut *inner, span.metadata());
132
133 writeln!(inner.out).unwrap();
134
135 inner.indent.push(level);
136
137 span.values().record(&mut FieldDisplayVisitor {
138 inner: &mut inner,
139 indent: "",
140 ignore_message: false,
141 });
142 }
143
144 fn on_record(&self, _: &Id, values: &Record<'_>, _: Context<'_, S>) {
145 let mut inner = self.inner.lock().unwrap();
146 values.record(&mut FieldDisplayVisitor {
147 inner: &mut inner,
148 indent: "",
149 ignore_message: false,
150 });
151 }
152
153 fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
154 let mut inner = self.inner.lock().unwrap();
155
156 let mut return_finder = FieldVisitor {
157 field: "return",
158 output: None,
159 };
160 event.record(&mut return_finder);
161 if let Some((_, false)) = return_finder.output {
162 event.record(&mut FieldDisplayVisitor {
163 inner: &mut inner,
164 indent: "",
165 ignore_message: false,
166 });
167 return;
168 }
169
170 let meta = event.metadata();
171 let (style, sym) = event_marker(*meta.level(), inner.sym);
172
173 let mut message_finder = FieldVisitor {
174 field: "message",
175 output: None,
176 };
177 event.record(&mut message_finder);
178 if let Some((message, _)) = message_finder.output {
179 inner.line_prefix();
180 if inner.ansi {
181 write!(inner.out, "{}{sym}", style.prefix()).unwrap();
182 } else {
183 write!(inner.out, "{sym}").unwrap();
184 }
185
186 if inner.ansi {
187 write!(inner.out, "{}{message}{}", style.prefix(), style.suffix()).unwrap();
188 } else {
189 write!(inner.out, "{message}").unwrap();
190 }
191 } else {
192 inner.line_prefix();
193 if inner.ansi {
194 write!(
195 inner.out,
196 "{}{sym}{}{}",
197 style.prefix(),
198 meta.name(),
199 style.suffix()
200 )
201 .unwrap();
202 } else {
203 write!(inner.out, "{sym}{}", meta.name()).unwrap();
204 }
205 }
206
207 write_file(&mut *inner, event.metadata());
208
209 writeln!(inner.out).unwrap();
210
211 event.record(&mut FieldDisplayVisitor {
212 inner: &mut inner,
213 indent: " ",
214 ignore_message: true,
215 });
216 }
217
218 fn on_exit(&self, span: &Id, ctx: Context<'_, S>) {
219 let mut inner = self.inner.lock().unwrap();
220
221 let name = ctx.span(span).unwrap().name();
222 let level = inner.indent.pop().unwrap();
223
224 inner.line_prefix();
225
226 let close_sym = span_close_marker(inner.sym);
227 if inner.ansi {
228 let style = event_marker(level, inner.sym).0;
229 let name_style = Color::DarkGray.normal().bold();
230 writeln!(
231 inner.out,
232 "{}{close_sym}{}{name}{}",
233 style.prefix(),
234 style.infix(name_style),
235 name_style.suffix()
236 )
237 .unwrap();
238 } else {
239 writeln!(inner.out, "{close_sym} {name}").unwrap();
240 }
241 }
242}
243
244fn write_file<O: Write + 'static>(inner: &mut Inner<O>, metadata: &'static Metadata<'static>) {
245 if metadata.file().is_some() || metadata.line().is_some() {
246 if inner.ansi {
247 let style = Color::DarkGray.normal();
248 write!(
249 inner.out,
250 " {}{}:{}{}",
251 style.prefix(),
252 metadata.file().unwrap_or_default(),
253 metadata
254 .line()
255 .map_or_else(String::new, |line| line.to_string()),
256 style.suffix(),
257 )
258 .unwrap();
259 } else {
260 write!(
261 inner.out,
262 " {}:{}",
263 metadata.file().unwrap_or_default(),
264 metadata
265 .line()
266 .map_or_else(String::new, |line| line.to_string()),
267 )
268 .unwrap();
269 }
270 }
271}
272
273struct Inner<O: Write + 'static> {
274 ansi: bool,
275 sym: bool,
276 out: O,
277 indent: Vec<Level>,
278}
279
280impl<O: Write + 'static> Inner<O> {
281 fn line_prefix(&mut self) {
282 let continue_sym = span_continue_marker(self.sym);
283 if self.ansi {
284 let mut last_color = Style::new();
285 for level in &self.indent {
286 let style = event_marker(*level, self.sym).0;
287 write!(self.out, "{}{continue_sym}", last_color.infix(style)).unwrap();
288 last_color = style;
289 }
290 self.out
291 .write_fmt(format_args!("{}", last_color.suffix()))
292 .unwrap();
293 } else {
294 for _ in &self.indent {
295 write!(self.out, "{continue_sym}").unwrap();
296 }
297 }
298 }
299}
300
301struct FieldDisplayVisitor<'inner, O: Write + 'static> {
302 inner: &'inner mut Inner<O>,
303 indent: &'static str,
304 ignore_message: bool,
305}
306
307impl<O: Write + 'static> FieldDisplayVisitor<'_, O> {
308 fn field(&mut self, name: &str, style: Style, content: &str) {
309 if name == "message" && self.ignore_message {
310 return;
311 }
312
313 for (i, line) in content.lines().enumerate() {
314 self.inner.line_prefix();
315
316 write!(self.inner.out, "{}", self.indent).unwrap();
317
318 if i == 0 {
319 if self.inner.ansi {
320 let name_style = Color::LightGreen.normal().bold();
321 write!(
322 self.inner.out,
323 "{}{name}{}: ",
324 name_style.prefix(),
325 name_style.suffix()
326 )
327 .unwrap();
328 } else {
329 write!(self.inner.out, "{name}: ").unwrap();
330 }
331 } else {
332 write!(self.inner.out, "{} ", " ".repeat(name.len())).unwrap();
333 }
334
335 if self.inner.ansi {
336 write!(self.inner.out, "{}{line}{}", style.prefix(), style.suffix()).unwrap();
337 } else {
338 write!(self.inner.out, "{line}").unwrap();
339 }
340
341 writeln!(self.inner.out).unwrap();
342 }
343 }
344}
345
346impl<O: Write + 'static> Visit for FieldDisplayVisitor<'_, O> {
347 fn record_f64(&mut self, field: &Field, value: f64) {
348 self.field(field.name(), Color::Yellow.normal(), &format!("{value}"));
349 }
350
351 fn record_i64(&mut self, field: &Field, value: i64) {
352 self.field(field.name(), Color::Yellow.normal(), &format!("{value}"));
353 }
354
355 fn record_u64(&mut self, field: &Field, value: u64) {
356 self.field(field.name(), Color::Yellow.normal(), &format!("{value}"));
357 }
358
359 fn record_i128(&mut self, field: &Field, value: i128) {
360 self.field(field.name(), Color::Yellow.normal(), &format!("{value}"));
361 }
362
363 fn record_u128(&mut self, field: &Field, value: u128) {
364 self.field(field.name(), Color::Yellow.normal(), &format!("{value}"));
365 }
366
367 fn record_bool(&mut self, field: &Field, value: bool) {
368 self.field(
369 field.name(),
370 Color::LightGreen.normal(),
371 &format!("{value}"),
372 );
373 }
374
375 fn record_str(&mut self, field: &Field, value: &str) {
376 self.field(field.name(), Color::Blue.normal(), value);
377 }
378
379 fn record_bytes(&mut self, field: &Field, value: &[u8]) {
380 let mut hex = String::with_capacity((value.len() * 3).saturating_sub(1));
381
382 for (i, byte) in value.iter().enumerate() {
383 if i != 0 {
384 hex.push(' ');
385 }
386 write!(hex, "{byte:x}").unwrap();
387 }
388
389 self.field(field.name(), Color::Purple.normal(), &hex);
390 }
391
392 fn record_error(&mut self, field: &Field, value: &(dyn ::std::error::Error + 'static)) {
393 self.field(field.name(), Color::Red.normal(), &format!("{value}"));
394 }
395
396 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
397 self.field(field.name(), Color::White.normal(), &format!("{value:#?}"));
398 }
399}
400
401struct FieldVisitor {
402 field: &'static str,
403 output: Option<(String, bool)>,
404}
405
406impl Visit for FieldVisitor {
407 fn record_str(&mut self, field: &Field, value: &str) {
408 if let Some((_, any_others)) = &mut self.output {
409 *any_others = true;
410 }
411
412 if field.name() == self.field {
413 self.output = Some((value.to_string(), false));
414 }
415 }
416
417 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
418 if let Some((_, any_others)) = &mut self.output {
419 *any_others = true;
420 }
421
422 if field.name() == self.field {
423 self.output = Some((format!("{value:#?}"), false));
424 }
425 }
426}