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