1use std::collections::HashMap;
28use std::fmt;
29use std::sync::Arc;
30use std::sync::atomic::{AtomicU64, Ordering};
31use std::time::{Duration, Instant};
32
33use parking_lot::Mutex;
34use tracing::field::{Field, Visit};
35use tracing::span::{Attributes, Id, Record};
36use tracing::{Level, Subscriber};
37use tracing_subscriber::layer::Context;
38use tracing_subscriber::registry::LookupSpan;
39
40pub const ROOT_FIELD: &str = "fibre.root";
42
43pub const OUTCOME_FIELD: &str = "fibre.outcome";
45
46pub const DEFAULT_RING: usize = 256;
48
49#[derive(Debug, Clone, PartialEq)]
51pub enum Value {
52 Str(String),
53 Int(i64),
54 UInt(u64),
55 Float(f64),
56 Bool(bool),
57}
58
59impl fmt::Display for Value {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Value::Str(v) => f.write_str(v),
63 Value::Int(v) => write!(f, "{v}"),
64 Value::UInt(v) => write!(f, "{v}"),
65 Value::Float(v) => write!(f, "{v}"),
66 Value::Bool(v) => write!(f, "{v}"),
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73pub struct Span {
74 pub name: &'static str,
75 pub target: String,
76 pub level: Level,
77 pub parent: Option<usize>,
79 pub depth: usize,
81 pub at: Duration,
83 pub duration: Duration,
85 pub fields: HashMap<String, Value>,
86}
87
88impl Span {
89 pub fn outcome(&self) -> Option<&str> {
91 match self.fields.get(OUTCOME_FIELD) {
92 Some(Value::Str(s)) => Some(s.as_str()),
93 _ => None,
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
101pub struct Trace {
102 pub id: u64,
103 pub spans: Vec<Span>,
104 pub duration: Duration,
106}
107
108impl Trace {
109 pub fn root(&self) -> Option<&Span> {
110 self.spans.first()
111 }
112
113 pub fn named<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Span> + 'a {
116 self.spans.iter().filter(move |s| s.name == name)
117 }
118}
119
120struct Open {
122 id: u64,
123 started: Instant,
124 spans: Vec<Span>,
125 index: HashMap<u64, usize>,
127 ids: Vec<u64>,
130 root: u64,
132}
133
134#[derive(Clone)]
136pub struct Traces {
137 inner: Arc<Inner>,
138}
139
140struct Inner {
141 next_id: AtomicU64,
142 open: Mutex<HashMap<u64, Open>>,
144 belongs: Mutex<HashMap<u64, u64>>,
146 finished: Mutex<Vec<Trace>>,
147 ring: usize,
148 listeners: Mutex<Vec<Arc<dyn Fn(&Trace) + Send + Sync>>>,
149}
150
151impl Traces {
152 pub fn current(&self) -> Option<Trace> {
155 let id = tracing::Span::current().id()?;
156 self.of_span(id.into_u64())
157 }
158
159 pub fn of_span(&self, span: u64) -> Option<Trace> {
161 let root = *self.inner.belongs.lock().get(&span)?;
162 let open = self.inner.open.lock();
163 let held = open.get(&root)?;
164 Some(Trace { id: held.id, spans: held.spans.clone(), duration: Duration::ZERO })
165 }
166
167 pub fn recent(&self, n: usize) -> Vec<Trace> {
169 let held = self.inner.finished.lock();
170 let from = held.len().saturating_sub(n);
171 held[from..].to_vec()
172 }
173
174 pub fn len(&self) -> usize {
176 self.inner.finished.lock().len()
177 }
178
179 pub fn is_empty(&self) -> bool {
180 self.len() == 0
181 }
182
183 pub fn clear(&self) {
185 self.inner.finished.lock().clear();
186 }
187
188 pub fn on_finish(&self, f: impl Fn(&Trace) + Send + Sync + 'static) {
193 self.inner.listeners.lock().push(Arc::new(f));
194 }
195}
196
197pub fn layer() -> (TraceLayer, Traces) {
200 with_ring(DEFAULT_RING)
201}
202
203pub fn with_ring(ring: usize) -> (TraceLayer, Traces) {
206 let traces = Traces {
207 inner: Arc::new(Inner {
208 next_id: AtomicU64::new(1),
209 open: Mutex::new(HashMap::new()),
210 belongs: Mutex::new(HashMap::new()),
211 finished: Mutex::new(Vec::new()),
212 ring,
213 listeners: Mutex::new(Vec::new()),
214 }),
215 };
216 (TraceLayer { traces: traces.clone() }, traces)
217}
218
219pub struct TraceLayer {
220 traces: Traces,
221}
222
223impl<S> tracing_subscriber::Layer<S> for TraceLayer
224where
225 S: Subscriber + for<'a> LookupSpan<'a>,
226{
227 fn max_level_hint(&self) -> Option<tracing::level_filters::LevelFilter> {
230 Some(tracing::level_filters::LevelFilter::TRACE)
231 }
232
233 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
234 let mut fields = Fields::default();
235 attrs.record(&mut fields);
236 let is_root = matches!(fields.map.get(ROOT_FIELD), Some(Value::Bool(true)));
237
238 let this = id.into_u64();
239 let parent_span = ctx.span(id).and_then(|s| s.parent().map(|p| p.id().into_u64()));
240 let root = match is_root {
241 true => this,
242 false => match parent_span.and_then(|p| self.traces.inner.belongs.lock().get(&p).copied()) {
243 Some(root) => root,
244 None => return,
246 },
247 };
248
249 let mut open = self.traces.inner.open.lock();
250 if is_root {
251 let trace_id = self.traces.inner.next_id.fetch_add(1, Ordering::Relaxed);
252 open.insert(this, Open { id: trace_id, started: Instant::now(), spans: Vec::new(), index: HashMap::new(), ids: Vec::new(), root: this });
253 }
254 let Some(held) = open.get_mut(&root) else { return };
255 let parent = parent_span.and_then(|p| held.index.get(&p).copied());
256 let depth = parent.map(|i| held.spans[i].depth + 1).unwrap_or(0);
257 let metadata = attrs.metadata();
258 held.index.insert(this, held.spans.len());
259 held.ids.push(this);
260 held.spans.push(Span {
261 name: metadata.name(),
262 target: metadata.target().to_owned(),
263 level: *metadata.level(),
264 parent,
265 depth,
266 at: held.started.elapsed(),
267 duration: Duration::ZERO,
268 fields: fields.map,
269 });
270 drop(open);
271 self.traces.inner.belongs.lock().insert(this, root);
272 }
273
274 fn on_record(&self, id: &Id, values: &Record<'_>, _ctx: Context<'_, S>) {
275 let this = id.into_u64();
276 let Some(root) = self.traces.inner.belongs.lock().get(&this).copied() else { return };
277 let mut fields = Fields::default();
278 values.record(&mut fields);
279 let mut open = self.traces.inner.open.lock();
280 let Some(held) = open.get_mut(&root) else { return };
281 let Some(at) = held.index.get(&this).copied() else { return };
282 held.spans[at].fields.extend(fields.map);
283 }
284
285 fn on_close(&self, id: Id, _ctx: Context<'_, S>) {
286 let this = id.into_u64();
287 let Some(root) = self.traces.inner.belongs.lock().remove(&this) else { return };
288 let mut open = self.traces.inner.open.lock();
289 let Some(held) = open.get_mut(&root) else { return };
290 if let Some(at) = held.index.get(&this).copied() {
291 held.spans[at].duration = held.started.elapsed().saturating_sub(held.spans[at].at);
292 }
293 if held.root != this {
294 return;
295 }
296 let Some(held) = open.remove(&root) else { return };
297 drop(open);
298 {
299 let mut belongs = self.traces.inner.belongs.lock();
300 for id in &held.ids {
301 belongs.remove(id);
302 }
303 }
304 let trace = Trace { id: held.id, duration: held.started.elapsed(), spans: held.spans };
305 for listener in self.traces.inner.listeners.lock().iter() {
306 listener(&trace);
307 }
308 if self.traces.inner.ring == 0 {
309 return;
310 }
311 let mut finished = self.traces.inner.finished.lock();
312 finished.push(trace);
313 let ring = self.traces.inner.ring;
314 if finished.len() > ring {
315 let over = finished.len() - ring;
316 finished.drain(..over);
317 }
318 }
319}
320
321#[derive(Default)]
322struct Fields {
323 map: HashMap<String, Value>,
324}
325
326impl Visit for Fields {
327 fn record_str(&mut self, field: &Field, value: &str) {
328 self.map.insert(field.name().to_owned(), Value::Str(value.to_owned()));
329 }
330 fn record_i64(&mut self, field: &Field, value: i64) {
331 self.map.insert(field.name().to_owned(), Value::Int(value));
332 }
333 fn record_u64(&mut self, field: &Field, value: u64) {
334 self.map.insert(field.name().to_owned(), Value::UInt(value));
335 }
336 fn record_bool(&mut self, field: &Field, value: bool) {
337 self.map.insert(field.name().to_owned(), Value::Bool(value));
338 }
339 fn record_f64(&mut self, field: &Field, value: f64) {
340 self.map.insert(field.name().to_owned(), Value::Float(value));
341 }
342 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
343 self.map.insert(field.name().to_owned(), Value::Str(format!("{value:?}")));
344 }
345}