🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_error/
subscriber.rs

1use std::fmt;
2use std::marker::PhantomData;
3use std::{
4    any::{type_name, TypeId},
5    ptr::NonNull,
6};
7use tracing::{span, Collect, Dispatch, Metadata};
8use tracing_subscriber::fmt::format::{DefaultFields, FormatFields};
9use tracing_subscriber::{
10    fmt::FormattedFields,
11    registry::LookupSpan,
12    subscribe::{self, Subscribe},
13};
14
15/// A [subscriber] that enables capturing [`SpanTrace`]s.
16///
17/// Optionally, this type may be constructed with a [field formatter] to use
18/// when formatting the fields of each span in a trace. When no formatter is
19/// provided, the [default format] is used instead.
20///
21/// [subscriber]: tracing_subscriber::subscribe::Subscribe
22/// [`SpanTrace`]: super::SpanTrace
23/// [field formatter]: tracing_subscriber::fmt::FormatFields
24/// [default format]: tracing_subscriber::fmt::format::DefaultFields
25pub struct ErrorSubscriber<C, F = DefaultFields> {
26    format: F,
27
28    get_context: WithContext,
29    _collector: PhantomData<fn(C)>,
30}
31
32// this function "remembers" the types of the subscriber and the formatter,
33// so that we can downcast to something aware of them without knowing those
34// types at the callsite.
35pub(crate) struct WithContext(
36    fn(&Dispatch, &span::Id, f: &mut dyn FnMut(&'static Metadata<'static>, &str) -> bool),
37);
38
39impl<C, F> Subscribe<C> for ErrorSubscriber<C, F>
40where
41    C: Collect + for<'span> LookupSpan<'span>,
42    F: for<'writer> FormatFields<'writer> + 'static,
43{
44    /// Notifies this subscriber that a new span was constructed with the given
45    /// `Attributes` and `Id`.
46    fn on_new_span(
47        &self,
48        attrs: &span::Attributes<'_>,
49        id: &span::Id,
50        ctx: subscribe::Context<'_, C>,
51    ) {
52        let span = ctx.span(id).expect("span must already exist!");
53        if span.extensions().get::<FormattedFields<F>>().is_some() {
54            return;
55        }
56        let mut fields = FormattedFields::<F>::new(String::new());
57        if self.format.format_fields(fields.as_writer(), attrs).is_ok() {
58            span.extensions_mut().insert(fields);
59        }
60    }
61
62    unsafe fn downcast_raw(&self, id: TypeId) -> Option<NonNull<()>> {
63        match id {
64            id if id == TypeId::of::<Self>() => Some(NonNull::from(self).cast()),
65            id if id == TypeId::of::<WithContext>() => {
66                Some(NonNull::from(&self.get_context).cast())
67            }
68            _ => None,
69        }
70    }
71}
72
73impl<C, F> ErrorSubscriber<C, F>
74where
75    F: for<'writer> FormatFields<'writer> + 'static,
76    C: Collect + for<'span> LookupSpan<'span>,
77{
78    /// Returns a new `ErrorSubscriber` with the provided [field formatter].
79    ///
80    /// [field formatter]: tracing_subscriber::fmt::FormatFields
81    pub fn new(format: F) -> Self {
82        Self {
83            format,
84            get_context: WithContext(Self::get_context),
85            _collector: PhantomData,
86        }
87    }
88
89    fn get_context(
90        dispatch: &Dispatch,
91        id: &span::Id,
92        f: &mut dyn FnMut(&'static Metadata<'static>, &str) -> bool,
93    ) {
94        let collector = dispatch
95            .downcast_ref::<C>()
96            .expect("collector should downcast to expected type; this is a bug!");
97        let span = collector
98            .span(id)
99            .expect("registry should have a span for the current ID");
100        for span in span.scope() {
101            let cont = if let Some(fields) = span.extensions().get::<FormattedFields<F>>() {
102                f(span.metadata(), fields.fields.as_str())
103            } else {
104                f(span.metadata(), "")
105            };
106            if !cont {
107                break;
108            }
109        }
110    }
111}
112
113impl WithContext {
114    pub(crate) fn with_context(
115        &self,
116        dispatch: &Dispatch,
117        id: &span::Id,
118        mut f: impl FnMut(&'static Metadata<'static>, &str) -> bool,
119    ) {
120        (self.0)(dispatch, id, &mut f)
121    }
122}
123
124impl<C> Default for ErrorSubscriber<C>
125where
126    C: Collect + for<'span> LookupSpan<'span>,
127{
128    fn default() -> Self {
129        Self::new(DefaultFields::default())
130    }
131}
132
133impl<C, F: fmt::Debug> fmt::Debug for ErrorSubscriber<C, F> {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        f.debug_struct("ErrorSubscriber")
136            .field("format", &self.format)
137            .field("collector", &format_args!("{}", type_name::<C>()))
138            .finish()
139    }
140}