rustyclaw_core/observability/
mod.rs1pub mod log;
10pub mod traits;
11
12pub use log::LogObserver;
13pub use traits::{Observer, ObserverEvent, ObserverMetric};
14
15use std::sync::Arc;
16
17pub struct CompositeObserver {
22 observers: Vec<Arc<dyn Observer>>,
23}
24
25impl CompositeObserver {
26 pub fn new(observers: Vec<Arc<dyn Observer>>) -> Self {
28 Self { observers }
29 }
30
31 pub fn add(&mut self, observer: Arc<dyn Observer>) {
33 self.observers.push(observer);
34 }
35}
36
37impl Observer for CompositeObserver {
38 fn record_event(&self, event: &ObserverEvent) {
39 for observer in &self.observers {
40 observer.record_event(event);
41 }
42 }
43
44 fn record_metric(&self, metric: &ObserverMetric) {
45 for observer in &self.observers {
46 observer.record_metric(metric);
47 }
48 }
49
50 fn flush(&self) {
51 for observer in &self.observers {
52 observer.flush();
53 }
54 }
55
56 fn name(&self) -> &str {
57 "composite"
58 }
59
60 fn as_any(&self) -> &dyn std::any::Any {
61 self
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use std::sync::Mutex;
69 use std::time::Duration;
70
71 #[derive(Default)]
72 struct CountingObserver {
73 events: Mutex<u64>,
74 metrics: Mutex<u64>,
75 flushes: Mutex<u64>,
76 }
77
78 impl Observer for CountingObserver {
79 fn record_event(&self, _event: &ObserverEvent) {
80 *self.events.lock().unwrap() += 1;
81 }
82
83 fn record_metric(&self, _metric: &ObserverMetric) {
84 *self.metrics.lock().unwrap() += 1;
85 }
86
87 fn flush(&self) {
88 *self.flushes.lock().unwrap() += 1;
89 }
90
91 fn name(&self) -> &str {
92 "counting"
93 }
94
95 fn as_any(&self) -> &dyn std::any::Any {
96 self
97 }
98 }
99
100 #[test]
101 fn composite_dispatches_to_all_backends() {
102 let obs1 = Arc::new(CountingObserver::default());
103 let obs2 = Arc::new(CountingObserver::default());
104
105 let composite = CompositeObserver::new(vec![obs1.clone(), obs2.clone()]);
106
107 composite.record_event(&ObserverEvent::HeartbeatTick);
108 composite.record_metric(&ObserverMetric::TokensUsed(100));
109 composite.flush();
110
111 assert_eq!(*obs1.events.lock().unwrap(), 1);
112 assert_eq!(*obs2.events.lock().unwrap(), 1);
113 assert_eq!(*obs1.metrics.lock().unwrap(), 1);
114 assert_eq!(*obs2.metrics.lock().unwrap(), 1);
115 assert_eq!(*obs1.flushes.lock().unwrap(), 1);
116 assert_eq!(*obs2.flushes.lock().unwrap(), 1);
117 }
118
119 #[test]
120 fn composite_name() {
121 let composite = CompositeObserver::new(vec![]);
122 assert_eq!(composite.name(), "composite");
123 }
124}