1 | use nu_ansi_term::Color; |
2 | use std::{ |
3 | fmt::{self, Write as _}, |
4 | io, |
5 | }; |
6 | use tracing_core::{ |
7 | field::{Field, Visit}, |
8 | span, Level, |
9 | }; |
10 | |
11 | pub(crate) const LINE_VERT: &str = "│" ; |
12 | const LINE_HORIZ: &str = "─" ; |
13 | pub(crate) const LINE_BRANCH: &str = "├" ; |
14 | pub(crate) const LINE_CLOSE: &str = "┘" ; |
15 | pub(crate) const LINE_CLOSE2: char = '┌' ; |
16 | pub(crate) const LINE_OPEN: &str = "┐" ; |
17 | pub(crate) const LINE_OPEN2: char = '└' ; |
18 | |
19 | #[derive (Debug, Copy, Clone)] |
20 | pub(crate) enum SpanMode { |
21 | /// Executed on the parent before entering a child span |
22 | PreOpen, |
23 | Open { |
24 | verbose: bool, |
25 | }, |
26 | Close { |
27 | verbose: bool, |
28 | }, |
29 | /// A span has been entered but another *different* span has been entered in the meantime. |
30 | Retrace { |
31 | verbose: bool, |
32 | }, |
33 | PostClose, |
34 | Event, |
35 | } |
36 | |
37 | #[derive (Debug)] |
38 | pub struct Config { |
39 | /// Whether to use colors. |
40 | pub ansi: bool, |
41 | /// Whether an ascii art tree is used or (if false) whether to just use whitespace indent |
42 | pub indent_lines: bool, |
43 | /// The amount of chars to indent. |
44 | pub indent_amount: usize, |
45 | /// Whether to show the module paths. |
46 | pub targets: bool, |
47 | /// Whether to show thread ids. |
48 | pub render_thread_ids: bool, |
49 | /// Whether to show thread names. |
50 | pub render_thread_names: bool, |
51 | /// Specifies after how many indentation levels we will wrap back around to zero |
52 | pub wraparound: usize, |
53 | /// Whether to print the current span before activating a new one |
54 | pub verbose_entry: bool, |
55 | /// Whether to print the current span before exiting it. |
56 | pub verbose_exit: bool, |
57 | /// Print the path leading up to a span if a different span was entered concurrently |
58 | pub span_retrace: bool, |
59 | /// Whether to print squiggly brackets (`{}`) around the list of fields in a span. |
60 | pub bracketed_fields: bool, |
61 | /// Defer printing a span until an event is generated inside of it |
62 | pub deferred_spans: bool, |
63 | /// Print a label of the span mode (open/close etc). |
64 | pub span_modes: bool, |
65 | /// Whether to print the time with higher precision. |
66 | pub higher_precision: bool, |
67 | } |
68 | |
69 | impl Config { |
70 | pub fn with_ansi(self, ansi: bool) -> Self { |
71 | Self { ansi, ..self } |
72 | } |
73 | |
74 | pub fn with_indent_lines(self, indent_lines: bool) -> Self { |
75 | Self { |
76 | indent_lines, |
77 | ..self |
78 | } |
79 | } |
80 | |
81 | pub fn with_targets(self, targets: bool) -> Self { |
82 | Self { targets, ..self } |
83 | } |
84 | |
85 | pub fn with_thread_ids(self, render_thread_ids: bool) -> Self { |
86 | Self { |
87 | render_thread_ids, |
88 | ..self |
89 | } |
90 | } |
91 | |
92 | pub fn with_thread_names(self, render_thread_names: bool) -> Self { |
93 | Self { |
94 | render_thread_names, |
95 | ..self |
96 | } |
97 | } |
98 | |
99 | pub fn with_wraparound(self, wraparound: usize) -> Self { |
100 | Self { wraparound, ..self } |
101 | } |
102 | |
103 | pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { |
104 | Self { |
105 | verbose_entry, |
106 | ..self |
107 | } |
108 | } |
109 | |
110 | pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { |
111 | Self { |
112 | verbose_exit, |
113 | ..self |
114 | } |
115 | } |
116 | |
117 | pub fn with_span_retrace(self, enabled: bool) -> Self { |
118 | Self { |
119 | span_retrace: enabled, |
120 | ..self |
121 | } |
122 | } |
123 | |
124 | pub fn with_deferred_spans(self, enable: bool) -> Self { |
125 | Self { |
126 | deferred_spans: enable, |
127 | ..self |
128 | } |
129 | } |
130 | |
131 | pub fn with_span_modes(self, enable: bool) -> Self { |
132 | Self { |
133 | span_modes: enable, |
134 | ..self |
135 | } |
136 | } |
137 | |
138 | pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { |
139 | Self { |
140 | bracketed_fields, |
141 | ..self |
142 | } |
143 | } |
144 | |
145 | pub fn with_higher_precision(self, higher_precision: bool) -> Self { |
146 | Self { |
147 | higher_precision, |
148 | ..self |
149 | } |
150 | } |
151 | |
152 | pub(crate) fn prefix(&self) -> String { |
153 | let mut buf = String::new(); |
154 | if self.render_thread_ids { |
155 | write!(buf, " {:?}" , std::thread::current().id()).unwrap(); |
156 | if buf.ends_with(')' ) { |
157 | buf.truncate(buf.len() - 1); |
158 | } |
159 | if buf.starts_with("ThreadId(" ) { |
160 | buf.drain(0.."ThreadId(" .len()); |
161 | } |
162 | } |
163 | if self.render_thread_names { |
164 | if let Some(name) = std::thread::current().name() { |
165 | if self.render_thread_ids { |
166 | buf.push(':' ); |
167 | } |
168 | buf.push_str(name); |
169 | } |
170 | } |
171 | buf |
172 | } |
173 | } |
174 | |
175 | impl Default for Config { |
176 | fn default() -> Self { |
177 | Self { |
178 | ansi: true, |
179 | indent_lines: false, |
180 | indent_amount: 2, |
181 | targets: false, |
182 | render_thread_ids: false, |
183 | render_thread_names: false, |
184 | wraparound: usize::max_value(), |
185 | verbose_entry: false, |
186 | verbose_exit: false, |
187 | span_retrace: false, |
188 | bracketed_fields: false, |
189 | deferred_spans: false, |
190 | span_modes: false, |
191 | higher_precision: false, |
192 | } |
193 | } |
194 | } |
195 | |
196 | #[derive (Debug)] |
197 | pub struct Buffers { |
198 | pub current_buf: String, |
199 | pub indent_buf: String, |
200 | |
201 | /// The last seen span of this layer |
202 | /// |
203 | /// This serves to serialize spans as two events can be generated in different spans |
204 | /// without the spans entering and exiting beforehand. This happens for multithreaded code |
205 | /// and instrumented futures |
206 | pub current_span: Option<span::Id>, |
207 | } |
208 | |
209 | impl Buffers { |
210 | pub fn new() -> Self { |
211 | Self { |
212 | current_buf: String::new(), |
213 | indent_buf: String::new(), |
214 | current_span: None, |
215 | } |
216 | } |
217 | |
218 | pub fn flush_current_buf(&mut self, mut writer: impl io::Write) { |
219 | write!(writer, " {}" , &self.current_buf).unwrap(); |
220 | self.current_buf.clear(); |
221 | } |
222 | |
223 | pub fn flush_indent_buf(&mut self) { |
224 | self.current_buf.push_str(&self.indent_buf); |
225 | self.indent_buf.clear(); |
226 | } |
227 | |
228 | pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) { |
229 | let prefix = config.prefix(); |
230 | |
231 | // Render something when wraparound occurs so the user is aware of it |
232 | if config.indent_lines { |
233 | self.current_buf.push(' \n' ); |
234 | |
235 | match style { |
236 | SpanMode::Close { .. } | SpanMode::PostClose => { |
237 | if indent > 0 && (indent + 1) % config.wraparound == 0 { |
238 | self.indent_buf.push_str(&prefix); |
239 | for _ in 0..(indent % config.wraparound * config.indent_amount) { |
240 | self.indent_buf.push_str(LINE_HORIZ); |
241 | } |
242 | self.indent_buf.push_str(LINE_OPEN); |
243 | self.indent_buf.push(' \n' ); |
244 | } |
245 | } |
246 | _ => {} |
247 | } |
248 | } |
249 | |
250 | indent_block( |
251 | &self.current_buf, |
252 | &mut self.indent_buf, |
253 | indent % config.wraparound, |
254 | config.indent_amount, |
255 | config.indent_lines, |
256 | &prefix, |
257 | style, |
258 | ); |
259 | |
260 | self.current_buf.clear(); |
261 | self.flush_indent_buf(); |
262 | |
263 | // Render something when wraparound occurs so the user is aware of it |
264 | if config.indent_lines { |
265 | match style { |
266 | SpanMode::PreOpen { .. } | SpanMode::Open { .. } => { |
267 | if indent > 0 && (indent + 1) % config.wraparound == 0 { |
268 | self.current_buf.push_str(&prefix); |
269 | for _ in 0..(indent % config.wraparound * config.indent_amount) { |
270 | self.current_buf.push_str(LINE_HORIZ); |
271 | } |
272 | self.current_buf.push_str(LINE_CLOSE); |
273 | self.current_buf.push(' \n' ); |
274 | } |
275 | } |
276 | _ => {} |
277 | } |
278 | } |
279 | } |
280 | } |
281 | |
282 | pub struct FmtEvent<'a> { |
283 | pub bufs: &'a mut Buffers, |
284 | pub comma: bool, |
285 | } |
286 | |
287 | impl<'a> Visit for FmtEvent<'a> { |
288 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { |
289 | let buf: &mut String = &mut self.bufs.current_buf; |
290 | let comma: &'static str = if self.comma { "," } else { "" }; |
291 | match field.name() { |
292 | "message" => { |
293 | write!(buf, " {} {:?}" , comma, value).unwrap(); |
294 | self.comma = true; |
295 | } |
296 | // Skip fields that are actually log metadata that have already been handled |
297 | #[cfg (feature = "tracing-log" )] |
298 | name: &'static str if name.starts_with("log." ) => {} |
299 | name: &'static str => { |
300 | write!(buf, " {} {}= {:?}" , comma, name, value).unwrap(); |
301 | self.comma = true; |
302 | } |
303 | } |
304 | } |
305 | } |
306 | |
307 | pub struct ColorLevel<'a>(pub &'a Level); |
308 | |
309 | impl<'a> fmt::Display for ColorLevel<'a> { |
310 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
311 | matchAnsiGenericString<'_, str> *self.0 { |
312 | Level::TRACE => Color::Purple.bold().paint(input:"TRACE" ), |
313 | Level::DEBUG => Color::Blue.bold().paint(input:"DEBUG" ), |
314 | Level::INFO => Color::Green.bold().paint(input:" INFO" ), |
315 | Level::WARN => Color::Rgb(252, 234, 160).bold().paint(input:" WARN" ), // orange |
316 | Level::ERROR => Color::Red.bold().paint(input:"ERROR" ), |
317 | } |
318 | .fmt(f) |
319 | } |
320 | } |
321 | |
322 | pub(crate) fn write_span_mode(buf: &mut String, style: SpanMode) { |
323 | match style { |
324 | SpanMode::Open { verbose: true } => buf.push_str(string:"open(v)" ), |
325 | SpanMode::Open { verbose: false } => buf.push_str(string:"open" ), |
326 | SpanMode::Retrace { verbose: false } => buf.push_str(string:"retrace" ), |
327 | SpanMode::Retrace { verbose: true } => buf.push_str(string:"retrace(v)" ), |
328 | SpanMode::Close { verbose: true } => buf.push_str(string:"close(v)" ), |
329 | SpanMode::Close { verbose: false } => buf.push_str(string:"close" ), |
330 | SpanMode::PreOpen => buf.push_str(string:"pre_open" ), |
331 | SpanMode::PostClose => buf.push_str(string:"post_close" ), |
332 | SpanMode::Event => buf.push_str(string:"event" ), |
333 | } |
334 | |
335 | buf.push_str(string:": " ) |
336 | } |
337 | |
338 | fn indent_block_with_lines( |
339 | lines: &[&str], |
340 | buf: &mut String, |
341 | indent: usize, |
342 | // width of one level of indent |
343 | indent_amount: usize, |
344 | prefix: &str, |
345 | style: SpanMode, |
346 | ) { |
347 | let indent_spaces = indent * indent_amount; |
348 | |
349 | if lines.is_empty() { |
350 | return; |
351 | } else if indent_spaces == 0 { |
352 | for line in lines { |
353 | buf.push_str(prefix); |
354 | // The first indent is special, we only need to print open/close and nothing else |
355 | if indent == 0 { |
356 | match style { |
357 | SpanMode::Open { .. } => buf.push_str(LINE_OPEN), |
358 | SpanMode::Retrace { .. } => buf.push_str(LINE_OPEN), |
359 | SpanMode::Close { .. } => buf.push_str(LINE_CLOSE), |
360 | SpanMode::PreOpen { .. } | SpanMode::PostClose => {} |
361 | SpanMode::Event => {} |
362 | } |
363 | } |
364 | buf.push_str(line); |
365 | buf.push(' \n' ); |
366 | } |
367 | return; |
368 | } |
369 | |
370 | let mut s = String::with_capacity(indent_spaces + prefix.len()); |
371 | s.push_str(prefix); |
372 | |
373 | for _ in 0..(indent_spaces - indent_amount) { |
374 | s.push(' ' ); |
375 | } |
376 | |
377 | // draw branch |
378 | buf.push_str(&s); |
379 | |
380 | match style { |
381 | SpanMode::PreOpen => { |
382 | buf.push(LINE_OPEN2); |
383 | for _ in 1..(indent_amount / 2) { |
384 | buf.push_str(LINE_HORIZ); |
385 | } |
386 | buf.push_str(LINE_OPEN); |
387 | } |
388 | SpanMode::Open { verbose: false } | SpanMode::Retrace { verbose: false } => { |
389 | buf.push(LINE_OPEN2); |
390 | for _ in 1..indent_amount { |
391 | buf.push_str(LINE_HORIZ); |
392 | } |
393 | buf.push_str(LINE_OPEN); |
394 | } |
395 | SpanMode::Open { verbose: true } | SpanMode::Retrace { verbose: true } => { |
396 | buf.push(' ' ); |
397 | for _ in 1..(indent_amount / 2) { |
398 | buf.push(' ' ); |
399 | } |
400 | // We don't have the space for fancy rendering at single space indent. |
401 | if indent_amount > 1 { |
402 | buf.push(LINE_OPEN2); |
403 | } |
404 | for _ in (indent_amount / 2)..(indent_amount - 1) { |
405 | buf.push_str(LINE_HORIZ); |
406 | } |
407 | // We don't have the space for fancy rendering at single space indent. |
408 | if indent_amount > 1 { |
409 | buf.push_str(LINE_OPEN); |
410 | } else { |
411 | buf.push(' ' ); |
412 | } |
413 | } |
414 | SpanMode::Close { verbose: false } => { |
415 | buf.push(LINE_CLOSE2); |
416 | for _ in 1..indent_amount { |
417 | buf.push_str(LINE_HORIZ); |
418 | } |
419 | buf.push_str(LINE_CLOSE); |
420 | } |
421 | SpanMode::Close { verbose: true } => { |
422 | buf.push(' ' ); |
423 | for _ in 1..(indent_amount / 2) { |
424 | buf.push(' ' ); |
425 | } |
426 | // We don't have the space for fancy rendering at single space indent. |
427 | if indent_amount > 1 { |
428 | buf.push(LINE_CLOSE2); |
429 | } |
430 | for _ in (indent_amount / 2)..(indent_amount - 1) { |
431 | buf.push_str(LINE_HORIZ); |
432 | } |
433 | // We don't have the space for fancy rendering at single space indent. |
434 | if indent_amount > 1 { |
435 | buf.push_str(LINE_CLOSE); |
436 | } else { |
437 | buf.push(' ' ); |
438 | } |
439 | } |
440 | SpanMode::PostClose => { |
441 | buf.push(LINE_CLOSE2); |
442 | for _ in 1..(indent_amount / 2) { |
443 | buf.push_str(LINE_HORIZ); |
444 | } |
445 | buf.push_str(LINE_CLOSE); |
446 | } |
447 | SpanMode::Event => { |
448 | buf.push_str(LINE_BRANCH); |
449 | |
450 | // add `indent_amount - 1` horizontal lines before the span/event |
451 | for _ in 0..(indent_amount - 1) { |
452 | buf.push_str(LINE_HORIZ); |
453 | } |
454 | } |
455 | } |
456 | buf.push_str(lines[0]); |
457 | buf.push(' \n' ); |
458 | |
459 | // add the rest of the indentation, since we don't want to draw horizontal lines |
460 | // for subsequent lines |
461 | for i in 0..indent_amount { |
462 | if i % indent_amount == 0 { |
463 | s.push_str(LINE_VERT); |
464 | } else { |
465 | s.push(' ' ); |
466 | } |
467 | } |
468 | |
469 | // add all of the actual content, with each line preceded by the indent string |
470 | for line in &lines[1..] { |
471 | buf.push_str(&s); |
472 | buf.push_str(line); |
473 | buf.push(' \n' ); |
474 | } |
475 | } |
476 | |
477 | fn indent_block( |
478 | block: &str, |
479 | buf: &mut String, |
480 | mut indent: usize, |
481 | indent_amount: usize, |
482 | indent_lines: bool, |
483 | prefix: &str, |
484 | style: SpanMode, |
485 | ) { |
486 | let lines: Vec<&str> = block.lines().collect(); |
487 | let indent_spaces = indent * indent_amount; |
488 | buf.reserve(block.len() + (lines.len() * indent_spaces)); |
489 | |
490 | // The PreOpen and PostClose need to match up with the indent of the entered child span one more indent |
491 | // deep |
492 | match style { |
493 | SpanMode::PreOpen | SpanMode::PostClose => { |
494 | indent += 1; |
495 | } |
496 | _ => (), |
497 | } |
498 | |
499 | if indent_lines { |
500 | indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style); |
501 | } else { |
502 | let indent_str = String::from(" " ).repeat(indent_spaces); |
503 | for line in lines { |
504 | buf.push_str(prefix); |
505 | buf.push(' ' ); |
506 | buf.push_str(&indent_str); |
507 | buf.push_str(line); |
508 | buf.push(' \n' ); |
509 | } |
510 | } |
511 | } |
512 | |