1use std::{
16 future::Future,
17 ops::Deref,
18 pin::Pin,
19 sync::atomic::{AtomicU64, Ordering},
20 task::{Context, Poll},
21 time::Duration,
22};
23
24use fastrace::prelude::*;
25use pin_project::pin_project;
26use serde::{Deserialize, Serialize};
27
28#[derive(Debug, Default)]
30pub struct TracingConfig {
31 record_hybrid_insert_threshold_us: AtomicU64,
33 record_hybrid_get_threshold_us: AtomicU64,
35 record_hybrid_obtain_threshold_us: AtomicU64,
37 record_hybrid_remove_threshold_us: AtomicU64,
39 record_hybrid_fetch_threshold_us: AtomicU64,
41}
42
43impl TracingConfig {
44 pub fn update(&self, options: TracingOptions) {
46 if let Some(threshold) = options.record_hybrid_insert_threshold {
47 self.record_hybrid_insert_threshold_us
48 .store(threshold.as_micros() as _, Ordering::Relaxed);
49 }
50
51 if let Some(threshold) = options.record_hybrid_get_threshold {
52 self.record_hybrid_get_threshold_us
53 .store(threshold.as_micros() as _, Ordering::Relaxed);
54 }
55
56 if let Some(threshold) = options.record_hybrid_obtain_threshold {
57 self.record_hybrid_obtain_threshold_us
58 .store(threshold.as_micros() as _, Ordering::Relaxed);
59 }
60
61 if let Some(threshold) = options.record_hybrid_remove_threshold {
62 self.record_hybrid_remove_threshold_us
63 .store(threshold.as_micros() as _, Ordering::Relaxed);
64 }
65
66 if let Some(threshold) = options.record_hybrid_fetch_threshold {
67 self.record_hybrid_fetch_threshold_us
68 .store(threshold.as_micros() as _, Ordering::Relaxed);
69 }
70 }
71
72 pub fn record_hybrid_insert_threshold(&self) -> Duration {
74 Duration::from_micros(self.record_hybrid_insert_threshold_us.load(Ordering::Relaxed))
75 }
76
77 pub fn record_hybrid_get_threshold(&self) -> Duration {
79 Duration::from_micros(self.record_hybrid_get_threshold_us.load(Ordering::Relaxed))
80 }
81
82 pub fn record_hybrid_obtain_threshold(&self) -> Duration {
84 Duration::from_micros(self.record_hybrid_obtain_threshold_us.load(Ordering::Relaxed))
85 }
86
87 pub fn record_hybrid_remove_threshold(&self) -> Duration {
89 Duration::from_micros(self.record_hybrid_remove_threshold_us.load(Ordering::Relaxed))
90 }
91
92 pub fn record_hybrid_fetch_threshold(&self) -> Duration {
94 Duration::from_micros(self.record_hybrid_fetch_threshold_us.load(Ordering::Relaxed))
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct TracingOptions {
101 record_hybrid_insert_threshold: Option<Duration>,
103 record_hybrid_get_threshold: Option<Duration>,
105 record_hybrid_obtain_threshold: Option<Duration>,
107 record_hybrid_remove_threshold: Option<Duration>,
109 record_hybrid_fetch_threshold: Option<Duration>,
111}
112
113impl Default for TracingOptions {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119impl TracingOptions {
120 pub fn new() -> Self {
122 Self {
123 record_hybrid_insert_threshold: None,
124 record_hybrid_get_threshold: None,
125 record_hybrid_obtain_threshold: None,
126 record_hybrid_remove_threshold: None,
127 record_hybrid_fetch_threshold: None,
128 }
129 }
130
131 pub fn with_record_hybrid_insert_threshold(mut self, threshold: Duration) -> Self {
133 self.record_hybrid_insert_threshold = Some(threshold);
134 self
135 }
136
137 pub fn with_record_hybrid_get_threshold(mut self, threshold: Duration) -> Self {
139 self.record_hybrid_get_threshold = Some(threshold);
140 self
141 }
142
143 pub fn with_record_hybrid_obtain_threshold(mut self, threshold: Duration) -> Self {
145 self.record_hybrid_obtain_threshold = Some(threshold);
146 self
147 }
148
149 pub fn with_record_hybrid_remove_threshold(mut self, threshold: Duration) -> Self {
151 self.record_hybrid_remove_threshold = Some(threshold);
152 self
153 }
154
155 pub fn with_record_hybrid_fetch_threshold(mut self, threshold: Duration) -> Self {
157 self.record_hybrid_fetch_threshold = Some(threshold);
158 self
159 }
160}
161
162#[pin_project]
164pub struct InRootSpan<F> {
165 #[pin]
166 inner: F,
167
168 root: Option<Span>,
169 threshold: Option<Duration>,
170}
171
172impl<F> InRootSpan<F> {
173 pub fn new(inner: F, root: Span) -> Self
175 where
176 F: Future,
177 {
178 Self {
179 inner,
180 root: Some(root),
181 threshold: None,
182 }
183 }
184
185 pub fn with_threshold(mut self, threshold: Duration) -> Self {
189 self.threshold = Some(threshold);
190 self
191 }
192}
193
194impl<F> Future for InRootSpan<F>
195where
196 F: Future,
197{
198 type Output = F::Output;
199
200 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
201 let this = self.project();
202
203 let _guard = this.root.as_ref().map(|s| s.set_local_parent());
204 let res = match this.inner.poll(cx) {
205 Poll::Ready(res) => res,
206 Poll::Pending => return Poll::Pending,
207 };
208
209 let root = this.root.take().unwrap();
210
211 if let (Some(elapsed), Some(threshold)) = (root.elapsed(), this.threshold.as_ref()) {
212 if &elapsed < threshold {
213 root.cancel();
214 }
215 }
216
217 Poll::Ready(res)
218 }
219}
220
221impl<F> Deref for InRootSpan<F> {
222 type Target = F;
223
224 fn deref(&self) -> &Self::Target {
225 &self.inner
226 }
227}