mirror of
https://github.com/LittleQuartZ/addmon.git
synced 2026-02-07 02:45:28 +07:00
feat(monitor): add alert pairing logic and display incident names
- Add pair_alerts() to pair consecutive Alerting→Normal state transitions - Add parse_labels_from_text() to extract labels from alert text field - Update ProcessedAlert struct with alertTime, resolveTime, isResolved, labels - Display incident name in alert list items instead of incident ID - Show parsed labels in alert row (excluding alertname)
This commit is contained in:
parent
ea1c9105a7
commit
22aee93fb3
6 changed files with 165 additions and 129 deletions
|
|
@ -1,19 +1,17 @@
|
|||
use crate::{calculate_kpis, AppError, GrafanaAlert, Incident, KpiMetrics, ProcessedAlert};
|
||||
use crate::{calculate_kpis, pair_alerts, AppError, GrafanaAlert, Incident, KpiMetrics, ProcessedAlert};
|
||||
use std::fs;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn load_alerts_from_file(path: String) -> Result<Vec<ProcessedAlert>, AppError> {
|
||||
let contents = fs::read_to_string(&path)?;
|
||||
let raw_alerts: Vec<GrafanaAlert> = serde_json::from_str(&contents)?;
|
||||
let processed: Vec<ProcessedAlert> = raw_alerts.into_iter().map(ProcessedAlert::from).collect();
|
||||
Ok(processed)
|
||||
Ok(pair_alerts(raw_alerts))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn process_alerts_json(json_content: String) -> Result<Vec<ProcessedAlert>, AppError> {
|
||||
let raw_alerts: Vec<GrafanaAlert> = serde_json::from_str(&json_content)?;
|
||||
let processed: Vec<ProcessedAlert> = raw_alerts.into_iter().map(ProcessedAlert::from).collect();
|
||||
Ok(processed)
|
||||
Ok(pair_alerts(raw_alerts))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
|
|
|||
|
|
@ -98,44 +98,76 @@ pub struct ProcessedAlert {
|
|||
pub alert_id: u32,
|
||||
#[serde(default)]
|
||||
pub alert_name: Option<String>,
|
||||
pub new_state: String,
|
||||
pub prev_state: String,
|
||||
pub time: u64,
|
||||
pub time_end: u64,
|
||||
pub alert_time: u64,
|
||||
pub resolve_time: Option<u64>,
|
||||
pub text: String,
|
||||
pub duration_ms: u64,
|
||||
pub is_firing: bool,
|
||||
pub is_resolved: bool,
|
||||
pub is_invalid: bool,
|
||||
pub attached_incident_id: Option<String>,
|
||||
pub values: HashMap<String, f64>,
|
||||
}
|
||||
|
||||
impl From<GrafanaAlert> for ProcessedAlert {
|
||||
fn from(alert: GrafanaAlert) -> Self {
|
||||
let duration_ms = if alert.time_end > alert.time {
|
||||
alert.time_end - alert.time
|
||||
} else {
|
||||
0
|
||||
};
|
||||
pub fn pair_alerts(raw_alerts: Vec<GrafanaAlert>) -> Vec<ProcessedAlert> {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
let is_firing = alert.new_state == "Alerting" || alert.new_state == "Firing";
|
||||
let mut by_alert_id: BTreeMap<u32, Vec<GrafanaAlert>> = BTreeMap::new();
|
||||
for alert in raw_alerts {
|
||||
by_alert_id.entry(alert.alert_id).or_default().push(alert);
|
||||
}
|
||||
|
||||
ProcessedAlert {
|
||||
id: alert.id,
|
||||
alert_id: alert.alert_id,
|
||||
alert_name: alert.alert_name,
|
||||
new_state: alert.new_state,
|
||||
prev_state: alert.prev_state,
|
||||
time: alert.time,
|
||||
time_end: alert.time_end,
|
||||
text: alert.text,
|
||||
duration_ms,
|
||||
is_firing,
|
||||
is_invalid: false,
|
||||
attached_incident_id: None,
|
||||
values: alert.data.map(|d| d.values).unwrap_or_default(),
|
||||
let mut result = Vec::new();
|
||||
|
||||
for (_alert_id, mut alerts) in by_alert_id {
|
||||
alerts.sort_by_key(|a| a.time);
|
||||
|
||||
let mut i = 0;
|
||||
while i < alerts.len() {
|
||||
let current = &alerts[i];
|
||||
|
||||
if current.new_state == "Alerting" {
|
||||
if i + 1 < alerts.len() && alerts[i + 1].new_state == "Normal" {
|
||||
let resolve = &alerts[i + 1];
|
||||
let duration_ms = resolve.time.saturating_sub(current.time);
|
||||
|
||||
result.push(ProcessedAlert {
|
||||
id: current.id,
|
||||
alert_id: current.alert_id,
|
||||
alert_name: current.alert_name.clone(),
|
||||
alert_time: current.time,
|
||||
resolve_time: Some(resolve.time),
|
||||
text: current.text.clone(),
|
||||
duration_ms,
|
||||
is_resolved: true,
|
||||
is_invalid: false,
|
||||
attached_incident_id: None,
|
||||
values: current.data.clone().map(|d| d.values).unwrap_or_default(),
|
||||
});
|
||||
i += 2;
|
||||
} else {
|
||||
result.push(ProcessedAlert {
|
||||
id: current.id,
|
||||
alert_id: current.alert_id,
|
||||
alert_name: current.alert_name.clone(),
|
||||
alert_time: current.time,
|
||||
resolve_time: None,
|
||||
text: current.text.clone(),
|
||||
duration_ms: 0,
|
||||
is_resolved: false,
|
||||
is_invalid: false,
|
||||
attached_incident_id: None,
|
||||
values: current.data.clone().map(|d| d.values).unwrap_or_default(),
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.sort_by(|a, b| b.alert_time.cmp(&a.alert_time));
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -176,19 +208,16 @@ pub fn calculate_kpis(alerts: &[ProcessedAlert], incidents: &[Incident]) -> KpiM
|
|||
|
||||
let overall_downtime_ms: u64 = alerts
|
||||
.iter()
|
||||
.filter(|a| a.is_firing && !a.is_invalid)
|
||||
.filter(|a| !a.is_invalid)
|
||||
.map(|a| a.duration_ms)
|
||||
.sum();
|
||||
|
||||
let overall_downtime_formatted = format_duration(overall_downtime_ms);
|
||||
|
||||
let total_firing_alerts = alerts.iter().filter(|a| a.is_firing).count();
|
||||
let invalid_alerts = alerts
|
||||
.iter()
|
||||
.filter(|a| a.is_firing && a.is_invalid)
|
||||
.count();
|
||||
let invalid_alert_ratio = if total_firing_alerts > 0 {
|
||||
(invalid_alerts as f64 / total_firing_alerts as f64) * 100.0
|
||||
let total_alerts = alerts.len();
|
||||
let invalid_alerts = alerts.iter().filter(|a| a.is_invalid).count();
|
||||
let invalid_alert_ratio = if total_alerts > 0 {
|
||||
(invalid_alerts as f64 / total_alerts as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
|
@ -200,7 +229,7 @@ pub fn calculate_kpis(alerts: &[ProcessedAlert], incidents: &[Incident]) -> KpiM
|
|||
overall_downtime_ms,
|
||||
overall_downtime_formatted,
|
||||
invalid_alert_ratio,
|
||||
total_firing_alerts,
|
||||
total_firing_alerts: total_alerts,
|
||||
invalid_alerts,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue