use chrono::{DateTime, Utc};
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AlertHistoryEventType {
Created,
Pending,
Firing,
Resolved,
Silenced,
Acknowledged,
NotificationSent { channel: String },
NotificationFailed { channel: String, error: String },
LabelsUpdated,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertHistoryEvent {
pub id: String,
pub alert_id: String,
pub event_type: AlertHistoryEventType,
pub timestamp: DateTime<Utc>,
pub details: Option<String>,
}
impl AlertHistoryEvent {
pub fn new(alert_id: impl Into<String>, event_type: AlertHistoryEventType) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
alert_id: alert_id.into(),
event_type,
timestamp: Utc::now(),
details: None,
}
}
#[must_use]
pub fn with_details(mut self, details: impl Into<String>) -> Self {
self.details = Some(details.into());
self
}
}
pub struct AlertHistory {
events: Arc<RwLock<HashMap<String, VecDeque<AlertHistoryEvent>>>>,
max_events_per_alert: usize,
global_log: Arc<RwLock<VecDeque<AlertHistoryEvent>>>,
max_global_log_size: usize,
}
impl AlertHistory {
pub fn new() -> Self {
Self {
events: Arc::new(RwLock::new(HashMap::new())),
max_events_per_alert: 100,
global_log: Arc::new(RwLock::new(VecDeque::new())),
max_global_log_size: 10000,
}
}
pub fn with_limits(max_per_alert: usize, max_global: usize) -> Self {
Self {
events: Arc::new(RwLock::new(HashMap::new())),
max_events_per_alert: max_per_alert,
global_log: Arc::new(RwLock::new(VecDeque::new())),
max_global_log_size: max_global,
}
}
pub fn record(&self, event: AlertHistoryEvent) {
{
let mut events = self.events.write();
let alert_events = events.entry(event.alert_id.clone()).or_default();
alert_events.push_back(event.clone());
while alert_events.len() > self.max_events_per_alert {
alert_events.pop_front();
}
}
{
let mut global = self.global_log.write();
global.push_back(event);
while global.len() > self.max_global_log_size {
global.pop_front();
}
}
}
pub fn get_alert_history(&self, alert_id: &str) -> Vec<AlertHistoryEvent> {
self.events
.read()
.get(alert_id)
.map(|events| events.iter().cloned().collect())
.unwrap_or_default()
}
pub fn get_recent_events(&self, limit: usize) -> Vec<AlertHistoryEvent> {
self.global_log
.read()
.iter()
.rev()
.take(limit)
.cloned()
.collect()
}
pub fn get_events_in_range(
&self,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Vec<AlertHistoryEvent> {
self.global_log
.read()
.iter()
.filter(|e| e.timestamp >= start && e.timestamp <= end)
.cloned()
.collect()
}
pub fn event_count(&self, alert_id: &str) -> usize {
self.events
.read()
.get(alert_id)
.map(|e| e.len())
.unwrap_or(0)
}
pub fn clear_alert_history(&self, alert_id: &str) {
self.events.write().remove(alert_id);
}
pub fn clear_all(&self) {
self.events.write().clear();
self.global_log.write().clear();
}
}
impl Default for AlertHistory {
fn default() -> Self {
Self::new()
}
}