mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-10-02 15:24:47 +00:00
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>
This commit is contained in:
parent
87c075e349
commit
06daa9512b
70
tracing-fmt/examples/custom_visitor.rs
Normal file
70
tracing-fmt/examples/custom_visitor.rs
Normal file
@ -0,0 +1,70 @@
|
||||
#![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,
|
||||
);
|
||||
});
|
||||
}
|
100
tracing-subscriber/src/field/debug.rs
Normal file
100
tracing-subscriber/src/field/debug.rs
Normal file
@ -0,0 +1,100 @@
|
||||
//! `MakeVisitor` wrappers for working with `fmt::Debug` fields.
|
||||
use super::{MakeVisitor, VisitFmt, VisitOutput, VisitWrite};
|
||||
use tracing_core::field::{Field, Visit};
|
||||
|
||||
use std::{fmt, io};
|
||||
|
||||
/// A visitor wrapper that ensures any `fmt::Debug` fields are formatted using
|
||||
/// the alternate (`:#`) formatter.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Alt<V>(V);
|
||||
|
||||
// TODO(eliza): When `error` as a primitive type is stable, add a
|
||||
// `DisplayErrors` wrapper...
|
||||
|
||||
// === impl Alt ===
|
||||
//
|
||||
impl<V> Alt<V> {
|
||||
/// Wraps the provided visitor so that any `fmt::Debug` fields are formated
|
||||
/// using the alternative (`:#`) formatter.
|
||||
pub fn new(inner: V) -> Self {
|
||||
Alt(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, V> MakeVisitor<T> for Alt<V>
|
||||
where
|
||||
V: MakeVisitor<T>,
|
||||
{
|
||||
type Visitor = Alt<V::Visitor>;
|
||||
|
||||
#[inline]
|
||||
fn make_visitor(&self, target: T) -> Self::Visitor {
|
||||
Alt(self.0.make_visitor(target))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Visit for Alt<V>
|
||||
where
|
||||
V: Visit,
|
||||
{
|
||||
#[inline]
|
||||
fn record_i64(&mut self, field: &Field, value: i64) {
|
||||
self.0.record_i64(field, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
self.0.record_u64(field, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record_bool(&mut self, field: &Field, value: bool) {
|
||||
self.0.record_bool(field, value)
|
||||
}
|
||||
|
||||
/// Visit a string value.
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
self.0.record_str(field, value)
|
||||
}
|
||||
|
||||
// TODO(eliza): add RecordError when stable
|
||||
// fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
|
||||
// self.record_debug(field, &format_args!("{}", value))
|
||||
// }
|
||||
|
||||
#[inline]
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||
self.0.record_debug(field, &format_args!("{:#?}", value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> VisitOutput<O> for Alt<V>
|
||||
where
|
||||
V: VisitOutput<O>,
|
||||
{
|
||||
#[inline]
|
||||
fn finish(self) -> O {
|
||||
self.0.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> VisitWrite for Alt<V>
|
||||
where
|
||||
V: VisitWrite,
|
||||
{
|
||||
#[inline]
|
||||
fn writer(&mut self) -> &mut dyn io::Write {
|
||||
self.0.writer()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> VisitFmt for Alt<V>
|
||||
where
|
||||
V: VisitFmt,
|
||||
{
|
||||
#[inline]
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write {
|
||||
self.0.writer()
|
||||
}
|
||||
}
|
183
tracing-subscriber/src/field/delimited.rs
Normal file
183
tracing-subscriber/src/field/delimited.rs
Normal file
@ -0,0 +1,183 @@
|
||||
//! A `MakeVisitor` wrapper that separates formatted fields with a delimiter.
|
||||
use super::{MakeVisitor, VisitFmt, VisitOutput};
|
||||
|
||||
use std::fmt;
|
||||
use tracing_core::field::{Field, Visit};
|
||||
|
||||
/// A `MakeVisitor` wrapper that wraps a visitor that writes formatted output so
|
||||
/// that a delimiter is inserted between writing formatted field values.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Delimited<D, V> {
|
||||
delimiter: D,
|
||||
inner: V,
|
||||
}
|
||||
|
||||
/// A visitor wrapper that inserts a delimiter after the wrapped visitor formats
|
||||
/// a field value.
|
||||
#[derive(Debug)]
|
||||
pub struct VisitDelimited<D, V> {
|
||||
delimiter: D,
|
||||
seen: bool,
|
||||
inner: V,
|
||||
err: fmt::Result,
|
||||
}
|
||||
|
||||
// === impl Delimited ===
|
||||
|
||||
impl<D, V, T> MakeVisitor<T> for Delimited<D, V>
|
||||
where
|
||||
D: AsRef<str> + Clone,
|
||||
V: MakeVisitor<T>,
|
||||
V::Visitor: VisitFmt,
|
||||
{
|
||||
type Visitor = VisitDelimited<D, V::Visitor>;
|
||||
fn make_visitor(&self, target: T) -> Self::Visitor {
|
||||
let inner = self.inner.make_visitor(target);
|
||||
VisitDelimited::new(self.delimiter.clone(), inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, V> Delimited<D, V> {
|
||||
/// Returns a new [`MakeVisitor`] implementation that wraps `inner` so that
|
||||
/// it will format each visited field separated by the provided `delimiter`.
|
||||
///
|
||||
/// [`MakeVisitor`]: ../trait.MakeVisitor.html
|
||||
pub fn new(delimiter: D, inner: V) -> Self {
|
||||
Self { delimiter, inner }
|
||||
}
|
||||
}
|
||||
|
||||
// === impl VisitDelimited ===
|
||||
|
||||
impl<D, V> VisitDelimited<D, V> {
|
||||
/// Returns a new [`Visit`] implementation that wraps `inner` so that
|
||||
/// each formatted field is separated by the provided `delimiter`.
|
||||
///
|
||||
/// [`Visit`]: https://docs.rs/tracing-core/0.1.6/tracing_core/field/trait.Visit.html
|
||||
pub fn new(delimiter: D, inner: V) -> Self {
|
||||
Self {
|
||||
delimiter,
|
||||
inner,
|
||||
seen: false,
|
||||
err: Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn delimit(&mut self)
|
||||
where
|
||||
V: VisitFmt,
|
||||
D: AsRef<str>,
|
||||
{
|
||||
if self.err.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.seen {
|
||||
self.err = self.inner.writer().write_str(self.delimiter.as_ref());
|
||||
}
|
||||
|
||||
self.seen = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, V> Visit for VisitDelimited<D, V>
|
||||
where
|
||||
V: VisitFmt,
|
||||
D: AsRef<str>,
|
||||
{
|
||||
fn record_i64(&mut self, field: &Field, value: i64) {
|
||||
self.delimit();
|
||||
self.inner.record_i64(field, value);
|
||||
}
|
||||
|
||||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
self.delimit();
|
||||
self.inner.record_u64(field, value);
|
||||
}
|
||||
|
||||
fn record_bool(&mut self, field: &Field, value: bool) {
|
||||
self.delimit();
|
||||
self.inner.record_bool(field, value);
|
||||
}
|
||||
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
self.delimit();
|
||||
self.inner.record_str(field, value);
|
||||
}
|
||||
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||
self.delimit();
|
||||
self.inner.record_debug(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, V> VisitOutput<fmt::Result> for VisitDelimited<D, V>
|
||||
where
|
||||
V: VisitFmt,
|
||||
D: AsRef<str>,
|
||||
{
|
||||
fn finish(self) -> fmt::Result {
|
||||
self.err?;
|
||||
self.inner.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, V> VisitFmt for VisitDelimited<D, V>
|
||||
where
|
||||
V: VisitFmt,
|
||||
D: AsRef<str>,
|
||||
{
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write {
|
||||
self.inner.writer()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::field::test_util::*;
|
||||
|
||||
#[test]
|
||||
fn delimited_visitor() {
|
||||
let mut s = String::new();
|
||||
let visitor = DebugVisitor::new(&mut s);
|
||||
let mut visitor = VisitDelimited::new(", ", visitor);
|
||||
|
||||
TestAttrs1::with(|attrs| attrs.record(&mut visitor));
|
||||
visitor.finish().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
s.as_str(),
|
||||
"question=\"life, the universe, and everything\", tricky=true, can_you_do_it=true"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delimited_new_visitor() {
|
||||
let make = Delimited::new("; ", MakeDebug);
|
||||
|
||||
TestAttrs1::with(|attrs| {
|
||||
let mut s = String::new();
|
||||
{
|
||||
let mut v = make.make_visitor(&mut s);
|
||||
attrs.record(&mut v);
|
||||
}
|
||||
assert_eq!(
|
||||
s.as_str(),
|
||||
"question=\"life, the universe, and everything\"; tricky=true; can_you_do_it=true"
|
||||
);
|
||||
});
|
||||
|
||||
TestAttrs2::with(|attrs| {
|
||||
let mut s = String::new();
|
||||
{
|
||||
let mut v = make.make_visitor(&mut s);
|
||||
attrs.record(&mut v);
|
||||
}
|
||||
assert_eq!(
|
||||
s.as_str(),
|
||||
"question=None; question.answer=42; tricky=true; can_you_do_it=false"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
106
tracing-subscriber/src/field/display.rs
Normal file
106
tracing-subscriber/src/field/display.rs
Normal file
@ -0,0 +1,106 @@
|
||||
//! `MakeVisitor` wrappers for working with `fmt::Displag` fields.
|
||||
use super::{MakeVisitor, VisitFmt, VisitOutput, VisitWrite};
|
||||
use tracing_core::field::{Field, Visit};
|
||||
|
||||
use std::{fmt, io};
|
||||
|
||||
/// A visitor wrapper that ensures any strings named "message" are formatted
|
||||
/// using `fmt::Display`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Messages<V>(V);
|
||||
|
||||
// TODO(eliza): When `error` as a primitive type is stable, add a
|
||||
// `DisplayErrors` wrapper...
|
||||
|
||||
// === impl Messages ===
|
||||
//
|
||||
impl<V> Messages<V> {
|
||||
/// Returns a new [`MakeVisitor`] implementation that will wrap `inner` so
|
||||
/// that any strings named `message` are formatted using `fmt::Display`.
|
||||
///
|
||||
/// [`MakeVisitor`]: ../trait.MakeVisitor.html
|
||||
pub fn new(inner: V) -> Self {
|
||||
Messages(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, V> MakeVisitor<T> for Messages<V>
|
||||
where
|
||||
V: MakeVisitor<T>,
|
||||
{
|
||||
type Visitor = Messages<V::Visitor>;
|
||||
|
||||
#[inline]
|
||||
fn make_visitor(&self, target: T) -> Self::Visitor {
|
||||
Messages(self.0.make_visitor(target))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Visit for Messages<V>
|
||||
where
|
||||
V: Visit,
|
||||
{
|
||||
#[inline]
|
||||
fn record_i64(&mut self, field: &Field, value: i64) {
|
||||
self.0.record_i64(field, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
self.0.record_u64(field, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record_bool(&mut self, field: &Field, value: bool) {
|
||||
self.0.record_bool(field, value)
|
||||
}
|
||||
|
||||
/// Visit a string value.
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
if field.name() == "message" {
|
||||
self.0.record_debug(field, &format_args!("{}", value))
|
||||
} else {
|
||||
self.0.record_str(field, value)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(eliza): add RecordError when stable
|
||||
// fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
|
||||
// self.record_debug(field, &format_args!("{}", value))
|
||||
// }
|
||||
|
||||
#[inline]
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||
self.0.record_debug(field, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> VisitOutput<O> for Messages<V>
|
||||
where
|
||||
V: VisitOutput<O>,
|
||||
{
|
||||
#[inline]
|
||||
fn finish(self) -> O {
|
||||
self.0.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> VisitWrite for Messages<V>
|
||||
where
|
||||
V: VisitWrite,
|
||||
{
|
||||
#[inline]
|
||||
fn writer(&mut self) -> &mut dyn io::Write {
|
||||
self.0.writer()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> VisitFmt for Messages<V>
|
||||
where
|
||||
V: VisitFmt,
|
||||
{
|
||||
#[inline]
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write {
|
||||
self.0.writer()
|
||||
}
|
||||
}
|
361
tracing-subscriber/src/field/mod.rs
Normal file
361
tracing-subscriber/src/field/mod.rs
Normal file
@ -0,0 +1,361 @@
|
||||
//! Utilities for working with [fields] and [field visitors].
|
||||
//!
|
||||
//! [fields]: https://docs.rs/tracing-core/latest/tracing_core/field/index.html
|
||||
//! [field visitors]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
|
||||
use std::{fmt, io};
|
||||
pub use tracing_core::field::Visit;
|
||||
use tracing_core::{
|
||||
span::{Attributes, Record},
|
||||
Event,
|
||||
};
|
||||
pub mod debug;
|
||||
pub mod delimited;
|
||||
pub mod display;
|
||||
|
||||
/// Creates new [visitors].
|
||||
///
|
||||
/// A type implementing `MakeVisitor` represents a composable factory for types
|
||||
/// implementing the [`Visit` trait][visitors]. The `MakeVisitor` trait defines
|
||||
/// a single function, `make_visitor`, which takes in a `T`-typed `target` and
|
||||
/// returns a type implementing `Visit` configured for that target. A target may
|
||||
/// be a string, output stream, or data structure that the visitor will record
|
||||
/// data to, configuration variables that determine the visitor's behavior, or
|
||||
/// `()` when no input is required to produce a visitor.
|
||||
///
|
||||
/// [visitors]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
|
||||
pub trait MakeVisitor<T> {
|
||||
/// The visitor type produced by this `MakeVisitor`.
|
||||
type Visitor: Visit;
|
||||
|
||||
/// Make a new visitor for the provided `target`.
|
||||
fn make_visitor(&self, target: T) -> Self::Visitor;
|
||||
}
|
||||
|
||||
/// A [visitor] that produces output once it has visited a set of fields.
|
||||
///
|
||||
/// [visitor]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
|
||||
pub trait VisitOutput<Out>: Visit {
|
||||
/// Completes the visitor, returning any output.
|
||||
///
|
||||
/// This is called once a full set of fields has been visited.
|
||||
fn finish(self) -> Out;
|
||||
|
||||
/// Visit a set of fields, and return the output of finishing the visitor
|
||||
/// once the fields have been visited.
|
||||
fn visit<R>(mut self, fields: &R) -> Out
|
||||
where
|
||||
R: RecordFields,
|
||||
Self: Sized,
|
||||
{
|
||||
fields.record(&mut self);
|
||||
self.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait implemented by types which can be recorded by a [visitor].
|
||||
///
|
||||
/// This allows writing code that is generic over `tracing_core`'s
|
||||
/// [`span::Attributes`][attr], [`span::Record`][rec], and [`Event`][event]
|
||||
/// types. These types all provide inherent `record` methods that allow a
|
||||
/// visitor to record their fields, but there is no common trait representing this.
|
||||
///
|
||||
/// With `RecordFields`, we can write code like this:
|
||||
/// ```
|
||||
/// use tracing_core::field::Visit;
|
||||
/// # use tracing_core::field::Field;
|
||||
/// use tracing_subscriber::field::RecordFields;
|
||||
///
|
||||
/// struct MyVisitor {
|
||||
/// // ...
|
||||
/// }
|
||||
/// # impl MyVisitor { fn new() -> Self { Self{} } }
|
||||
/// impl Visit for MyVisitor {
|
||||
/// // ...
|
||||
/// # fn record_debug(&mut self, _: &Field, _: &dyn std::fmt::Debug) {}
|
||||
/// }
|
||||
///
|
||||
/// fn record_with_my_visitor<R>(r: R)
|
||||
/// where
|
||||
/// R: RecordFields,
|
||||
/// {
|
||||
/// let mut visitor = MyVisitor::new();
|
||||
/// r.record(&mut visitor);
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
/// [visitor]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html
|
||||
/// [attr]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Attributes.html
|
||||
/// [rec]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Record.html
|
||||
/// [event]: https://docs.rs/tracing-core/latest/tracing_core/event/struct.Event.html
|
||||
pub trait RecordFields: crate::sealed::Sealed<RecordFieldsMarker> {
|
||||
/// Record all the fields in `self` with the provided `visitor`.
|
||||
fn record(&self, visitor: &mut dyn Visit);
|
||||
}
|
||||
|
||||
/// Extension trait implemented for all `MakeVisitor` implementations that
|
||||
/// produce a visitor implementing `VisitOutput`.
|
||||
pub trait MakeOutput<T, Out>
|
||||
where
|
||||
Self: MakeVisitor<T> + crate::sealed::Sealed<(T, Out)>,
|
||||
Self::Visitor: VisitOutput<Out>,
|
||||
{
|
||||
/// Visits all fields in `fields` with a new visitor constructed from
|
||||
/// `target`.
|
||||
fn visit_with<F>(&self, target: T, fields: &F) -> Out
|
||||
where
|
||||
F: RecordFields,
|
||||
{
|
||||
self.make_visitor(target).visit(fields)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait implemented by visitors to indicate that they write to an
|
||||
/// `io::Write` instance, and allow access to that writer.
|
||||
pub trait VisitWrite: VisitOutput<Result<(), io::Error>> {
|
||||
/// Returns the writer that this visitor writes to.
|
||||
fn writer(&mut self) -> &mut dyn io::Write;
|
||||
}
|
||||
|
||||
/// Extension trait implemented by visitors to indicate that they write to a
|
||||
/// `fmt::Write` instance, and allow access to that writer.
|
||||
pub trait VisitFmt: VisitOutput<fmt::Result> {
|
||||
/// Returns the formatter that this visitor writes to.
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write;
|
||||
}
|
||||
|
||||
/// Extension trait providing `MakeVisitor` combinators.
|
||||
pub trait MakeExt<T>
|
||||
where
|
||||
Self: MakeVisitor<T> + Sized,
|
||||
Self: crate::sealed::Sealed<MakeExtMarker<T>>,
|
||||
{
|
||||
/// Wraps `self` so that any `fmt::Debug` fields are recorded using the
|
||||
/// alternate formatter (`{:#?}`).
|
||||
fn debug_alt(self) -> debug::Alt<Self> {
|
||||
debug::Alt::new(self)
|
||||
}
|
||||
|
||||
/// Wraps `self` so that any string fields named "message" are recorded
|
||||
/// using `fmt::Display`.
|
||||
fn display_messages(self) -> display::Messages<Self> {
|
||||
display::Messages::new(self)
|
||||
}
|
||||
|
||||
/// Wraps `self` so that when fields are formatted to a writer, they are
|
||||
/// separated by the provided `delimiter`.
|
||||
fn delimited<D>(self, delimiter: D) -> delimited::Delimited<D, Self>
|
||||
where
|
||||
D: AsRef<str> + Clone,
|
||||
Self::Visitor: VisitFmt,
|
||||
{
|
||||
delimited::Delimited::new(delimiter, self)
|
||||
}
|
||||
}
|
||||
|
||||
// === impl RecordFields ===
|
||||
|
||||
impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Event<'a> {}
|
||||
impl<'a> RecordFields for Event<'a> {
|
||||
fn record(&self, visitor: &mut dyn Visit) {
|
||||
Event::record(&self, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Attributes<'a> {}
|
||||
impl<'a> RecordFields for Attributes<'a> {
|
||||
fn record(&self, visitor: &mut dyn Visit) {
|
||||
Attributes::record(&self, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Record<'a> {}
|
||||
impl<'a> RecordFields for Record<'a> {
|
||||
fn record(&self, visitor: &mut dyn Visit) {
|
||||
Record::record(&self, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F> crate::sealed::Sealed<RecordFieldsMarker> for &'a F where F: RecordFields {}
|
||||
impl<'a, F> RecordFields for &'a F
|
||||
where
|
||||
F: RecordFields,
|
||||
{
|
||||
fn record(&self, visitor: &mut dyn Visit) {
|
||||
F::record(*self, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
// === blanket impls ===
|
||||
|
||||
impl<T, V, F> MakeVisitor<T> for F
|
||||
where
|
||||
F: Fn(T) -> V,
|
||||
V: Visit,
|
||||
{
|
||||
type Visitor = V;
|
||||
fn make_visitor(&self, target: T) -> Self::Visitor {
|
||||
(self)(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Out, M> crate::sealed::Sealed<(T, Out)> for M
|
||||
where
|
||||
M: MakeVisitor<T>,
|
||||
M::Visitor: VisitOutput<Out>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, Out, M> MakeOutput<T, Out> for M
|
||||
where
|
||||
M: MakeVisitor<T>,
|
||||
M::Visitor: VisitOutput<Out>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, M> crate::sealed::Sealed<MakeExtMarker<T>> for M where M: MakeVisitor<T> + Sized {}
|
||||
|
||||
impl<T, M> MakeExt<T> for M
|
||||
where
|
||||
M: MakeVisitor<T> + Sized,
|
||||
M: crate::sealed::Sealed<MakeExtMarker<T>>,
|
||||
{
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[doc(hidden)]
|
||||
pub struct MakeExtMarker<T> {
|
||||
_p: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[doc(hidden)]
|
||||
pub struct RecordFieldsMarker {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
pub(in crate::field) mod test_util {
|
||||
use super::*;
|
||||
use tracing_core::{
|
||||
callsite::Callsite,
|
||||
field::{Field, Value},
|
||||
metadata::{Kind, Level, Metadata},
|
||||
};
|
||||
|
||||
pub(crate) struct TestAttrs1;
|
||||
pub(crate) struct TestAttrs2;
|
||||
|
||||
impl TestAttrs1 {
|
||||
pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T {
|
||||
let fieldset = TEST_META_1.fields();
|
||||
let values = &[
|
||||
(
|
||||
&fieldset.field("question").unwrap(),
|
||||
Some(&"life, the universe, and everything" as &dyn Value),
|
||||
),
|
||||
(&fieldset.field("question.answer").unwrap(), None),
|
||||
(
|
||||
&fieldset.field("tricky").unwrap(),
|
||||
Some(&true as &dyn Value),
|
||||
),
|
||||
(
|
||||
&fieldset.field("can_you_do_it").unwrap(),
|
||||
Some(&true as &dyn Value),
|
||||
),
|
||||
];
|
||||
let valueset = fieldset.value_set(values);
|
||||
let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset);
|
||||
f(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
impl TestAttrs2 {
|
||||
pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T {
|
||||
let fieldset = TEST_META_1.fields();
|
||||
let none = tracing_core::field::debug(&Option::<&str>::None);
|
||||
let values = &[
|
||||
(
|
||||
&fieldset.field("question").unwrap(),
|
||||
Some(&none as &dyn Value),
|
||||
),
|
||||
(
|
||||
&fieldset.field("question.answer").unwrap(),
|
||||
Some(&42 as &dyn Value),
|
||||
),
|
||||
(
|
||||
&fieldset.field("tricky").unwrap(),
|
||||
Some(&true as &dyn Value),
|
||||
),
|
||||
(
|
||||
&fieldset.field("can_you_do_it").unwrap(),
|
||||
Some(&false as &dyn Value),
|
||||
),
|
||||
];
|
||||
let valueset = fieldset.value_set(values);
|
||||
let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset);
|
||||
f(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
struct TestCallsite1;
|
||||
static TEST_CALLSITE_1: &'static dyn Callsite = &TestCallsite1;
|
||||
static TEST_META_1: Metadata<'static> = tracing_core::metadata! {
|
||||
name: "field_test1",
|
||||
target: module_path!(),
|
||||
level: Level::INFO,
|
||||
fields: &["question", "question.answer", "tricky", "can_you_do_it"],
|
||||
callsite: TEST_CALLSITE_1,
|
||||
kind: Kind::SPAN,
|
||||
};
|
||||
|
||||
impl Callsite for TestCallsite1 {
|
||||
fn set_interest(&self, _: tracing_core::subscriber::Interest) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &Metadata<'_> {
|
||||
&TEST_META_1
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MakeDebug;
|
||||
pub(crate) struct DebugVisitor<'a> {
|
||||
writer: &'a mut dyn fmt::Write,
|
||||
err: fmt::Result,
|
||||
}
|
||||
|
||||
impl<'a> DebugVisitor<'a> {
|
||||
pub(crate) fn new(writer: &'a mut dyn fmt::Write) -> Self {
|
||||
Self {
|
||||
writer,
|
||||
err: Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit for DebugVisitor<'a> {
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||
write!(&mut self.writer, "{}={:?}", field, value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VisitOutput<fmt::Result> for DebugVisitor<'a> {
|
||||
fn finish(self) -> fmt::Result {
|
||||
self.err
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VisitFmt for DebugVisitor<'a> {
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MakeVisitor<&'a mut dyn fmt::Write> for MakeDebug {
|
||||
type Visitor = DebugVisitor<'a>;
|
||||
fn make_visitor(&self, w: &'a mut dyn fmt::Write) -> DebugVisitor<'a> {
|
||||
DebugVisitor::new(w)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
//! Formatters for logging `tracing` events.
|
||||
use super::span;
|
||||
use super::time::{self, FormatTime, SystemTime};
|
||||
#[cfg(feature = "tracing-log")]
|
||||
use tracing_log::NormalizeEvent;
|
||||
|
||||
use crate::field::{MakeOutput, MakeVisitor, RecordFields, VisitFmt, VisitOutput};
|
||||
use std::fmt::{self, Write};
|
||||
use std::marker::PhantomData;
|
||||
use tracing_core::{
|
||||
field::{self, Field},
|
||||
field::{self, Field, Visit},
|
||||
Event, Level,
|
||||
};
|
||||
#[cfg(feature = "tracing-log")]
|
||||
use tracing_log::NormalizeEvent;
|
||||
|
||||
#[cfg(feature = "ansi")]
|
||||
use ansi_term::{Colour, Style};
|
||||
@ -20,7 +20,10 @@ use ansi_term::{Colour, Style};
|
||||
/// dispatched to [`FmtSubscriber`], the subscriber forwards it to its associated `FormatEvent` to
|
||||
/// emit a log message.
|
||||
///
|
||||
/// This trait is already implemented for function pointers with the same signature as `format`.
|
||||
/// This trait is already implemented for function pointers with the same
|
||||
/// signature as `format_event`.
|
||||
///
|
||||
/// [`FmtSubscriber`]: ../fmt/struct.Subscriber.html
|
||||
pub trait FormatEvent<N> {
|
||||
/// Write a log message for `Event` in `Context` to the given `Write`.
|
||||
fn format_event(
|
||||
@ -43,7 +46,50 @@ impl<N> FormatEvent<N>
|
||||
(*self)(ctx, writer, event)
|
||||
}
|
||||
}
|
||||
/// A type that can format a [set of fields] to a `fmt::Write`.
|
||||
///
|
||||
/// `FormatFields` is primarily used in the context of [`FmtSubscriber`]. Each
|
||||
/// time a span or event with fields is recorded, the subscriber will format
|
||||
/// those fields with its associated `FormatFields` implementation.
|
||||
///
|
||||
/// [set of fields]: ../field/trait.RecordFields.html
|
||||
/// [`FmtSubscriber`]: ../fmt/struct.Subscriber.html
|
||||
pub trait FormatFields<'writer> {
|
||||
/// Format the provided `fields` to the provided `writer`, returning a result.
|
||||
fn format_fields<R: RecordFields>(
|
||||
&self,
|
||||
writer: &'writer mut dyn fmt::Write,
|
||||
fields: R,
|
||||
) -> fmt::Result;
|
||||
}
|
||||
|
||||
/// Returns a [`FormatFields`] implementation that formats fields using the
|
||||
/// provided function or closure.
|
||||
///
|
||||
/// [`FormatFields`]: trait.FormatFields.html
|
||||
pub fn debug_fn<F>(f: F) -> FieldFn<F>
|
||||
where
|
||||
F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result + Clone,
|
||||
{
|
||||
FieldFn(f)
|
||||
}
|
||||
|
||||
/// A [`FormatFields`] implementation that formats fields by calling a function
|
||||
/// or closure.
|
||||
///
|
||||
/// [`FormatFields`]: trait.FormatFields.html
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FieldFn<F>(F);
|
||||
/// The [visitor] produced by [`FieldFn`]'s [`MakeVisitor`] implementation.
|
||||
///
|
||||
/// [visitor]: ../../field/trait.Visit.html
|
||||
/// [`FieldFn`]: struct.FieldFn.html
|
||||
/// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html
|
||||
pub struct FieldFnVisitor<'a, F> {
|
||||
f: F,
|
||||
writer: &'a mut dyn fmt::Write,
|
||||
result: fmt::Result,
|
||||
}
|
||||
/// Marker for `Format` that indicates that the compact log format should be used.
|
||||
///
|
||||
/// The compact format only includes the fields from the most recently entered span.
|
||||
@ -131,7 +177,7 @@ impl<F, T> Format<F, T> {
|
||||
|
||||
impl<N, T> FormatEvent<N> for Format<Full, T>
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
N: for<'writer> FormatFields<'writer>,
|
||||
T: FormatTime,
|
||||
{
|
||||
fn format_event(
|
||||
@ -176,17 +222,14 @@ where
|
||||
""
|
||||
}
|
||||
)?;
|
||||
{
|
||||
let mut recorder = ctx.new_visitor(writer, true);
|
||||
event.record(&mut recorder);
|
||||
}
|
||||
ctx.format_fields(writer, event)?;
|
||||
writeln!(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, T> FormatEvent<N> for Format<Compact, T>
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
N: for<'writer> FormatFields<'writer>,
|
||||
T: FormatTime,
|
||||
{
|
||||
fn format_event(
|
||||
@ -230,60 +273,107 @@ where
|
||||
""
|
||||
}
|
||||
)?;
|
||||
{
|
||||
let mut recorder = ctx.new_visitor(writer, true);
|
||||
event.record(&mut recorder);
|
||||
}
|
||||
ctx.format_fields(writer, event)?;
|
||||
ctx.with_current(|(_, span)| write!(writer, " {}", span.fields()))
|
||||
.unwrap_or(Ok(()))?;
|
||||
writeln!(writer)
|
||||
}
|
||||
}
|
||||
|
||||
/// The default implementation of `NewVisitor` that records fields using the
|
||||
/// default format.
|
||||
// === impl FormatFields ===
|
||||
|
||||
impl<'writer, M> FormatFields<'writer> for M
|
||||
where
|
||||
M: MakeOutput<&'writer mut dyn fmt::Write, fmt::Result>,
|
||||
M::Visitor: VisitFmt + VisitOutput<fmt::Result>,
|
||||
{
|
||||
fn format_fields<R: RecordFields>(
|
||||
&self,
|
||||
writer: &'writer mut dyn fmt::Write,
|
||||
fields: R,
|
||||
) -> fmt::Result {
|
||||
let mut v = self.make_visitor(writer);
|
||||
fields.record(&mut v);
|
||||
v.finish()
|
||||
}
|
||||
}
|
||||
/// The default [`FormatFields`] implementation.
|
||||
///
|
||||
/// [`FormatFields`]: trait.FormatFields.html
|
||||
#[derive(Debug)]
|
||||
pub struct NewRecorder {
|
||||
_p: (),
|
||||
pub struct DefaultFields {
|
||||
// reserve the ability to add fields to this without causing a breaking
|
||||
// change in the future.
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl NewRecorder {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { _p: () }
|
||||
/// The [visitor] produced by [`DefaultFields`]'s [`MakeVisitor`] implementation.
|
||||
///
|
||||
/// [visitor]: ../../field/trait.Visit.html
|
||||
/// [`DefaultFields`]: struct.DefaultFields.html
|
||||
/// [`MakeVisitor`]: ../../field/trait.MakeVisitor.html
|
||||
pub struct DefaultVisitor<'a> {
|
||||
writer: &'a mut dyn Write,
|
||||
is_empty: bool,
|
||||
result: fmt::Result,
|
||||
}
|
||||
|
||||
impl DefaultFields {
|
||||
/// Returns a new default [`FormatFields`] implementation.
|
||||
///
|
||||
/// [`FormatFields`]: trait.FormatFields.html
|
||||
pub fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
/// A visitor that records fields using the default format.
|
||||
pub struct Recorder<'a> {
|
||||
writer: &'a mut dyn Write,
|
||||
is_empty: bool,
|
||||
impl Default for DefaultFields {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Recorder<'a> {
|
||||
pub(crate) fn new(writer: &'a mut dyn Write, is_empty: bool) -> Self {
|
||||
Self { writer, is_empty }
|
||||
impl<'a> MakeVisitor<&'a mut dyn Write> for DefaultFields {
|
||||
type Visitor = DefaultVisitor<'a>;
|
||||
|
||||
#[inline]
|
||||
fn make_visitor(&self, target: &'a mut dyn Write) -> Self::Visitor {
|
||||
DefaultVisitor::new(target, true)
|
||||
}
|
||||
}
|
||||
|
||||
// === impl DefaultVisitor ===
|
||||
|
||||
impl<'a> DefaultVisitor<'a> {
|
||||
/// Returns a new default visitor that formats to the provided `writer`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `writer`: the writer to format to.
|
||||
/// - `is_empty`: whether or not any fields have been previously written to
|
||||
/// that writer.
|
||||
pub fn new(writer: &'a mut dyn Write, is_empty: bool) -> Self {
|
||||
Self {
|
||||
writer,
|
||||
is_empty,
|
||||
result: Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_pad(&mut self) {
|
||||
if self.is_empty {
|
||||
self.is_empty = false;
|
||||
} else {
|
||||
let _ = write!(self.writer, " ");
|
||||
self.result = write!(self.writer, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> super::NewVisitor<'a> for NewRecorder {
|
||||
type Visitor = Recorder<'a>;
|
||||
|
||||
#[inline]
|
||||
fn make(&self, writer: &'a mut dyn Write, is_empty: bool) -> Self::Visitor {
|
||||
Recorder::new(writer, is_empty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> field::Visit for Recorder<'a> {
|
||||
impl<'a> field::Visit for DefaultVisitor<'a> {
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
if self.result.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
if field.name() == "message" {
|
||||
self.record_debug(field, &format_args!("{}", value))
|
||||
} else {
|
||||
@ -303,8 +393,12 @@ impl<'a> field::Visit for Recorder<'a> {
|
||||
}
|
||||
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||
if self.result.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.maybe_pad();
|
||||
let _ = match field.name() {
|
||||
self.result = match field.name() {
|
||||
"message" => write!(self.writer, "{:?}", value),
|
||||
// Skip fields that are actually log metadata that have already been handled
|
||||
#[cfg(feature = "tracing-log")]
|
||||
@ -315,12 +409,24 @@ impl<'a> field::Visit for Recorder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// This has to be a manual impl, as `&mut dyn Writer` doesn't implement `Debug`.
|
||||
impl<'a> fmt::Debug for Recorder<'a> {
|
||||
impl<'a> crate::field::VisitOutput<fmt::Result> for DefaultVisitor<'a> {
|
||||
fn finish(self) -> fmt::Result {
|
||||
self.result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::field::VisitFmt for DefaultVisitor<'a> {
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for DefaultVisitor<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Recorder")
|
||||
f.debug_struct("DefaultVisitor")
|
||||
.field("writer", &format_args!("<dyn fmt::Write>"))
|
||||
.field("is_empty", &self.is_empty)
|
||||
.field("result", &self.result)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -344,10 +450,7 @@ impl<'a, N: 'a> FmtCtx<'a, N> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "ansi")]
|
||||
impl<'a, N> fmt::Display for FmtCtx<'a, N>
|
||||
where
|
||||
N: super::NewVisitor<'a>,
|
||||
{
|
||||
impl<'a, N> fmt::Display for FmtCtx<'a, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut seen = false;
|
||||
self.ctx.visit_spans(|_, span| {
|
||||
@ -406,10 +509,7 @@ impl<'a, N: 'a> FullCtx<'a, N> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "ansi")]
|
||||
impl<'a, N> fmt::Display for FullCtx<'a, N>
|
||||
where
|
||||
N: super::NewVisitor<'a>,
|
||||
{
|
||||
impl<'a, N> fmt::Display for FullCtx<'a, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut seen = false;
|
||||
let style = if self.ansi {
|
||||
@ -510,6 +610,62 @@ impl<'a> fmt::Display for FmtLevel<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// === impl FieldFn ===
|
||||
|
||||
impl<'a, F> MakeVisitor<&'a mut dyn fmt::Write> for FieldFn<F>
|
||||
where
|
||||
F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result + Clone,
|
||||
{
|
||||
type Visitor = FieldFnVisitor<'a, F>;
|
||||
|
||||
fn make_visitor(&self, writer: &'a mut dyn fmt::Write) -> Self::Visitor {
|
||||
FieldFnVisitor {
|
||||
writer,
|
||||
f: self.0.clone(),
|
||||
result: Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F> Visit for FieldFnVisitor<'a, F>
|
||||
where
|
||||
F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result,
|
||||
{
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||
if self.result.is_ok() {
|
||||
self.result = (self.f)(&mut self.writer, field, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F> VisitOutput<fmt::Result> for FieldFnVisitor<'a, F>
|
||||
where
|
||||
F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result,
|
||||
{
|
||||
fn finish(self) -> fmt::Result {
|
||||
self.result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F> VisitFmt for FieldFnVisitor<'a, F>
|
||||
where
|
||||
F: Fn(&mut dyn fmt::Write, &Field, &dyn fmt::Debug) -> fmt::Result,
|
||||
{
|
||||
fn writer(&mut self) -> &mut dyn fmt::Write {
|
||||
&mut *self.writer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FieldFnVisitor")
|
||||
.field("f", &format_args!("<Fn>"))
|
||||
.field("writer", &format_args!("<dyn fmt::Write>"))
|
||||
.field("result", &self.result)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
|
@ -10,9 +10,9 @@
|
||||
//!
|
||||
//! [`tracing`]: https://crates.io/crates/tracing
|
||||
//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
|
||||
use tracing_core::{field, subscriber::Interest, Event, Metadata};
|
||||
use std::{any::TypeId, cell::RefCell, io};
|
||||
use tracing_core::{subscriber::Interest, Event, Metadata};
|
||||
|
||||
use std::{any::TypeId, cell::RefCell, fmt, io};
|
||||
pub mod format;
|
||||
mod span;
|
||||
pub mod time;
|
||||
@ -22,14 +22,18 @@ use crate::filter::LevelFilter;
|
||||
use crate::layer::{self, Layer};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::{format::FormatEvent, span::Context, writer::MakeWriter};
|
||||
pub use self::{
|
||||
format::{FormatEvent, FormatFields},
|
||||
span::Context,
|
||||
writer::MakeWriter,
|
||||
};
|
||||
|
||||
/// A `Subscriber` that logs formatted representations of `tracing` events.
|
||||
///
|
||||
/// This consists of an inner`Formatter` wrapped in a layer that performs filtering.
|
||||
/// This consists of an inner `Formatter` wrapped in a layer that performs filtering.
|
||||
#[derive(Debug)]
|
||||
pub struct Subscriber<
|
||||
N = format::NewRecorder,
|
||||
N = format::DefaultFields,
|
||||
E = format::Format<format::Full>,
|
||||
F = LevelFilter,
|
||||
W = fn() -> io::Stdout,
|
||||
@ -41,11 +45,11 @@ pub struct Subscriber<
|
||||
/// This type only logs formatted events; it does not perform any filtering.
|
||||
#[derive(Debug)]
|
||||
pub struct Formatter<
|
||||
N = format::NewRecorder,
|
||||
N = format::DefaultFields,
|
||||
E = format::Format<format::Full>,
|
||||
W = fn() -> io::Stdout,
|
||||
> {
|
||||
new_visitor: N,
|
||||
fmt_fields: N,
|
||||
fmt_event: E,
|
||||
spans: span::Store,
|
||||
settings: Settings,
|
||||
@ -55,13 +59,13 @@ pub struct Formatter<
|
||||
/// Configures and constructs `Subscriber`s.
|
||||
#[derive(Debug)]
|
||||
pub struct Builder<
|
||||
N = format::NewRecorder,
|
||||
N = format::DefaultFields,
|
||||
E = format::Format<format::Full>,
|
||||
F = LevelFilter,
|
||||
W = fn() -> io::Stdout,
|
||||
> {
|
||||
filter: F,
|
||||
new_visitor: N,
|
||||
fmt_fields: N,
|
||||
fmt_event: E,
|
||||
settings: Settings,
|
||||
make_writer: W,
|
||||
@ -103,7 +107,7 @@ impl Default for Subscriber {
|
||||
|
||||
impl<N, E, F, W> tracing_core::Subscriber for Subscriber<N, E, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
N: for<'writer> FormatFields<'writer> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
F: Layer<Formatter<N, E, W>> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
@ -177,17 +181,17 @@ where
|
||||
// === impl Formatter ===
|
||||
impl<N, E, W> Formatter<N, E, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a>,
|
||||
N: for<'writer> FormatFields<'writer>,
|
||||
{
|
||||
#[inline]
|
||||
fn ctx(&self) -> span::Context<'_, N> {
|
||||
span::Context::new(&self.spans, &self.new_visitor)
|
||||
span::Context::new(&self.spans, &self.fmt_fields)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, W> tracing_core::Subscriber for Formatter<N, E, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
N: for<'writer> FormatFields<'writer> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
{
|
||||
@ -201,12 +205,12 @@ where
|
||||
|
||||
#[inline]
|
||||
fn new_span(&self, attrs: &span::Attributes<'_>) -> span::Id {
|
||||
self.spans.new_span(attrs, &self.new_visitor)
|
||||
self.spans.new_span(attrs, &self.fmt_fields)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
|
||||
self.spans.record(span, values, &self.new_visitor)
|
||||
self.spans.record(span, values, &self.fmt_fields)
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
|
||||
@ -275,41 +279,19 @@ where
|
||||
_ if id == TypeId::of::<Self>() => Some(self as *const Self as *const ()),
|
||||
// _ if id == TypeId::of::<F>() => Some(&self.filter as *const F as *const ()),
|
||||
_ if id == TypeId::of::<E>() => Some(&self.fmt_event as *const E as *const ()),
|
||||
_ if id == TypeId::of::<N>() => Some(&self.new_visitor as *const N as *const ()),
|
||||
_ if id == TypeId::of::<N>() => Some(&self.fmt_fields as *const N as *const ()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that can construct a new field visitor for formatting the fields on a
|
||||
/// span or event.
|
||||
pub trait NewVisitor<'a> {
|
||||
/// The type of the returned `Visitor`.
|
||||
type Visitor: field::Visit + 'a;
|
||||
/// Returns a new `Visitor` that writes to the provided `writer`.
|
||||
fn make(&self, writer: &'a mut dyn fmt::Write, is_empty: bool) -> Self::Visitor;
|
||||
}
|
||||
|
||||
impl<'a, F, R> NewVisitor<'a> for F
|
||||
where
|
||||
F: Fn(&'a mut dyn fmt::Write, bool) -> R,
|
||||
R: field::Visit + 'a,
|
||||
{
|
||||
type Visitor = R;
|
||||
|
||||
#[inline]
|
||||
fn make(&self, writer: &'a mut dyn fmt::Write, is_empty: bool) -> Self::Visitor {
|
||||
(self)(writer, is_empty)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Builder =====
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder {
|
||||
filter: Subscriber::DEFAULT_MAX_LEVEL,
|
||||
new_visitor: format::NewRecorder::new(),
|
||||
fmt_fields: format::DefaultFields::default(),
|
||||
fmt_event: format::Format::default(),
|
||||
settings: Settings::default(),
|
||||
make_writer: io::stdout,
|
||||
@ -319,7 +301,7 @@ impl Default for Builder {
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
N: for<'writer> FormatFields<'writer> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
F: Layer<Formatter<N, E, W>> + 'static,
|
||||
@ -327,7 +309,7 @@ where
|
||||
/// Finish the builder, returning a new `FmtSubscriber`.
|
||||
pub fn finish(self) -> Subscriber<N, E, F, W> {
|
||||
let subscriber = Formatter {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event,
|
||||
spans: span::Store::with_capacity(self.settings.initial_span_capacity),
|
||||
settings: self.settings,
|
||||
@ -341,12 +323,12 @@ where
|
||||
|
||||
impl<N, L, T, F, W> Builder<N, format::Format<L, T>, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
N: for<'writer> FormatFields<'writer> + 'static,
|
||||
{
|
||||
/// Use the given `timer` for log message timestamps.
|
||||
pub fn with_timer<T2>(self, timer: T2) -> Builder<N, format::Format<L, T2>, F, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event.with_timer(timer),
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
@ -357,7 +339,7 @@ where
|
||||
/// Do not emit timestamps with log messages.
|
||||
pub fn without_time(self) -> Builder<N, format::Format<L, ()>, F, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event.without_time(),
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
@ -395,7 +377,7 @@ where
|
||||
) -> Builder<N, E, crate::reload::Layer<crate::EnvFilter, Formatter<N, E, W>>, W> {
|
||||
let (filter, _) = crate::reload::Layer::new(self.filter);
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event,
|
||||
filter,
|
||||
settings: self.settings,
|
||||
@ -419,12 +401,30 @@ where
|
||||
impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
/// Sets the Visitor that the subscriber being built will use to record
|
||||
/// fields.
|
||||
pub fn with_visitor<N2>(self, new_visitor: N2) -> Builder<N2, E, F, W>
|
||||
///
|
||||
/// For example:
|
||||
/// ```rust
|
||||
/// use tracing_subscriber::fmt::{Subscriber, 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 the `tracing_subscriber::MakeFmtExt` trait to wrap the
|
||||
/// // formatter so that a delimiter is added between fields.
|
||||
/// .delimited(", ");
|
||||
///
|
||||
/// let subscriber = Subscriber::builder()
|
||||
/// .fmt_fields(formatter)
|
||||
/// .finish();
|
||||
/// # drop(subscriber)
|
||||
/// ```
|
||||
pub fn fmt_fields<N2>(self, fmt_fields: N2) -> Builder<N2, E, F, W>
|
||||
where
|
||||
N2: for<'a> NewVisitor<'a> + 'static,
|
||||
N2: for<'writer> FormatFields<'writer> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor,
|
||||
fmt_fields: fmt_fields.into(),
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
@ -495,7 +495,7 @@ impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
{
|
||||
let filter = filter.into();
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event,
|
||||
filter,
|
||||
settings: self.settings,
|
||||
@ -559,7 +559,7 @@ impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
pub fn with_max_level(self, filter: impl Into<LevelFilter>) -> Builder<N, E, LevelFilter, W> {
|
||||
let filter = filter.into();
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event,
|
||||
filter,
|
||||
settings: self.settings,
|
||||
@ -572,12 +572,12 @@ impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
/// See [`format::Compact`].
|
||||
pub fn compact(self) -> Builder<N, format::Format<format::Compact>, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
N: for<'writer> FormatFields<'writer> + 'static,
|
||||
{
|
||||
Builder {
|
||||
fmt_event: format::Format::default().compact(),
|
||||
filter: self.filter,
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
@ -590,7 +590,7 @@ impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
E2: FormatEvent<N> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
@ -646,7 +646,7 @@ impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
W2: MakeWriter + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_fields: self.fmt_fields,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
@ -748,7 +748,7 @@ mod test {
|
||||
fn subscriber_downcasts_to_parts() {
|
||||
let subscriber = Subscriber::builder().finish();
|
||||
let dispatch = Dispatch::new(subscriber);
|
||||
assert!(dispatch.downcast_ref::<format::NewRecorder>().is_some());
|
||||
assert!(dispatch.downcast_ref::<format::DefaultFields>().is_some());
|
||||
assert!(dispatch.downcast_ref::<LevelFilter>().is_some());
|
||||
assert!(dispatch.downcast_ref::<format::Format>().is_some())
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ use std::collections::HashSet;
|
||||
pub(crate) use tracing_core::span::{Attributes, Current, Id, Record};
|
||||
use tracing_core::{dispatcher, Metadata};
|
||||
|
||||
use super::format::FormatFields;
|
||||
|
||||
pub struct Span<'a> {
|
||||
lock: OwningHandle<RwLockReadGuard<'a, Slab>, RwLockReadGuard<'a, Slot>>,
|
||||
}
|
||||
@ -18,9 +20,9 @@ pub struct Span<'a> {
|
||||
/// Represents the `Subscriber`'s view of the current span context to a
|
||||
/// formatter.
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a, N> {
|
||||
pub struct Context<'a, F> {
|
||||
store: &'a Store,
|
||||
new_visitor: &'a N,
|
||||
fmt_fields: &'a F,
|
||||
}
|
||||
|
||||
/// Stores data associated with currently-active spans.
|
||||
@ -190,7 +192,7 @@ impl<'a> fmt::Debug for Span<'a> {
|
||||
|
||||
// ===== impl Context =====
|
||||
|
||||
impl<'a, N> Context<'a, N> {
|
||||
impl<'a, F> Context<'a, F> {
|
||||
/// Applies a function to each span in the current trace context.
|
||||
///
|
||||
/// The function is applied in order, beginning with the root of the trace,
|
||||
@ -201,9 +203,9 @@ impl<'a, N> Context<'a, N> {
|
||||
///
|
||||
/// Note that if we are currently unwinding, this will do nothing, rather
|
||||
/// than potentially causing a double panic.
|
||||
pub fn visit_spans<F, E>(&self, mut f: F) -> Result<(), E>
|
||||
pub fn visit_spans<N, E>(&self, mut f: N) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(&Id, Span<'_>) -> Result<(), E>,
|
||||
N: FnMut(&Id, Span<'_>) -> Result<(), E>,
|
||||
{
|
||||
CONTEXT
|
||||
.try_with(|current| {
|
||||
@ -223,9 +225,9 @@ impl<'a, N> Context<'a, N> {
|
||||
}
|
||||
|
||||
/// Executes a closure with the reference to the current span.
|
||||
pub fn with_current<F, R>(&self, f: F) -> Option<R>
|
||||
pub fn with_current<N, R>(&self, f: N) -> Option<R>
|
||||
where
|
||||
F: FnOnce((&Id, Span<'_>)) -> R,
|
||||
N: FnOnce((&Id, Span<'_>)) -> R,
|
||||
{
|
||||
// If the lock is poisoned or the thread local has already been
|
||||
// destroyed, we might be in the middle of unwinding, so this
|
||||
@ -244,21 +246,21 @@ impl<'a, N> Context<'a, N> {
|
||||
.ok()?
|
||||
}
|
||||
|
||||
pub(crate) fn new(store: &'a Store, new_visitor: &'a N) -> Self {
|
||||
Self { store, new_visitor }
|
||||
pub(crate) fn new(store: &'a Store, fmt_fields: &'a F) -> Self {
|
||||
Self { store, fmt_fields }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new visitor that formats span fields to the provided writer.
|
||||
/// The visitor configuration is provided by the subscriber.
|
||||
pub fn new_visitor<'writer>(
|
||||
&self,
|
||||
writer: &'writer mut dyn fmt::Write,
|
||||
is_empty: bool,
|
||||
) -> N::Visitor
|
||||
impl<'ctx, 'writer, F> FormatFields<'writer> for Context<'ctx, F>
|
||||
where
|
||||
F: FormatFields<'writer>,
|
||||
{
|
||||
#[inline]
|
||||
fn format_fields<R>(&self, writer: &'writer mut dyn fmt::Write, fields: R) -> fmt::Result
|
||||
where
|
||||
N: super::NewVisitor<'writer>,
|
||||
R: crate::field::RecordFields,
|
||||
{
|
||||
self.new_visitor.make(writer, is_empty)
|
||||
self.fmt_fields.format_fields(writer, fields)
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,9 +313,9 @@ impl Store {
|
||||
/// recently emptied span will be reused. Otherwise, a new allocation will
|
||||
/// be added to the slab.
|
||||
#[inline]
|
||||
pub(crate) fn new_span<N>(&self, attrs: &Attributes<'_>, new_visitor: &N) -> Id
|
||||
pub(crate) fn new_span<F>(&self, attrs: &Attributes<'_>, fmt_fields: &F) -> Id
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
F: for<'writer> FormatFields<'writer>,
|
||||
{
|
||||
let mut span = Some(Data::new(attrs, self));
|
||||
|
||||
@ -343,7 +345,7 @@ impl Store {
|
||||
// Is our snapshot still valid?
|
||||
if self.next.compare_and_swap(head, next, Ordering::Release) == head {
|
||||
// We can finally fill the slot!
|
||||
slot.fill(span.take().unwrap(), attrs, new_visitor);
|
||||
slot.fill(span.take().unwrap(), attrs, fmt_fields);
|
||||
return idx_to_id(head);
|
||||
}
|
||||
}
|
||||
@ -360,7 +362,7 @@ impl Store {
|
||||
let len = this.slab.len();
|
||||
|
||||
// Insert the span into a new slot.
|
||||
let slot = Slot::new(span.take().unwrap(), attrs, new_visitor);
|
||||
let slot = Slot::new(span.take().unwrap(), attrs, fmt_fields);
|
||||
this.slab.push(RwLock::new(slot));
|
||||
// TODO: can we grow the slab in chunks to avoid having to
|
||||
// realloc as often?
|
||||
@ -388,14 +390,14 @@ impl Store {
|
||||
|
||||
/// Records that the span with the given `id` has the given `fields`.
|
||||
#[inline]
|
||||
pub(crate) fn record<N>(&self, id: &Id, fields: &Record<'_>, new_recorder: &N)
|
||||
pub(crate) fn record<F>(&self, id: &Id, fields: &Record<'_>, fmt_fields: &F)
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
F: for<'writer> FormatFields<'writer>,
|
||||
{
|
||||
let slab = try_lock!(self.inner.read(), else return);
|
||||
let slot = slab.write_slot(id_to_idx(id));
|
||||
if let Some(mut slot) = slot {
|
||||
slot.record(fields, new_recorder);
|
||||
slot.record(fields, fmt_fields);
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,15 +483,14 @@ impl Drop for Data {
|
||||
}
|
||||
|
||||
impl Slot {
|
||||
fn new<N>(mut data: Data, attrs: &Attributes<'_>, new_visitor: &N) -> Self
|
||||
fn new<F>(mut data: Data, attrs: &Attributes<'_>, fmt_fields: &F) -> Self
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
F: for<'writer> FormatFields<'writer>,
|
||||
{
|
||||
let mut fields = String::new();
|
||||
{
|
||||
let mut recorder = new_visitor.make(&mut fields, true);
|
||||
attrs.record(&mut recorder);
|
||||
}
|
||||
fmt_fields
|
||||
.format_fields(&mut fields, attrs)
|
||||
.expect("formatting to string should not fail");
|
||||
if fields.is_empty() {
|
||||
data.is_empty = false;
|
||||
}
|
||||
@ -506,15 +507,14 @@ impl Slot {
|
||||
}
|
||||
}
|
||||
|
||||
fn fill<N>(&mut self, mut data: Data, attrs: &Attributes<'_>, new_visitor: &N) -> usize
|
||||
fn fill<F>(&mut self, mut data: Data, attrs: &Attributes<'_>, fmt_fields: &F) -> usize
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
F: for<'writer> FormatFields<'writer>,
|
||||
{
|
||||
let fields = &mut self.fields;
|
||||
{
|
||||
let mut recorder = new_visitor.make(fields, true);
|
||||
attrs.record(&mut recorder);
|
||||
}
|
||||
fmt_fields
|
||||
.format_fields(fields, attrs)
|
||||
.expect("formatting to string should not fail");
|
||||
if fields.is_empty() {
|
||||
data.is_empty = false;
|
||||
}
|
||||
@ -524,19 +524,18 @@ impl Slot {
|
||||
}
|
||||
}
|
||||
|
||||
fn record<N>(&mut self, fields: &Record<'_>, new_visitor: &N)
|
||||
fn record<F>(&mut self, fields: &Record<'_>, fmt_fields: &F)
|
||||
where
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
F: for<'writer> FormatFields<'writer>,
|
||||
{
|
||||
let state = &mut self.span;
|
||||
let buf = &mut self.fields;
|
||||
match state {
|
||||
State::Empty(_) => return,
|
||||
State::Full(ref mut data) => {
|
||||
{
|
||||
let mut recorder = new_visitor.make(buf, data.is_empty);
|
||||
fields.record(&mut recorder);
|
||||
}
|
||||
fmt_fields
|
||||
.format_fields(buf, fields)
|
||||
.expect("formatting to string should not fail");
|
||||
if buf.is_empty() {
|
||||
data.is_empty = false;
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ macro_rules! try_lock {
|
||||
};
|
||||
}
|
||||
|
||||
pub mod field;
|
||||
pub mod filter;
|
||||
#[cfg(feature = "fmt")]
|
||||
pub mod fmt;
|
||||
|
@ -3,6 +3,10 @@
|
||||
//! This brings into scope a number of extension traits that define methods on
|
||||
//! types defined here and in other crates.
|
||||
|
||||
pub use crate::field::{
|
||||
MakeExt as __tracing_subscriber_field_MakeExt,
|
||||
RecordFields as __tracing_subscriber_field_RecordFields,
|
||||
};
|
||||
pub use crate::layer::{
|
||||
Layer as __tracing_subscriber_Layer, SubscriberExt as __tracing_subscriber_SubscriberExt,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user