tracing_timing/
builder.rs

1#[cfg(feature = "layer")]
2use crate::TimingLayer;
3use crate::{group, EventGroup, Histogram, SpanGroup, Timing, TimingSubscriber};
4use std::hash::Hash;
5
6/// Builder for [`TimingSubscriber`] instances.
7///
8/// This type implements the [builder pattern]. It lets you easily configure and construct a new
9/// [`TimingSubscriber`] subscriber. See the individual methods for details. To start, use
10/// [`Builder::default`]:
11///
12/// ```rust
13/// use tracing_timing::{Builder, Histogram};
14/// let builder = Builder::default();
15/// let subscriber = builder.build(|| Histogram::new(3).unwrap());
16/// ```
17///
18/// See the various `new_*` methods on [`Histogram`] for how to construct an appropriate histogram
19/// in the first place. All samples recorded by the subscriber returned from [`Builder::build`]
20/// will be recorded into histograms as returned by the provided constructor. You can also
21/// construct the histograms based on the span and event group it will be tracking by using
22/// [`Builder::build_informed`].
23///
24///   [builder pattern]: https://rust-lang-nursery.github.io/api-guidelines/type-safety.html#c-builder
25pub struct Builder<S = group::ByName, E = group::ByMessage> {
26    span_group: S,
27    event_group: E,
28    time: quanta::Clock,
29    bubble_spans: bool,
30    span_close_events: bool,
31}
32
33impl Default for Builder<group::ByName, group::ByMessage> {
34    fn default() -> Self {
35        Builder {
36            span_group: group::ByName,
37            event_group: group::ByMessage,
38            time: quanta::Clock::new(),
39            bubble_spans: true,
40            span_close_events: false,
41        }
42    }
43}
44
45impl<S, E> Builder<S, E> {
46    /// Set the mechanism used to divide spans into groups.
47    ///
48    /// See [`SpanGroup`] and the [`group`] module for details.
49    pub fn spans<S2>(self, span_group: S2) -> Builder<S2, E> {
50        Builder {
51            span_group,
52            event_group: self.event_group,
53            time: self.time,
54            bubble_spans: self.bubble_spans,
55            span_close_events: self.span_close_events,
56        }
57    }
58
59    /// Set the mechanism used to divide events into per-span groups.
60    ///
61    /// See [`EventGroup`] and the [`group`] module for details.
62    pub fn events<E2>(self, event_group: E2) -> Builder<S, E2> {
63        Builder {
64            span_group: self.span_group,
65            event_group,
66            time: self.time,
67            bubble_spans: self.bubble_spans,
68            span_close_events: self.span_close_events,
69        }
70    }
71
72    /// Set the time source to use for time measurements.
73    pub fn time(self, time: quanta::Clock) -> Builder<S, E> {
74        Builder { time, ..self }
75    }
76
77    /// By default, a [`TimingSubscriber`] will record the time since the last event in *any* child
78    /// span:
79    ///
80    /// ```text
81    /// | span foo
82    /// | - event a
83    /// | | span bar
84    /// | | - event b
85    /// | - event c
86    /// ```
87    ///
88    /// What time is recorded for event c? The default is `t_c - t_b`.
89    /// With `no_span_recursion`, event c will have `t_c - t_a`. event b will record the time since
90    /// the start of span bar.
91    pub fn no_span_recursion(self) -> Self {
92        Builder {
93            bubble_spans: false,
94            ..self
95        }
96    }
97
98    /// By default, a [`TimingSubscriber`] or [`TimingLayer`] won't record a [span closure] as an
99    /// event.
100    ///
101    /// ```text
102    /// | span foo
103    /// | - event a
104    /// | | span bar
105    /// | | - (bar closed)
106    /// | - event c
107    /// ```
108    ///
109    /// Without span close events, event c will record `t_c - t_a`. With `span_close_events`, event
110    /// c will record `t_c - t_bar_close`.
111    ///
112    /// [span closure]: https://docs.rs/tracing/0.1.25/tracing/span/index.html#closing-spans
113    pub fn span_close_events(self) -> Self {
114        Builder {
115            span_close_events: true,
116            ..self
117        }
118    }
119
120    /// Construct a [`TimingSubscriber`] that uses the given function to construct new histograms.
121    ///
122    /// This is equivalent to [`build`], except that the passed function is also told which
123    /// span/event group the histogram is for.
124    ///
125    /// Note that you _may_ run into weird lifetime errors from the compiler when using this method
126    /// with a closure. This is a [known compiler issue]. You can work around it by adding a slight
127    /// type hint to the arguments passed to the closure as follows (note the `: &_`):
128    ///
129    /// ```rust
130    /// use tracing_timing::{Builder, Histogram};
131    /// let builder = Builder::default();
132    /// let subscriber = builder.build_informed(|s: &_, e: &_| Histogram::new(3).unwrap());
133    /// ```
134    ///
135    ///   [known compiler issue]: https://github.com/rust-lang/rust/issues/41078
136    pub fn build_informed<F>(self, new_histogram: F) -> TimingSubscriber<S, E>
137    where
138        S: SpanGroup,
139        E: EventGroup,
140        S::Id: Hash + Eq,
141        E::Id: Hash + Eq,
142        F: FnMut(&S::Id, &E::Id) -> Histogram<u64> + Send + Sync + 'static,
143    {
144        let (tx, rx) = crossbeam::channel::unbounded();
145        TimingSubscriber::new(Timing {
146            span_group: self.span_group,
147            event_group: self.event_group,
148            time: self.time,
149            bubble_spans: self.bubble_spans,
150            span_close_events: self.span_close_events,
151            reader: super::ReaderState {
152                created: rx,
153                histograms: Default::default(),
154            }
155            .into(),
156            writers: super::WriterState {
157                tls: Default::default(),
158                idle_recorders: Default::default(),
159                new_histogram: Box::new(new_histogram),
160                created: tx,
161            }
162            .into(),
163        })
164    }
165
166    /// Construct a [`TimingSubscriber`] that uses the given function to construct new histograms.
167    pub fn build<F>(self, mut new_histogram: F) -> TimingSubscriber<S, E>
168    where
169        S: SpanGroup,
170        E: EventGroup,
171        S::Id: Hash + Eq,
172        E::Id: Hash + Eq,
173        F: FnMut() -> Histogram<u64> + Send + Sync + 'static,
174    {
175        self.build_informed(move |_: &_, _: &_| (new_histogram)())
176    }
177
178    /// Construct a [`TimingLayer`] that uses the given function to construct new histograms.
179    ///
180    /// This is equivalent to [`layer`], except that the passed function is also told which
181    /// span/event group the histogram is for.
182    ///
183    /// Note that you _may_ run into weird lifetime errors from the compiler when using this method
184    /// with a closure. This is a [known compiler issue]. You can work around it by adding a slight
185    /// type hint to the arguments passed to the closure as follows (note the `: &_`):
186    ///
187    /// ```rust
188    /// use tracing_timing::{Builder, Histogram};
189    /// let builder = Builder::default();
190    /// let layer = builder.layer_informed(|s: &_, e: &_| Histogram::new(3).unwrap());
191    /// ```
192    ///
193    ///   [known compiler issue]: https://github.com/rust-lang/rust/issues/41078
194    #[cfg(feature = "layer")]
195    pub fn layer_informed<F>(self, new_histogram: F) -> TimingLayer<S, E>
196    where
197        S: SpanGroup,
198        E: EventGroup,
199        S::Id: Hash + Eq,
200        E::Id: Hash + Eq,
201        F: FnMut(&S::Id, &E::Id) -> Histogram<u64> + Send + Sync + 'static,
202    {
203        let (tx, rx) = crossbeam::channel::unbounded();
204        TimingLayer::new(Timing {
205            span_group: self.span_group,
206            event_group: self.event_group,
207            time: self.time,
208            bubble_spans: self.bubble_spans,
209            span_close_events: self.span_close_events,
210            reader: super::ReaderState {
211                created: rx,
212                histograms: Default::default(),
213            }
214            .into(),
215            writers: super::WriterState {
216                tls: Default::default(),
217                idle_recorders: Default::default(),
218                new_histogram: Box::new(new_histogram),
219                created: tx,
220            }
221            .into(),
222        })
223    }
224
225    /// Construct a [`TimingSubscriber`] that uses the given function to construct new histograms.
226    #[cfg(feature = "layer")]
227    pub fn layer<F>(self, mut new_histogram: F) -> TimingLayer<S, E>
228    where
229        S: SpanGroup,
230        E: EventGroup,
231        S::Id: Hash + Eq,
232        E::Id: Hash + Eq,
233        F: FnMut() -> Histogram<u64> + Send + Sync + 'static,
234    {
235        self.layer_informed(move |_: &_, _: &_| (new_histogram)())
236    }
237}