swarm_engine_core/online_stats/
dumpable.rs1use std::sync::{Arc, RwLock};
6
7use crate::debug::Dumpable;
8
9use super::swarm::SwarmStats;
10
11pub struct DumpableStats {
15 stats: Arc<RwLock<SwarmStats>>,
16}
17
18impl DumpableStats {
19 pub fn new(stats: Arc<RwLock<SwarmStats>>) -> Self {
20 Self { stats }
21 }
22}
23
24impl Dumpable for DumpableStats {
25 fn name(&self) -> &'static str {
26 "swarm_stats"
27 }
28
29 fn snapshot(&self, _tick: u64) -> Option<serde_json::Value> {
30 let stats = self.stats.read().ok()?;
31 let global = stats.global();
32
33 let action_stats: serde_json::Map<String, serde_json::Value> = stats
34 .all_action_stats()
35 .map(|(action, s)| {
36 (
37 action.clone(),
38 serde_json::json!({
39 "visits": s.visits,
40 "successes": s.successes,
41 "failures": s.failures,
42 "success_rate": s.success_rate(),
43 "avg_duration_ms": s.avg_duration().as_millis(),
44 }),
45 )
46 })
47 .collect();
48
49 Some(serde_json::json!({
50 "global": {
51 "total_visits": global.total_visits,
52 "total_successes": global.total_successes,
53 "total_failures": global.total_failures,
54 "success_rate": global.success_rate(),
55 },
56 "actions": action_stats,
57 }))
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use std::time::Duration;
64
65 use super::*;
66 use crate::events::{ActionEventBuilder, ActionEventResult};
67 use crate::types::WorkerId;
68
69 fn make_event(tick: u64, action: &str, success: bool) -> crate::events::ActionEvent {
70 let result = if success {
71 ActionEventResult::success()
72 } else {
73 ActionEventResult::failure("error")
74 };
75
76 ActionEventBuilder::new(tick, WorkerId(0), action)
77 .result(result)
78 .duration(Duration::from_millis(50))
79 .build()
80 }
81
82 #[test]
83 fn test_dumpable_stats() {
84 let stats = Arc::new(RwLock::new(SwarmStats::new()));
85
86 {
87 let mut s = stats.write().unwrap();
88 s.record(&make_event(1, "CheckStatus", true));
89 s.record(&make_event(2, "ReadLogs", true));
90 s.record(&make_event(3, "CheckStatus", false));
91 }
92
93 let dumpable = DumpableStats::new(Arc::clone(&stats));
94 let snapshot = dumpable.snapshot(0).unwrap();
95
96 assert_eq!(snapshot["global"]["total_visits"], 3);
97 assert_eq!(snapshot["global"]["total_successes"], 2);
98 assert!(snapshot["actions"]["CheckStatus"].is_object());
99 }
100}