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}