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