latency_trace/lt_report_g.rs
1//! [`LatencyTraceG`] activation and measurment methods, other supporting types and/or impls.
2
3use hdrhistogram::CreationError;
4use std::{
5 error::Error,
6 fmt::{Debug, Display},
7 sync::Arc,
8};
9use tracing::Dispatch;
10use tracing_subscriber::{
11 layer::{Layered, SubscriberExt},
12 util::{SubscriberInitExt, TryInitError},
13 Registry,
14};
15
16use crate::{
17 default_span_grouper,
18 lt_collect_g::{LatencyTraceCfg, LatencyTraceG, Timing},
19 lt_refine_g::Timings,
20 tlc_param::{TlcBase, TlcDirect, TlcParam},
21};
22
23//==============
24// Errors
25
26/// Error returned by [`LatencyTrace`](crate::LatencyTrace) activation methods.
27#[derive(Debug)]
28pub enum ActivationError {
29 HistogramConfigError,
30 TracingSubscriberInitError,
31}
32
33impl Display for ActivationError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 Debug::fmt(self, f)
36 }
37}
38
39impl Error for ActivationError {}
40
41impl From<CreationError> for ActivationError {
42 fn from(_: CreationError) -> Self {
43 Self::HistogramConfigError
44 }
45}
46
47impl From<TryInitError> for ActivationError {
48 fn from(_: TryInitError) -> Self {
49 Self::TracingSubscriberInitError
50 }
51}
52
53//==============
54// impl for LatencyTraceCfg
55
56impl LatencyTraceCfg {
57 /// Validates that the configuration settings yield histograms that avoid all potential [hdrhistogram::Histogram] errors
58 /// as our histograms are `u64`, have a `hist_low` of `1`, and are auto-resizable.
59 fn validate_hist_high_sigfig(&self) -> Result<(), CreationError> {
60 let _ = Timing::new_with_bounds(1, self.hist_high, self.hist_sigfig)?;
61 Ok(())
62 }
63}
64
65impl Default for LatencyTraceCfg {
66 /// Instantiates a default [LatencyTraceCfg]. The defaults are:
67 /// - Grouping of spans using the [`default_span_grouper`], which simply groups by the span's
68 /// callsite information, which distills the *tracing* framework's Callsite concept
69 /// (see [Metadata and Callsite](https://docs.rs/tracing-core/0.1.31/tracing_core/)). This default can be
70 /// modified by using the [`Self::with_span_grouper`] method.
71 /// - `hist_high` of `20,000,000` (20 seconds). This default can be modified by using the [`Self::with_hist_high`] method.
72 /// - `hist_sigfig` of 2. This default can be modified by using the [`Self::with_hist_sigfig`] method.
73 ///
74 /// See [hdrhistogram::Histogram::high] and [hdrhistogram::Histogram::sigfig] for an explanation of these histogram configuration parameters.
75 ///
76 /// Note that the histograms used here are auto-resizable, which means [`hdrhistogram::Histogram::high`] is
77 /// automatically adjusted as needed (although resizing requires memory reallocation at runtime).
78 fn default() -> Self {
79 LatencyTraceCfg {
80 span_grouper: Arc::new(default_span_grouper),
81 hist_high: 20 * 1000 * 1000,
82 hist_sigfig: 2,
83 }
84 }
85}
86
87//==============
88// impl for LatencyTrace
89
90impl<P> LatencyTraceG<P>
91where
92 P: TlcParam + Clone + 'static,
93 P::Control: TlcBase + Clone,
94 Layered<LatencyTraceG<P>, Registry>: Into<Dispatch>,
95{
96 /// Returns the active instance of `Self` if it exists.
97 pub fn active() -> Option<Self> {
98 tracing::dispatcher::get_default(|disp| {
99 let lt: &Self = disp.downcast_ref()?;
100 Some(lt.clone())
101 })
102 }
103
104 /// Returns the active instance of `Self` if it exists or activates a new instance with the given `config` otherwise.
105 /// Activation entails setting the global default [`tracing::Subscriber`], of which there can be only one and it can't
106 /// be changed once it is set.
107 ///
108 /// If a [`LatencyTrace`] has been previously activated in the same process, the `config` passed to this
109 /// function will be ignored and the current active [`LatencyTrace`] will be returned.
110 ///
111 /// # Errors
112 /// - [`ActivationError::HistogramConfigError`] if the `config`'s `hist_high` and `hist_sigfig` would cause
113 /// [`hdrhistogram::Histogram::new_with_bounds`]`(1, hist_high, hist_sigfig)` to fail.
114 /// - [`ActivationError::TracingSubscriberInitError`] if a global [`tracing::Subscriber`] is already set and its
115 /// type is not the same as `Self`.
116 pub fn activated(config: LatencyTraceCfg) -> Result<Self, ActivationError> {
117 config.validate_hist_high_sigfig()?;
118 let default_dispatch_exists =
119 tracing::dispatcher::get_default(|disp| disp.is::<Layered<Self, Registry>>());
120 let lt = if !default_dispatch_exists {
121 let lt_new = LatencyTraceG::new(config);
122 let reg: Layered<Self, Registry> = Registry::default().with(lt_new.clone());
123 reg.try_init()?;
124 lt_new
125 } else {
126 Self::active().expect("`if` condition should ensure `else` Ok")
127 };
128 Ok(lt)
129 }
130
131 /// Returns the active instance of `Self` if it exists or activates a new instance with the default configuration otherwise.
132 /// Activation entails setting the global default [`tracing::Subscriber`], of which there can be only one and it can't
133 /// be changed once it is set.
134 ///
135 /// If a [`LatencyTrace`] has been previously activated in the same process, the default configuration
136 /// will be ignored and the current active [`LatencyTrace`] will be returned.
137 ///
138 /// # Errors
139 /// - [`ActivationError::TracingSubscriberInitError`] if a global [`tracing::Subscriber`] is already set and its
140 /// type is not the same as `Self`.
141 pub fn activated_default() -> Result<Self, ActivationError> {
142 Self::activated(LatencyTraceCfg::default())
143 }
144}
145
146impl<P> LatencyTraceG<P>
147where
148 P: TlcParam,
149 P::Control: TlcDirect,
150{
151 /// Executes the instrumented function `f` and, after `f` completes, returns the observed latencies.
152 pub fn measure_latencies(&self, f: impl FnOnce()) -> Timings {
153 f();
154 let acc = self.take_acc_timings();
155 self.report_timings(acc)
156 }
157}