use std::sync::{Arc, RwLock};
use crate::debug::Dumpable;
use super::swarm::SwarmStats;
pub struct DumpableStats {
stats: Arc<RwLock<SwarmStats>>,
}
impl DumpableStats {
pub fn new(stats: Arc<RwLock<SwarmStats>>) -> Self {
Self { stats }
}
}
impl Dumpable for DumpableStats {
fn name(&self) -> &'static str {
"swarm_stats"
}
fn snapshot(&self, _tick: u64) -> Option<serde_json::Value> {
let stats = self.stats.read().ok()?;
let global = stats.global();
let action_stats: serde_json::Map<String, serde_json::Value> = stats
.all_action_stats()
.map(|(action, s)| {
(
action.clone(),
serde_json::json!({
"visits": s.visits,
"successes": s.successes,
"failures": s.failures,
"success_rate": s.success_rate(),
"avg_duration_ms": s.avg_duration().as_millis(),
}),
)
})
.collect();
Some(serde_json::json!({
"global": {
"total_visits": global.total_visits,
"total_successes": global.total_successes,
"total_failures": global.total_failures,
"success_rate": global.success_rate(),
},
"actions": action_stats,
}))
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
use crate::events::{ActionEventBuilder, ActionEventResult};
use crate::types::WorkerId;
fn make_event(tick: u64, action: &str, success: bool) -> crate::events::ActionEvent {
let result = if success {
ActionEventResult::success()
} else {
ActionEventResult::failure("error")
};
ActionEventBuilder::new(tick, WorkerId(0), action)
.result(result)
.duration(Duration::from_millis(50))
.build()
}
#[test]
fn test_dumpable_stats() {
let stats = Arc::new(RwLock::new(SwarmStats::new()));
{
let mut s = stats.write().unwrap();
s.record(&make_event(1, "CheckStatus", true));
s.record(&make_event(2, "ReadLogs", true));
s.record(&make_event(3, "CheckStatus", false));
}
let dumpable = DumpableStats::new(Arc::clone(&stats));
let snapshot = dumpable.snapshot(0).unwrap();
assert_eq!(snapshot["global"]["total_visits"], 3);
assert_eq!(snapshot["global"]["total_successes"], 2);
assert!(snapshot["actions"]["CheckStatus"].is_object());
}
}