Skip to main content

zeroclaw_runtime/observability/
noop.rs

1use super::traits::{Observer, ObserverEvent, ObserverMetric};
2use std::any::Any;
3
4/// Zero-overhead observer — all methods compile to nothing
5pub struct NoopObserver;
6
7impl Observer for NoopObserver {
8    #[inline(always)]
9    fn record_event(&self, _event: &ObserverEvent) {}
10
11    #[inline(always)]
12    fn record_metric(&self, _metric: &ObserverMetric) {}
13
14    fn name(&self) -> &str {
15        "noop"
16    }
17
18    fn as_any(&self) -> &dyn Any {
19        self
20    }
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26    use std::time::Duration;
27
28    #[test]
29    fn noop_name() {
30        assert_eq!(NoopObserver.name(), "noop");
31    }
32
33    #[test]
34    fn noop_record_event_does_not_panic() {
35        let obs = NoopObserver;
36        obs.record_event(&ObserverEvent::HeartbeatTick);
37        obs.record_event(&ObserverEvent::AgentStart {
38            model_provider: "test".into(),
39            model: "test".into(),
40            channel: None,
41            agent_alias: None,
42            turn_id: None,
43        });
44        obs.record_event(&ObserverEvent::AgentEnd {
45            model_provider: "test".into(),
46            model: "test".into(),
47            duration: Duration::from_millis(100),
48            tokens_used: None,
49            cost_usd: Some(0.001),
50            channel: None,
51            agent_alias: None,
52            turn_id: None,
53        });
54        obs.record_event(&ObserverEvent::AgentEnd {
55            model_provider: "test".into(),
56            model: "test".into(),
57            duration: Duration::ZERO,
58            tokens_used: None,
59            cost_usd: None,
60            channel: None,
61            agent_alias: None,
62            turn_id: None,
63        });
64        obs.record_event(&ObserverEvent::ToolCall {
65            tool: "shell".into(),
66            tool_call_id: None,
67            duration: Duration::from_secs(1),
68            success: true,
69            arguments: None,
70            result: None,
71            channel: None,
72            agent_alias: None,
73            turn_id: None,
74        });
75        obs.record_event(&ObserverEvent::ChannelMessage {
76            channel: "cli".into(),
77            direction: "inbound".into(),
78        });
79        obs.record_event(&ObserverEvent::Error {
80            component: "test".into(),
81            message: "boom".into(),
82        });
83    }
84
85    #[test]
86    fn noop_record_metric_does_not_panic() {
87        let obs = NoopObserver;
88        obs.record_metric(&ObserverMetric::RequestLatency(Duration::from_millis(50)));
89        obs.record_metric(&ObserverMetric::TokensUsed(1000));
90        obs.record_metric(&ObserverMetric::ActiveSessions(5));
91        obs.record_metric(&ObserverMetric::QueueDepth(0));
92    }
93
94    #[test]
95    fn noop_flush_does_not_panic() {
96        NoopObserver.flush();
97    }
98}