tuwunel_core/metrics/
mod.rs1pub mod dump;
8
9use std::sync::{
10 Arc, Mutex,
11 atomic::{AtomicU32, AtomicU64},
12};
13#[cfg(tokio_unstable)]
14use std::{iter::repeat, ops::Range, time::Duration};
15
16#[cfg(tokio_unstable)]
17use smallvec::SmallVec;
18use tokio::runtime;
19#[cfg(tokio_unstable)]
20use tokio_metrics::{RuntimeIntervals, RuntimeMonitor};
21use tokio_metrics::{TaskMetrics, TaskMonitor};
22
23#[cfg(tokio_unstable)]
28type Counts = SmallVec<[u64; 20]>;
29
30#[cfg(tokio_unstable)]
35type Bucket = (Range<Duration>, u64);
36
37pub struct Metrics {
43 _runtime: Option<runtime::Handle>,
44
45 runtime_metrics: Option<runtime::RuntimeMetrics>,
46
47 task_monitor: Option<TaskMonitor>,
48
49 task_intervals: Mutex<Option<Box<dyn Iterator<Item = TaskMetrics> + Send>>>,
50
51 #[cfg(tokio_unstable)]
52 _runtime_monitor: Option<RuntimeMonitor>,
53
54 #[cfg(tokio_unstable)]
55 runtime_intervals: Mutex<Option<RuntimeIntervals>>,
56
57 #[cfg(tokio_unstable)]
58 sched_histogram_last: Mutex<Counts>,
59
60 pub requests_count: AtomicU64,
66
67 pub requests_handle_finished: AtomicU64,
72
73 pub requests_handle_active: AtomicU32,
78
79 pub requests_panic: AtomicU32,
84}
85
86impl Metrics {
87 #[must_use]
88 pub fn new(runtime: Option<&runtime::Handle>) -> Arc<Self> {
94 #[cfg(tokio_unstable)]
95 let runtime_monitor = runtime.map(RuntimeMonitor::new);
96
97 #[cfg(tokio_unstable)]
98 let runtime_intervals = runtime_monitor
99 .as_ref()
100 .map(RuntimeMonitor::intervals);
101
102 let task_monitor = cfg!(tokio_unstable).then(|| {
103 TaskMonitor::builder()
104 .with_slow_poll_threshold(TaskMonitor::DEFAULT_SLOW_POLL_THRESHOLD)
105 .with_long_delay_threshold(TaskMonitor::DEFAULT_LONG_DELAY_THRESHOLD)
106 .clone()
107 .build()
108 });
109
110 let task_intervals = task_monitor.as_ref().map(
111 |task_monitor| -> Box<dyn Iterator<Item = TaskMetrics> + Send> {
112 Box::new(task_monitor.intervals())
113 },
114 );
115
116 Arc::new(Self {
117 _runtime: runtime.cloned(),
118
119 runtime_metrics: runtime.map(runtime::Handle::metrics),
120
121 task_monitor,
122
123 task_intervals: task_intervals.into(),
124
125 #[cfg(tokio_unstable)]
126 _runtime_monitor: runtime_monitor,
127
128 #[cfg(tokio_unstable)]
129 runtime_intervals: Mutex::new(runtime_intervals),
130
131 #[cfg(tokio_unstable)]
132 sched_histogram_last: Counts::new().into(),
133
134 requests_count: AtomicU64::new(0),
135 requests_handle_finished: AtomicU64::new(0),
136 requests_handle_active: AtomicU32::new(0),
137 requests_panic: AtomicU32::new(0),
138 })
139 }
140
141 #[inline]
142 pub async fn instrument<F, Output>(&self, f: F) -> Output
147 where
148 F: Future<Output = Output>,
149 {
150 if let Some(monitor) = self.task_metrics() {
151 monitor.instrument(f).await
152 } else {
153 f.await
154 }
155 }
156
157 pub fn task_interval(&self) -> Option<TaskMetrics> {
166 self.task_intervals
167 .lock()
168 .expect("locked")
169 .as_mut()
170 .and_then(Iterator::next)
171 }
172
173 #[cfg(tokio_unstable)]
174 pub fn runtime_interval(&self) -> Option<tokio_metrics::RuntimeMetrics> {
184 self.runtime_intervals
185 .lock()
186 .expect("locked")
187 .as_mut()
188 .map(Iterator::next)
189 .expect("next interval")
190 }
191
192 #[cfg(tokio_unstable)]
203 pub fn sched_histogram_interval(&self) -> Option<impl Iterator<Item = Bucket>> {
204 let metrics = self
205 .runtime_metrics()
206 .filter(|metrics| metrics.schedule_latency_histogram_enabled())?;
207
208 let num_workers = metrics.num_workers();
209 let bucket_total = |bucket| -> u64 {
210 (0..num_workers)
211 .map(|worker| metrics.schedule_latency_histogram_bucket_count(worker, bucket))
212 .sum()
213 };
214
215 let num_buckets = metrics.schedule_latency_histogram_num_buckets();
216 let totals: Counts = (0..num_buckets).map(bucket_total).collect();
217
218 let mut last = self.sched_histogram_last.lock().expect("locked");
219 let deltas: Counts = totals
220 .iter()
221 .zip(last.iter().copied().chain(repeat(0)))
222 .map(|(total, last)| total.saturating_sub(last))
223 .collect();
224
225 *last = totals;
226
227 let ranges = (0..num_buckets)
228 .map(move |bucket| metrics.schedule_latency_histogram_bucket_range(bucket));
229
230 Some(ranges.zip(deltas))
231 }
232
233 #[inline]
234 pub fn num_workers(&self) -> usize {
239 self.runtime_metrics()
240 .map_or(0, runtime::RuntimeMetrics::num_workers)
241 }
242
243 #[inline]
244 pub fn task_metrics(&self) -> Option<&TaskMonitor> { self.task_monitor.as_ref() }
250
251 #[inline]
252 pub fn runtime_metrics(&self) -> Option<&runtime::RuntimeMetrics> {
257 self.runtime_metrics.as_ref()
258 }
259}