codetether_agent/telemetry/tools/counter.rs
1//! Lock-free success/failure counter for tool invocations.
2
3use std::sync::atomic::{AtomicU64, Ordering};
4
5/// Cumulative `(total, failures)` counter shared across the whole process.
6///
7/// Prefer the [`super::super::TOOL_EXECUTIONS`] singleton — constructing your
8/// own instance is only useful in tests.
9///
10/// # Examples
11///
12/// ```rust
13/// use codetether_agent::telemetry::AtomicToolCounter;
14///
15/// let c = AtomicToolCounter::new();
16/// c.record(true);
17/// c.record(false);
18/// c.record(true);
19///
20/// let (total, failures) = c.get();
21/// assert_eq!((total, failures), (3, 1));
22/// ```
23#[derive(Debug)]
24pub struct AtomicToolCounter {
25 count: AtomicU64,
26 failures: AtomicU64,
27}
28
29impl AtomicToolCounter {
30 /// Construct a zeroed counter.
31 pub fn new() -> Self {
32 Self {
33 count: AtomicU64::new(0),
34 failures: AtomicU64::new(0),
35 }
36 }
37
38 /// Record a single invocation. Increments `failures` iff `success` is false.
39 pub fn record(&self, success: bool) {
40 self.count.fetch_add(1, Ordering::Relaxed);
41 if !success {
42 self.failures.fetch_add(1, Ordering::Relaxed);
43 }
44 }
45
46 /// Load `(total_invocations, total_failures)` with `Relaxed` ordering.
47 pub fn get(&self) -> (u64, u64) {
48 (
49 self.count.load(Ordering::Relaxed),
50 self.failures.load(Ordering::Relaxed),
51 )
52 }
53}
54
55impl Default for AtomicToolCounter {
56 fn default() -> Self {
57 Self::new()
58 }
59}