Skip to content

Commit 8bdc6c3

Browse files
authored
subscriber: add Pretty formatter (backports #1067) (#1080)
This backports PR #1067 to v0.1.x. Since this has already been approved on master, I'm just going to go ahead and merge it when CI passes. ## Motivation Currently, the `tracing_subscriber::fmt` module contains only single-line event formatters. Some users have requested a human-readable, pretty-printing event formatter optimized for aesthetics. ## Solution This branch adds a new `Pretty` formatter which uses an _excessively_ pretty output format. It's neither compact, single-line oriented, nor easily machine-readable, but it looks _quite_ nice, in my opinion. This is well suited for local development or potentially for user-facing logs in a CLI application. Additionally, I tried to improve the docs for the different formatters currently provided, including example output. Check out [the Netlify preview][1]! [1]: https://deploy-preview-1067--tracing-rs.netlify.app/tracing_subscriber/fmt/index.html#formatters Signed-off-by: Eliza Weisman <[email protected]>
1 parent 3bc2fd3 commit 8bdc6c3

File tree

9 files changed

+607
-37
lines changed

9 files changed

+607
-37
lines changed

examples/examples/fmt-json.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![deny(rust_2018_idioms)]
2+
#[path = "fmt/yak_shave.rs"]
3+
mod yak_shave;
4+
5+
fn main() {
6+
tracing_subscriber::fmt()
7+
.json()
8+
.with_max_level(tracing::Level::TRACE)
9+
.with_current_span(false)
10+
.init();
11+
12+
let number_of_yaks = 3;
13+
// this creates a new event, outside of any spans.
14+
tracing::info!(number_of_yaks, "preparing to shave yaks");
15+
16+
let number_shaved = yak_shave::shave_all(number_of_yaks);
17+
tracing::info!(
18+
all_yaks_shaved = number_shaved == number_of_yaks,
19+
"yak shaving completed"
20+
);
21+
}

examples/examples/fmt-pretty.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![deny(rust_2018_idioms)]
2+
#[path = "fmt/yak_shave.rs"]
3+
mod yak_shave;
4+
5+
fn main() {
6+
tracing_subscriber::fmt()
7+
.pretty()
8+
.with_thread_names(true)
9+
// enable everything
10+
.with_max_level(tracing::Level::TRACE)
11+
// sets this to be the default, global collector for this application.
12+
.init();
13+
14+
let number_of_yaks = 3;
15+
// this creates a new event, outside of any spans.
16+
tracing::info!(number_of_yaks, "preparing to shave yaks");
17+
18+
let number_shaved = yak_shave::shave_all(number_of_yaks);
19+
tracing::info!(
20+
all_yaks_shaved = number_shaved == number_of_yaks,
21+
"yak shaving completed"
22+
);
23+
}

examples/examples/fmt.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
#![deny(rust_2018_idioms)]
2-
use tracing::{info, Level};
3-
42
#[path = "fmt/yak_shave.rs"]
53
mod yak_shave;
64

75
fn main() {
86
tracing_subscriber::fmt()
9-
// all spans/events with a level higher than DEBUG (e.g, info, warn, etc.)
10-
// will be written to stdout.
11-
.with_max_level(Level::DEBUG)
12-
// sets this to be the default, global subscriber for this application.
7+
// enable everything
8+
.with_max_level(tracing::Level::TRACE)
9+
// sets this to be the default, global collector for this application.
1310
.init();
1411

1512
let number_of_yaks = 3;
1613
// this creates a new event, outside of any spans.
17-
info!(number_of_yaks, "preparing to shave yaks");
14+
tracing::info!(number_of_yaks, "preparing to shave yaks");
1815

1916
let number_shaved = yak_shave::shave_all(number_of_yaks);
20-
info!(
17+
tracing::info!(
2118
all_yaks_shaved = number_shaved == number_of_yaks,
2219
"yak shaving completed."
2320
);

examples/examples/fmt/yak_shave.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,55 @@
11
use std::{error::Error, io};
2-
use tracing::{debug, error, info, span, warn, Level};
2+
use tracing::{debug, error, info, span, trace, warn, Level};
33

44
// the `#[tracing::instrument]` attribute creates and enters a span
55
// every time the instrumented function is called. The span is named after the
66
// the function or method. Paramaters passed to the function are recorded as fields.
77
#[tracing::instrument]
88
pub fn shave(yak: usize) -> Result<(), Box<dyn Error + 'static>> {
9-
// this creates an event at the DEBUG log level with two fields:
9+
// this creates an event at the TRACE log level with two fields:
1010
// - `excitement`, with the key "excitement" and the value "yay!"
1111
// - `message`, with the key "message" and the value "hello! I'm gonna shave a yak."
1212
//
1313
// unlike other fields, `message`'s shorthand initialization is just the string itself.
14-
debug!(excitement = "yay!", "hello! I'm gonna shave a yak.");
14+
trace!(excitement = "yay!", "hello! I'm gonna shave a yak");
1515
if yak == 3 {
16-
warn!("could not locate yak!");
16+
warn!("could not locate yak");
1717
// note that this is intended to demonstrate `tracing`'s features, not idiomatic
1818
// error handling! in a library or application, you should consider returning
1919
// a dedicated `YakError`. libraries like snafu or thiserror make this easy.
20-
return Err(io::Error::new(io::ErrorKind::Other, "shaving yak failed!").into());
20+
return Err(io::Error::new(io::ErrorKind::Other, "missing yak").into());
2121
} else {
22-
debug!("yak shaved successfully");
22+
trace!("yak shaved successfully");
2323
}
2424
Ok(())
2525
}
2626

2727
pub fn shave_all(yaks: usize) -> usize {
28-
// Constructs a new span named "shaving_yaks" at the TRACE level,
28+
// Constructs a new span named "shaving_yaks" at the INFO level,
2929
// and a field whose key is "yaks". This is equivalent to writing:
3030
//
31-
// let span = span!(Level::TRACE, "shaving_yaks", yaks = yaks);
31+
// let span = span!(Level::INFO, "shaving_yaks", yaks = yaks);
3232
//
3333
// local variables (`yaks`) can be used as field values
3434
// without an assignment, similar to struct initializers.
35-
let span = span!(Level::TRACE, "shaving_yaks", yaks);
35+
let span = span!(Level::INFO, "shaving_yaks", yaks);
3636
let _enter = span.enter();
3737

3838
info!("shaving yaks");
3939

4040
let mut yaks_shaved = 0;
4141
for yak in 1..=yaks {
4242
let res = shave(yak);
43-
debug!(yak, shaved = res.is_ok());
43+
debug!(target: "yak_events", yak, shaved = res.is_ok());
4444

4545
if let Err(ref error) = res {
4646
// Like spans, events can also use the field initialization shorthand.
4747
// In this instance, `yak` is the field being initalized.
48-
error!(yak, error = error.as_ref(), "failed to shave yak!");
48+
error!(yak, error = error.as_ref(), "failed to shave yak");
4949
} else {
5050
yaks_shaved += 1;
5151
}
52-
debug!(yaks_shaved);
52+
trace!(yaks_shaved);
5353
}
5454

5555
yaks_shaved

tracing-subscriber/src/fmt/fmt_layer.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,19 @@ where
367367
}
368368
}
369369

370+
/// Sets the layer being built to use an [excessively pretty, human-readable formatter](crate::fmt::format::Pretty).
371+
#[cfg(feature = "ansi")]
372+
#[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
373+
pub fn pretty(self) -> Layer<S, format::Pretty, format::Format<format::Pretty, T>, W> {
374+
Layer {
375+
fmt_event: self.fmt_event.pretty(),
376+
fmt_fields: format::Pretty::default(),
377+
fmt_span: self.fmt_span,
378+
make_writer: self.make_writer,
379+
_inner: self._inner,
380+
}
381+
}
382+
370383
/// Sets the layer being built to use a [JSON formatter](../fmt/format/struct.Json.html).
371384
///
372385
/// The full format includes fields from all entered spans.

tracing-subscriber/src/fmt/format/mod.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@ use ansi_term::{Colour, Style};
2424

2525
#[cfg(feature = "json")]
2626
mod json;
27-
28-
use fmt::{Debug, Display};
2927
#[cfg(feature = "json")]
3028
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
3129
pub use json::*;
3230

31+
#[cfg(feature = "ansi")]
32+
mod pretty;
33+
#[cfg(feature = "ansi")]
34+
#[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
35+
pub use pretty::*;
36+
37+
use fmt::{Debug, Display};
38+
3339
/// A type that can format a tracing `Event` for a `fmt::Write`.
3440
///
3541
/// `FormatEvent` is primarily used in the context of [`fmt::Subscriber`] or [`fmt::Layer`]. Each time an event is
@@ -214,6 +220,25 @@ impl<F, T> Format<F, T> {
214220
}
215221
}
216222

