]> git.proxmox.com Git - rustc.git/blob - vendor/tracing-tree/src/format.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / vendor / tracing-tree / src / format.rs
1 use ansi_term::Color;
2 use std::{
3 fmt::{self, Write as _},
4 io,
5 };
6 use tracing::{
7 field::{Field, Visit},
8 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(Copy, Clone)]
18 pub(crate) enum SpanMode {
19 PreOpen,
20 Open { verbose: bool },
21 Close { verbose: bool },
22 PostClose,
23 Event,
24 }
25
26 #[derive(Debug)]
27 pub struct Config {
28 /// Whether to use colors.
29 pub ansi: bool,
30 /// Whether an ascii art tree is used or (if false) whether to just use whitespace indent
31 pub indent_lines: bool,
32 /// The amount of chars to indent.
33 pub indent_amount: usize,
34 /// Whether to show the module paths.
35 pub targets: bool,
36 /// Whether to show thread ids.
37 pub render_thread_ids: bool,
38 /// Whether to show thread names.
39 pub render_thread_names: bool,
40 /// Specifies after how many indentation levels we will wrap back around to zero
41 pub wraparound: usize,
42 /// Whether to print the current span before activating a new one
43 pub verbose_entry: bool,
44 /// Whether to print the current span before exiting it.
45 pub verbose_exit: bool,
46 /// Whether to print squiggly brackets (`{}`) around the list of fields in a span.
47 pub bracketed_fields: bool,
48 }
49
50 impl Config {
51 pub fn with_ansi(self, ansi: bool) -> Self {
52 Self { ansi, ..self }
53 }
54
55 pub fn with_indent_lines(self, indent_lines: bool) -> Self {
56 Self {
57 indent_lines,
58 ..self
59 }
60 }
61
62 pub fn with_targets(self, targets: bool) -> Self {
63 Self { targets, ..self }
64 }
65
66 pub fn with_thread_ids(self, render_thread_ids: bool) -> Self {
67 Self {
68 render_thread_ids,
69 ..self
70 }
71 }
72
73 pub fn with_thread_names(self, render_thread_names: bool) -> Self {
74 Self {
75 render_thread_names,
76 ..self
77 }
78 }
79
80 pub fn with_wraparound(self, wraparound: usize) -> Self {
81 Self { wraparound, ..self }
82 }
83
84 pub fn with_verbose_entry(self, verbose_entry: bool) -> Self {
85 Self {
86 verbose_entry,
87 ..self
88 }
89 }
90
91 pub fn with_verbose_exit(self, verbose_exit: bool) -> Self {
92 Self {
93 verbose_exit,
94 ..self
95 }
96 }
97
98 pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self {
99 Self {
100 bracketed_fields,
101 ..self
102 }
103 }
104
105 pub(crate) fn prefix(&self) -> String {
106 let mut buf = String::new();
107 if self.render_thread_ids {
108 write!(buf, "{:?}", std::thread::current().id()).unwrap();
109 if buf.ends_with(')') {
110 buf.truncate(buf.len() - 1);
111 }
112 if buf.starts_with("ThreadId(") {
113 buf.drain(0.."ThreadId(".len());
114 }
115 }
116 if self.render_thread_names {
117 if let Some(name) = std::thread::current().name() {
118 if self.render_thread_ids {
119 buf.push(':');
120 }
121 buf.push_str(name);
122 }
123 }
124 buf
125 }
126 }
127
128 impl Default for Config {
129 fn default() -> Self {
130 Self {
131 ansi: true,
132 indent_lines: false,
133 indent_amount: 2,
134 targets: false,
135 render_thread_ids: false,
136 render_thread_names: false,
137 wraparound: usize::max_value(),
138 verbose_entry: false,
139 verbose_exit: false,
140 bracketed_fields: false,
141 }
142 }
143 }
144
145 #[derive(Debug)]
146 pub struct Buffers {
147 pub current_buf: String,
148 pub indent_buf: String,
149 }
150
151 impl Buffers {
152 pub fn new() -> Self {
153 Self {
154 current_buf: String::new(),
155 indent_buf: String::new(),
156 }
157 }
158
159 pub fn flush_current_buf(&mut self, mut writer: impl io::Write) {
160 write!(writer, "{}", &self.current_buf).unwrap();
161 self.current_buf.clear();
162 }
163
164 pub fn flush_indent_buf(&mut self) {
165 self.current_buf.push_str(&self.indent_buf);
166 self.indent_buf.clear();
167 }
168
169 pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) {
170 self.current_buf.push('\n');
171 let prefix = config.prefix();
172
173 // Render something when wraparound occurs so the user is aware of it
174 if config.indent_lines {
175 match style {
176 SpanMode::Close { .. } | SpanMode::PostClose => {
177 if indent > 0 && (indent + 1) % config.wraparound == 0 {
178 self.indent_buf.push_str(&prefix);
179 for _ in 0..(indent % config.wraparound * config.indent_amount) {
180 self.indent_buf.push_str(LINE_HORIZ);
181 }
182 self.indent_buf.push_str(LINE_OPEN);
183 self.indent_buf.push('\n');
184 }
185 }
186 _ => {}
187 }
188 }
189
190 indent_block(
191 &mut self.current_buf,
192 &mut self.indent_buf,
193 indent % config.wraparound,
194 config.indent_amount,
195 config.indent_lines,
196 &prefix,
197 style,
198 );
199 self.current_buf.clear();
200 self.flush_indent_buf();
201
202 // Render something when wraparound occurs so the user is aware of it
203 if config.indent_lines {
204 match style {
205 SpanMode::PreOpen | SpanMode::Open { .. } => {
206 if indent > 0 && (indent + 1) % config.wraparound == 0 {
207 self.current_buf.push_str(&prefix);
208 for _ in 0..(indent % config.wraparound * config.indent_amount) {
209 self.current_buf.push_str(LINE_HORIZ);
210 }
211 self.current_buf.push_str(LINE_CLOSE);
212 self.current_buf.push('\n');
213 }
214 }
215 _ => {}
216 }
217 }
218 }
219 }
220
221 pub struct FmtEvent<'a> {
222 pub bufs: &'a mut Buffers,
223 pub comma: bool,
224 }
225
226 impl<'a> Visit for FmtEvent<'a> {
227 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
228 let buf = &mut self.bufs.current_buf;
229 let comma = if self.comma { "," } else { "" };
230 match field.name() {
231 "message" => {
232 write!(buf, "{} {:?}", comma, value).unwrap();
233 self.comma = true;
234 }
235 // Skip fields that are actually log metadata that have already been handled
236 #[cfg(feature = "tracing-log")]
237 name if name.starts_with("log.") => {}
238 name => {
239 write!(buf, "{} {}={:?}", comma, name, value).unwrap();
240 self.comma = true;
241 }
242 }
243 }
244 }
245
246 pub struct ColorLevel<'a>(pub &'a Level);
247
248 impl<'a> fmt::Display for ColorLevel<'a> {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 match *self.0 {
251 Level::TRACE => Color::Purple.bold().paint("TRACE"),
252 Level::DEBUG => Color::Blue.bold().paint("DEBUG"),
253 Level::INFO => Color::Green.bold().paint(" INFO"),
254 Level::WARN => Color::RGB(252, 234, 160).bold().paint(" WARN"), // orange
255 Level::ERROR => Color::Red.bold().paint("ERROR"),
256 }
257 .fmt(f)
258 }
259 }
260
261 fn indent_block_with_lines(
262 lines: &[&str],
263 buf: &mut String,
264 indent: usize,
265 indent_amount: usize,
266 prefix: &str,
267 style: SpanMode,
268 ) {
269 let indent_spaces = indent * indent_amount;
270 if lines.is_empty() {
271 return;
272 } else if indent_spaces == 0 {
273 for line in lines {
274 buf.push_str(prefix);
275 // The first indent is special, we only need to print open/close and nothing else
276 if indent == 0 {
277 match style {
278 SpanMode::Open { .. } => buf.push_str(LINE_OPEN),
279 SpanMode::Close { .. } => buf.push_str(LINE_CLOSE),
280 SpanMode::PreOpen | SpanMode::PostClose => {}
281 SpanMode::Event => {}
282 }
283 }
284 buf.push_str(line);
285 buf.push('\n');
286 }
287 return;
288 }
289 let mut s = String::with_capacity(indent_spaces + prefix.len());
290 s.push_str(prefix);
291
292 // instead of using all spaces to indent, draw a vertical line at every indent level
293 // up until the last indent
294 for i in 0..(indent_spaces - indent_amount) {
295 if i % indent_amount == 0 {
296 s.push_str(LINE_VERT);
297 } else {
298 s.push(' ');
299 }
300 }
301
302 // draw branch
303 buf.push_str(&s);
304
305 match style {
306 SpanMode::PreOpen => {
307 buf.push_str(LINE_BRANCH);
308 for _ in 1..(indent_amount / 2) {
309 buf.push_str(LINE_HORIZ);
310 }
311 buf.push_str(LINE_OPEN);
312 }
313 SpanMode::Open { verbose: false } => {
314 buf.push_str(LINE_BRANCH);
315 for _ in 1..indent_amount {
316 buf.push_str(LINE_HORIZ);
317 }
318 buf.push_str(LINE_OPEN);
319 }
320 SpanMode::Open { verbose: true } => {
321 buf.push_str(LINE_VERT);
322 for _ in 1..(indent_amount / 2) {
323 buf.push(' ');
324 }
325 // We don't have the space for fancy rendering at single space indent.
326 if indent_amount > 1 {
327 buf.push('└');
328 }
329 for _ in (indent_amount / 2)..(indent_amount - 1) {
330 buf.push_str(LINE_HORIZ);
331 }
332 // We don't have the space for fancy rendering at single space indent.
333 if indent_amount > 1 {
334 buf.push_str(LINE_OPEN);
335 } else {
336 buf.push_str(LINE_VERT);
337 }
338 }
339 SpanMode::Close { verbose: false } => {
340 buf.push_str(LINE_BRANCH);
341 for _ in 1..indent_amount {
342 buf.push_str(LINE_HORIZ);
343 }
344 buf.push_str(LINE_CLOSE);
345 }
346 SpanMode::Close { verbose: true } => {
347 buf.push_str(LINE_VERT);
348 for _ in 1..(indent_amount / 2) {
349 buf.push(' ');
350 }
351 // We don't have the space for fancy rendering at single space indent.
352 if indent_amount > 1 {
353 buf.push('┌');
354 }
355 for _ in (indent_amount / 2)..(indent_amount - 1) {
356 buf.push_str(LINE_HORIZ);
357 }
358 // We don't have the space for fancy rendering at single space indent.
359 if indent_amount > 1 {
360 buf.push_str(LINE_CLOSE);
361 } else {
362 buf.push_str(LINE_VERT);
363 }
364 }
365 SpanMode::PostClose => {
366 buf.push_str(LINE_BRANCH);
367 for _ in 1..(indent_amount / 2) {
368 buf.push_str(LINE_HORIZ);
369 }
370 buf.push_str(LINE_CLOSE);
371 }
372 SpanMode::Event => {
373 buf.push_str(LINE_BRANCH);
374
375 // add `indent_amount - 1` horizontal lines before the span/event
376 for _ in 0..(indent_amount - 1) {
377 buf.push_str(LINE_HORIZ);
378 }
379 }
380 }
381 buf.push_str(&lines[0]);
382 buf.push('\n');
383
384 // add the rest of the indentation, since we don't want to draw horizontal lines
385 // for subsequent lines
386 for i in 0..indent_amount {
387 if i % indent_amount == 0 {
388 s.push_str(LINE_VERT);
389 } else {
390 s.push(' ');
391 }
392 }
393
394 // add all of the actual content, with each line preceded by the indent string
395 for line in &lines[1..] {
396 buf.push_str(&s);
397 buf.push_str(line);
398 buf.push('\n');
399 }
400 }
401
402 fn indent_block(
403 block: &mut String,
404 buf: &mut String,
405 indent: usize,
406 indent_amount: usize,
407 indent_lines: bool,
408 prefix: &str,
409 style: SpanMode,
410 ) {
411 let lines: Vec<&str> = block.lines().collect();
412 let indent_spaces = indent * indent_amount;
413 buf.reserve(block.len() + (lines.len() * indent_spaces));
414 if indent_lines {
415 indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style);
416 } else {
417 let indent_str = String::from(" ").repeat(indent_spaces);
418 for line in lines {
419 buf.push_str(prefix);
420 buf.push_str(&indent_str);
421 buf.push_str(line);
422 buf.push('\n');
423 }
424 }
425 }