tracing_subscriber/fmt/format/pretty.rs
1use super::*;
2use crate::{
3 field::{VisitFmt, VisitOutput},
4 fmt::fmt_subscriber::{FmtContext, FormattedFields},
5 registry::LookupSpan,
6};
7
8use std::fmt;
9use tracing_core::{
10 field::{self, Field},
11 Collect, Event, Level,
12};
13
14#[cfg(feature = "tracing-log")]
15use tracing_log::NormalizeEvent;
16
17use nu_ansi_term::{Color, Style};
18
19/// An excessively pretty, human-readable event formatter.
20///
21/// Unlike the [`Full`], [`Compact`], and [`Json`] formatters, this is a
22/// multi-line output format. Each individual event may output multiple lines of
23/// text.
24///
25/// # Example Output
26///
27/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-pretty
28/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
29/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-pretty`
30/// 2022-02-15T18:44:24.535324Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: preparing to shave yaks, </font><font color="#4E9A06"><b>number_of_yaks</b></font><font color="#4E9A06">: 3</font>
31/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:16 <font color="#AAAAAA"><i>on</i></font> main
32///
33/// 2022-02-15T18:44:24.535403Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty::yak_shave</b></font><font color="#4E9A06">: shaving yaks</font>
34/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:41 <font color="#AAAAAA"><i>on</i></font> main
35/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
36///
37/// 2022-02-15T18:44:24.535442Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I'm gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: "yay!"</font>
38/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
39/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
40/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
41///
42/// 2022-02-15T18:44:24.535469Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
43/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
44/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
45/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
46///
47/// 2022-02-15T18:44:24.535502Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 1, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
48/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
49/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
50///
51/// 2022-02-15T18:44:24.535524Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 1</font>
52/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
53/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
54///
55/// 2022-02-15T18:44:24.535551Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I'm gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: "yay!"</font>
56/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
57/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
58/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
59///
60/// 2022-02-15T18:44:24.535573Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
61/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
62/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
63/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
64///
65/// 2022-02-15T18:44:24.535600Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 2, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
66/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
67/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
68///
69/// 2022-02-15T18:44:24.535618Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
70/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
71/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
72///
73/// 2022-02-15T18:44:24.535644Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I'm gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: "yay!"</font>
74/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
75/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
76/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
77///
78/// 2022-02-15T18:44:24.535670Z <font color="#C4A000"> WARN</font> <font color="#C4A000"><b>fmt_pretty::yak_shave</b></font><font color="#C4A000">: could not locate yak</font>
79/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:18 <font color="#AAAAAA"><i>on</i></font> main
80/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
81/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
82///
83/// 2022-02-15T18:44:24.535698Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 3, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: false</font>
84/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
85/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
86///
87/// 2022-02-15T18:44:24.535720Z <font color="#CC0000">ERROR</font> <font color="#CC0000"><b>fmt_pretty::yak_shave</b></font><font color="#CC0000">: failed to shave yak, </font><font color="#CC0000"><b>yak</b></font><font color="#CC0000">: 3, </font><font color="#CC0000"><b>error</b></font><font color="#CC0000">: missing yak, </font><font color="#CC0000"><b>error.sources</b></font><font color="#CC0000">: [out of space, out of cash]</font>
88/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:51 <font color="#AAAAAA"><i>on</i></font> main
89/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
90///
91/// 2022-02-15T18:44:24.535742Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
92/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
93/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
94///
95/// 2022-02-15T18:44:24.535765Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: yak shaving completed, </font><font color="#4E9A06"><b>all_yaks_shaved</b></font><font color="#4E9A06">: false</font>
96/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:19 <font color="#AAAAAA"><i>on</i></font> main
97/// </pre>
98#[derive(Debug, Clone, Eq, PartialEq)]
99pub struct Pretty {
100 display_location: bool,
101}
102
103/// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation.
104///
105/// [visitor]: field::Visit
106/// [`MakeVisitor`]: crate::field::MakeVisitor
107#[derive(Debug)]
108pub struct PrettyVisitor<'a> {
109 writer: Writer<'a>,
110 is_empty: bool,
111 style: Style,
112 result: fmt::Result,
113}
114
115/// An excessively pretty, human-readable [`MakeVisitor`] implementation.
116///
117/// [`MakeVisitor`]: crate::field::MakeVisitor
118#[derive(Debug)]
119pub struct PrettyFields {
120 /// A value to override the provided `Writer`'s ANSI formatting
121 /// configuration.
122 ///
123 /// If this is `Some`, we override the `Writer`'s ANSI setting. This is
124 /// necessary in order to continue supporting the deprecated
125 /// `PrettyFields::with_ansi` method. If it is `None`, we don't override the
126 /// ANSI formatting configuration (because the deprecated method was not
127 /// called).
128 // TODO: when `PrettyFields::with_ansi` is removed, we can get rid
129 // of this entirely.
130 ansi: Option<bool>,
131}
132
133// === impl Pretty ===
134
135impl Default for Pretty {
136 fn default() -> Self {
137 Self {
138 display_location: true,
139 }
140 }
141}
142
143impl Pretty {
144 fn style_for(level: &Level) -> Style {
145 match *level {
146 Level::TRACE => Style::new().fg(Color::Purple),
147 Level::DEBUG => Style::new().fg(Color::Blue),
148 Level::INFO => Style::new().fg(Color::Green),
149 Level::WARN => Style::new().fg(Color::Yellow),
150 Level::ERROR => Style::new().fg(Color::Red),
151 }
152 }
153
154 /// Sets whether the event's source code location is displayed.
155 ///
156 /// This defaults to `true`.
157 #[deprecated(
158 since = "0.3.6",
159 note = "all formatters now support configurable source locations. Use `Format::with_source_location` instead."
160 )]
161 pub fn with_source_location(self, display_location: bool) -> Self {
162 Self {
163 display_location,
164 ..self
165 }
166 }
167}
168
169impl<C, N, T> FormatEvent<C, N> for Format<Pretty, T>
170where
171 C: Collect + for<'a> LookupSpan<'a>,
172 N: for<'a> FormatFields<'a> + 'static,
173 T: FormatTime,
174{
175 fn format_event(
176 &self,
177 ctx: &FmtContext<'_, C, N>,
178 mut writer: Writer<'_>,
179 event: &Event<'_>,
180 ) -> fmt::Result {
181 #[cfg(feature = "tracing-log")]
182 let normalized_meta = event.normalized_metadata();
183 #[cfg(feature = "tracing-log")]
184 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
185 #[cfg(not(feature = "tracing-log"))]
186 let meta = event.metadata();
187 write!(&mut writer, " ")?;
188
189 self.format_timestamp(&mut writer)?;
190
191 let style = if self.display_level && writer.has_ansi_escapes() {
192 Pretty::style_for(meta.level())
193 } else {
194 Style::new()
195 };
196
197 if self.display_level {
198 self.format_level(*meta.level(), &mut writer)?;
199 }
200
201 if self.display_target {
202 let target_style = if writer.has_ansi_escapes() {
203 style.bold()
204 } else {
205 style
206 };
207 write!(
208 writer,
209 "{}{}{}:",
210 target_style.prefix(),
211 meta.target(),
212 target_style.infix(style)
213 )?;
214 }
215 let line_number = if self.display_line_number {
216 meta.line()
217 } else {
218 None
219 };
220
221 // If the file name is disabled, format the line number right after the
222 // target. Otherwise, if we also display the file, it'll go on a
223 // separate line.
224 if let (Some(line_number), false, true) = (
225 line_number,
226 self.display_filename,
227 self.format.display_location,
228 ) {
229 write!(
230 writer,
231 "{}{}{}:",
232 style.prefix(),
233 line_number,
234 style.infix(style)
235 )?;
236 }
237
238 writer.write_char(' ')?;
239
240 let mut v = PrettyVisitor::new(writer.by_ref(), true).with_style(style);
241 event.record(&mut v);
242 v.finish()?;
243 writer.write_char('\n')?;
244
245 let dimmed = if writer.has_ansi_escapes() {
246 Style::new().dimmed().italic()
247 } else {
248 Style::new()
249 };
250 let thread = self.display_thread_name || self.display_thread_id;
251
252 if let (Some(file), true, true) = (
253 meta.file(),
254 self.format.display_location,
255 self.display_filename,
256 ) {
257 write!(writer, " {} {}", dimmed.paint("at"), file,)?;
258
259 if let Some(line) = line_number {
260 write!(writer, ":{}", line)?;
261 }
262 writer.write_char(if thread { ' ' } else { '\n' })?;
263 } else if thread {
264 write!(writer, " ")?;
265 };
266
267 if thread {
268 write!(writer, "{} ", dimmed.paint("on"))?;
269 let thread = std::thread::current();
270 if self.display_thread_name {
271 if let Some(name) = thread.name() {
272 write!(writer, "{}", name)?;
273 if self.display_thread_id {
274 writer.write_char(' ')?;
275 }
276 }
277 }
278 if self.display_thread_id {
279 write!(writer, "{:?}", thread.id())?;
280 }
281 writer.write_char('\n')?;
282 }
283
284 let bold = writer.bold();
285 let span = event
286 .parent()
287 .and_then(|id| ctx.span(id))
288 .or_else(|| ctx.lookup_current());
289
290 let scope = span.into_iter().flat_map(|span| span.scope());
291
292 for span in scope {
293 let meta = span.metadata();
294 if self.display_target {
295 write!(
296 writer,
297 " {} {}::{}",
298 dimmed.paint("in"),
299 meta.target(),
300 bold.paint(meta.name()),
301 )?;
302 } else {
303 write!(
304 writer,
305 " {} {}",
306 dimmed.paint("in"),
307 bold.paint(meta.name()),
308 )?;
309 }
310
311 let ext = span.extensions();
312 let fields = &ext
313 .get::<FormattedFields<N>>()
314 .expect("Unable to find FormattedFields in extensions; this is a bug");
315 if !fields.is_empty() {
316 write!(writer, " {} {}", dimmed.paint("with"), fields)?;
317 }
318 writer.write_char('\n')?;
319 }
320
321 writer.write_char('\n')
322 }
323}
324
325impl<'writer> FormatFields<'writer> for Pretty {
326 fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result {
327 let mut v = PrettyVisitor::new(writer, true);
328 fields.record(&mut v);
329 v.finish()
330 }
331
332 fn add_fields(
333 &self,
334 current: &'writer mut FormattedFields<Self>,
335 fields: &span::Record<'_>,
336 ) -> fmt::Result {
337 let empty = current.is_empty();
338 let writer = current.as_writer();
339 let mut v = PrettyVisitor::new(writer, empty);
340 fields.record(&mut v);
341 v.finish()
342 }
343}
344
345// === impl PrettyFields ===
346
347impl Default for PrettyFields {
348 fn default() -> Self {
349 Self::new()
350 }
351}
352
353impl PrettyFields {
354 /// Returns a new default [`PrettyFields`] implementation.
355 pub fn new() -> Self {
356 // By default, don't override the `Writer`'s ANSI colors
357 // configuration. We'll only do this if the user calls the
358 // deprecated `PrettyFields::with_ansi` method.
359 Self { ansi: None }
360 }
361
362 /// Enable ANSI encoding for formatted fields.
363 #[deprecated(
364 since = "0.3.3",
365 note = "Use `fmt::Subscriber::with_ansi` or `fmt::Collector::with_ansi` instead."
366 )]
367 pub fn with_ansi(self, ansi: bool) -> Self {
368 Self {
369 ansi: Some(ansi),
370 ..self
371 }
372 }
373}
374
375impl<'a> MakeVisitor<Writer<'a>> for PrettyFields {
376 type Visitor = PrettyVisitor<'a>;
377
378 #[inline]
379 fn make_visitor(&self, mut target: Writer<'a>) -> Self::Visitor {
380 if let Some(ansi) = self.ansi {
381 target = target.with_ansi(ansi);
382 }
383 PrettyVisitor::new(target, true)
384 }
385}
386
387// === impl PrettyVisitor ===
388
389impl<'a> PrettyVisitor<'a> {
390 /// Returns a new default visitor that formats to the provided `writer`.
391 ///
392 /// # Arguments
393 /// - `writer`: the writer to format to.
394 /// - `is_empty`: whether or not any fields have been previously written to
395 /// that writer.
396 pub fn new(writer: Writer<'a>, is_empty: bool) -> Self {
397 Self {
398 writer,
399 is_empty,
400 style: Style::default(),
401 result: Ok(()),
402 }
403 }
404
405 pub(crate) fn with_style(self, style: Style) -> Self {
406 Self { style, ..self }
407 }
408
409 fn write_padded(&mut self, value: &impl fmt::Debug) {
410 let padding = if self.is_empty {
411 self.is_empty = false;
412 ""
413 } else {
414 ", "
415 };
416 self.result = write!(self.writer, "{}{:?}", padding, value);
417 }
418
419 fn bold(&self) -> Style {
420 if self.writer.has_ansi_escapes() {
421 self.style.bold()
422 } else {
423 Style::new()
424 }
425 }
426}
427
428impl field::Visit for PrettyVisitor<'_> {
429 fn record_str(&mut self, field: &Field, value: &str) {
430 if self.result.is_err() {
431 return;
432 }
433
434 if field.name() == "message" {
435 self.record_debug(field, &format_args!("{}", value))
436 } else {
437 self.record_debug(field, &value)
438 }
439 }
440
441 fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
442 if let Some(source) = value.source() {
443 let bold = self.bold();
444 self.record_debug(
445 field,
446 &format_args!(
447 "{}, {}{}.sources{}: {}",
448 value,
449 bold.prefix(),
450 field,
451 bold.infix(self.style),
452 ErrorSourceList(source),
453 ),
454 )
455 } else {
456 self.record_debug(field, &format_args!("{}", value))
457 }
458 }
459
460 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
461 if self.result.is_err() {
462 return;
463 }
464 let bold = self.bold();
465 match field.name() {
466 "message" => self.write_padded(&format_args!("{}{:?}", self.style.prefix(), value,)),
467 // Skip fields that are actually log metadata that have already been handled
468 #[cfg(feature = "tracing-log")]
469 name if name.starts_with("log.") => self.result = Ok(()),
470 name if name.starts_with("r#") => self.write_padded(&format_args!(
471 "{}{}{}: {:?}",
472 bold.prefix(),
473 &name[2..],
474 bold.infix(self.style),
475 value
476 )),
477 name => self.write_padded(&format_args!(
478 "{}{}{}: {:?}",
479 bold.prefix(),
480 name,
481 bold.infix(self.style),
482 value
483 )),
484 };
485 }
486}
487
488impl VisitOutput<fmt::Result> for PrettyVisitor<'_> {
489 fn finish(mut self) -> fmt::Result {
490 write!(&mut self.writer, "{}", self.style.suffix())?;
491 self.result
492 }
493}
494
495impl VisitFmt for PrettyVisitor<'_> {
496 fn writer(&mut self) -> &mut dyn fmt::Write {
497 &mut self.writer
498 }
499}