223+
/// Use an excessively pretty, human-readable output format.
224+
///
225+
/// See [`Pretty`].
226+
///
227+
/// Note that this requires the "ansi" feature to be enabled.
228+
#[cfg(feature = "ansi")]
229+
#[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
230+
pub fn pretty(self) -> Format<Pretty, T> {
231+
Format {
232+
format: Pretty::default(),
233+
timer: self.timer,
234+
ansi: self.ansi,
235+
display_target: self.display_target,
236+
display_level: self.display_level,
237+
display_thread_id: self.display_thread_id,
238+
display_thread_name: self.display_thread_name,
239+
}
240+
}
241+
217242
/// Use the full JSON format.
218243
///
219244
/// The full format includes fields from all entered spans.
@@ -521,6 +546,7 @@ where
521546
}
522547

523548
// === impl FormatFields ===
549+
524550
impl<'writer, M> FormatFields<'writer> for M
525551
where
526552
M: MakeOutput<&'writer mut dyn fmt::Write, fmt::Result>,
@@ -622,10 +648,7 @@ impl<'a> field::Visit for DefaultVisitor<'a> {
622648

623649
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
624650
if let Some(source) = value.source() {
625-
self.record_debug(
626-
field,
627-
&format_args!("{} {}.source={}", value, field, source),
628-
)
651+
self.record_debug(field, &format_args!("{}, {}: {}", value, field, source))
629652
} else {
630653
self.record_debug(field, &format_args!("{}", value))
631654
}

0 commit comments

Comments
 (0)