jigs_trace/lib.rs
1#![warn(missing_docs)]
2//! Per-jig execution tracing.
3//!
4//! Wires into `#[jig]` when the `trace` feature is enabled on the `jigs`
5//! umbrella crate (or on `jigs-macros` directly). Each instrumented jig
6//! records its name, depth, wall-clock duration and outcome into a
7//! thread-local buffer that callers can drain with [`take`].
8
9use std::cell::{Cell, RefCell};
10use std::time::Duration;
11
12pub use jigs_core::Status;
13
14/// One recorded jig invocation.
15pub struct Entry {
16 /// Function name of the jig.
17 pub name: &'static str,
18 /// Nesting depth at the time of entry (top-level jigs are depth 0).
19 pub depth: usize,
20 /// Wall-clock time spent inside the jig.
21 pub duration: Duration,
22 /// `true` if the jig produced a successful outcome.
23 pub ok: bool,
24 /// Error message captured from the jig's output, if any.
25 pub error: Option<String>,
26}
27
28thread_local! {
29 static DEPTH: Cell<usize> = const { Cell::new(0) };
30 static BUFFER: RefCell<Vec<Entry>> = const { RefCell::new(Vec::new()) };
31}
32
33/// Record the start of a jig invocation. Returns an index used by [`exit`]
34/// to close the same entry. Called by code generated from `#[jig]`.
35pub fn enter(name: &'static str) -> usize {
36 let depth = DEPTH.with(|d| {
37 let v = d.get();
38 d.set(v + 1);
39 v
40 });
41 BUFFER.with(|b| {
42 let mut buf = b.borrow_mut();
43 let idx = buf.len();
44 buf.push(Entry {
45 name,
46 depth,
47 duration: Duration::ZERO,
48 ok: true,
49 error: None,
50 });
51 idx
52 })
53}
54
55/// Close the entry at `idx` with its measured duration and outcome.
56/// Called by code generated from `#[jig]`.
57pub fn exit(idx: usize, duration: Duration, ok: bool, error: Option<String>) {
58 DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
59 BUFFER.with(|b| {
60 let mut buf = b.borrow_mut();
61 buf[idx].duration = duration;
62 buf[idx].ok = ok;
63 buf[idx].error = error;
64 });
65}
66
67/// Drain the current thread's trace buffer and reset depth tracking.
68/// Call once per request after the pipeline finishes.
69pub fn take() -> Vec<Entry> {
70 DEPTH.with(|d| d.set(0));
71 BUFFER.with(|b| std::mem::take(&mut *b.borrow_mut()))
72}