tracing/tracing-fmt/examples/custom_visitor.rs
Eliza Weisman 06daa9512b
subscriber: add an abstraction for building visitors (#241)
## Motivation

The `tracing-fmt` crate crate currently uses a `NewVisitor` trait to
abstract over constructing field visitors of different types or with
different configurations:
d51d4d69d8/tracing-fmt/src/lib.rs (L192-L196)

There have been other use-cases where having this abstraction seems
valuable. For example, issue #73 is a proposal for adding utilities for
wrapping a visitor to skip a set of excluded field names. A `MakeVisitor`
abstraction would allow us to provide much more ergonomic APIs for
composing this kind of visitor behaviour.

Additionally, the current trait in `tracing-fmt` is tied pretty closely
to `tracing-fmt- specific behaviour. A generalized version of this trait
should be generic over the input type used to build the visitor.

## Solution

This PR adds a new `MakeVisitor` trait in `tracing-subscriber`.
`MakeVisitor` generalizes the `NewVisitor` trait in `tracing-fmt` to
represent constructing visitors for arbitrary targets. In addition, I've
added some additional traits representing common patterns for visitors,
such as those that format to a `fmt::Write` instance or write to an IO
stream, or those that produce output once a set of fields have been
visited. I've also added some extension traits & combinators to make
composing visitors easier, and to demonstrate that the trait in this
branch can be used for implementing this kind of wrapper.

I've also rewritten the `tracing-fmt` crate to use the new
`tracing-subscriber::MakeVisitor` trait rather than its homegrown
`fmt`-specifc version.

Closes: #240 

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
2019-10-08 14:06:37 -07:00

71 lines
1.9 KiB
Rust

#![deny(rust_2018_idioms)]
use tracing::{debug, error, info, span, trace, warn, Level};
#[tracing::instrument]
fn shave(yak: usize) -> bool {
debug!(
message = "hello! I'm gonna shave a yak.",
excitement = "yay!"
);
if yak == 3 {
warn!(target: "yak_events", "could not locate yak!");
false
} else {
trace!(target: "yak_events", "yak shaved successfully");
true
}
}
fn shave_all(yaks: usize) -> usize {
let span = span!(Level::TRACE, "shaving_yaks", yaks_to_shave = yaks);
let _enter = span.enter();
info!("shaving yaks");
let mut num_shaved = 0;
for yak in 1..=yaks {
let shaved = shave(yak);
trace!(target: "yak_events", yak, shaved);
if !shaved {
error!(message = "failed to shave yak!", yak);
} else {
num_shaved += 1;
}
trace!(target: "yak_events", yaks_shaved = num_shaved);
}
num_shaved
}
fn main() {
use tracing_fmt::format;
use tracing_subscriber::prelude::*;
let formatter =
// Construct a custom formatter for `Debug` fields
format::debug_fn(|writer, field, value| write!(writer, "{}: {:?}", field, value))
// Use `tracing-subscriber`'s extension traits to add delimiters
// between fields, and ensure that fields named "message" are
// formatted using fmt::Display.
.display_messages()
.delimited(", ");
let subscriber = tracing_fmt::FmtSubscriber::builder()
.fmt_fields(formatter)
.finish();
tracing::subscriber::with_default(subscriber, || {
let number_of_yaks = 3;
debug!("preparing to shave {} yaks", number_of_yaks);
let number_shaved = shave_all(number_of_yaks);
debug!(
message = "yak shaving completed.",
all_yaks_shaved = number_shaved == number_of_yaks,
);
});
}