use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use tokio::sync::RwLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IncidentType {
ServiceOutage, HighLatency, HighMemoryUsage, DiskFull, NetworkIssue, DatabaseIssue, DeploymentFailure, SecurityIncident, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ChangeType {
Configuration, Code, Database, Infrastructure, Deployment, Production, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IncidentSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IncidentStatus {
Open,
Investigating,
Identified,
Resolving,
Resolved,
Closed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Incident {
pub id: String,
pub incident_type: IncidentType,
pub severity: IncidentSeverity,
pub status: IncidentStatus,
pub title: String,
pub description: String,
pub timestamp: SystemTime,
pub affected_services: Vec<String>,
pub affected_components: Vec<String>,
pub symptoms: Vec<String>,
pub reporter: String,
pub assignee: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Change {
pub id: String,
pub change_type: ChangeType,
pub title: String,
pub description: String,
pub timestamp: SystemTime,
pub implemented_by: String,
pub approved_by: Option<String>,
pub affected_services: Vec<String>,
pub affected_components: Vec<String>,
pub risk_level: f64, pub rollback_plan: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CausalityType {
Causes, ContributesTo, CorrelatesWith, Precedes, Blocks, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Causality {
pub cause_id: String,
pub effect_id: String,
pub causality_type: CausalityType,
pub strength: f64, pub confidence: f64, pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RootCauseAnalysis {
pub incident_id: String,
pub root_causes: Vec<RootCause>,
pub contributing_factors: Vec<ContributingFactor>,
pub confidence_score: f64,
pub analysis_timestamp: SystemTime,
pub recommendations: Vec<String>,
pub related_changes: Vec<Change>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RootCause {
pub cause_id: String,
pub cause_type: String,
pub description: String,
pub evidence: Vec<String>,
pub confidence: f64,
pub impact_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContributingFactor {
pub factor_id: String,
pub description: String,
pub contribution_level: f64,
pub related_components: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChangeImpactAnalysis {
pub change_id: String,
pub risk_level: f64,
pub potential_impacts: Vec<Impact>,
pub mitigation_strategies: Vec<String>,
pub rollback_strategy: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Impact {
pub impact_type: IncidentType,
pub affected_services: Vec<String>,
pub probability: f64,
pub severity: IncidentSeverity,
}
#[derive(Debug)]
pub struct EventCollector {
incidents: Arc<RwLock<Vec<Incident>>>,
changes: Arc<RwLock<Vec<Change>>>,
}
impl EventCollector {
pub fn new() -> Self {
Self {
incidents: Arc::new(RwLock::new(Vec::new())),
changes: Arc::new(RwLock::new(Vec::new())),
}
}
pub async fn collect_incident(
&self,
incident: Incident,
) -> Result<(), Box<dyn std::error::Error>> {
let mut incidents = self.incidents.write().await;
incidents.push(incident);
Ok(())
}
pub async fn collect_change(&self, change: Change) -> Result<(), Box<dyn std::error::Error>> {
let mut changes = self.changes.write().await;
changes.push(change);
Ok(())
}
pub async fn get_related_events(
&self,
service: &str,
timeframe: Duration,
) -> Result<(Vec<Incident>, Vec<Change>), Box<dyn std::error::Error>> {
let cutoff_time: _ = SystemTime::now() - timeframe;
let service_str: _ = service.to_string();
let incidents: _ = self.incidents.read().await;
let changes: _ = self.changes.read().await;
let related_incidents: Vec<Incident> = incidents
.iter()
.filter(|i| {
i.timestamp > cutoff_time
&& (i.affected_services.contains(&service_str)
|| i.affected_components.contains(&service_str))
})
.cloned()
.collect();
let related_changes: Vec<Change> = changes
.iter()
.filter(|c| {
c.timestamp > cutoff_time
&& (c.affected_services.contains(&service_str)
|| c.affected_components.contains(&service_str))
})
.cloned()
.collect();
Ok((related_incidents, related_changes))
}
}
impl Default for EventCollector {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ChangeCorrelator {
event_collector: Arc<EventCollector>,
}
impl ChangeCorrelator {
pub fn new(event_collector: Arc<EventCollector>) -> Self {
Self { event_collector }
}
pub async fn correlate_changes_with_incidents(
&self,
timeframe: Duration,
) -> Result<Vec<(Change, Incident)>, Box<dyn std::error::Error>> {
let (incidents, changes) = self.get_all_events().await?;
let mut correlations = Vec::new();
for change in &changes {
for incident in &incidents {
if self.has_temporal_correlation(change, incident, &timeframe)?
&& self.has_component_overlap(change, incident)
{
correlations.push((change.clone(), incident.clone()));
}
}
}
Ok(correlations)
}
pub async fn find_suspect_changes(
&self,
incident: &Incident,
timeframe: Duration,
) -> Result<Vec<Change>, Box<dyn std::error::Error>> {
let (incidents, changes) = self.get_all_events().await?;
let mut suspect_changes = Vec::new();
for change in &changes {
if change.timestamp < incident.timestamp
&& incident
.timestamp
.duration_since(change.timestamp)
.unwrap_or_default()
< timeframe
&& self.has_component_overlap(change, incident)
{
let suspicion_score: _ = self.calculate_suspicion_score(change, incident);
if suspicion_score > 0.5 {
suspect_changes.push(change.clone());
}
}
}
Ok(suspect_changes)
}
fn has_temporal_correlation(
&self,
change: &Change,
incident: &Incident,
timeframe: &Duration,
) -> Result<bool, Box<dyn std::error::Error>> {
let time_diff: _ = if change.timestamp < incident.timestamp {
incident
.timestamp
.duration_since(change.timestamp)
.unwrap_or_default()
} else {
change
.timestamp
.duration_since(incident.timestamp)
.unwrap_or_default()
};
Ok(time_diff <= *timeframe)
}
fn has_component_overlap(&self, change: &Change, incident: &Incident) -> bool {
let change_components: HashSet<&str> = change
.affected_services
.iter()
.chain(change.affected_components.iter())
.map(|s| s.as_str())
.collect();
let incident_components: HashSet<&str> = incident
.affected_services
.iter()
.chain(incident.affected_components.iter())
.map(|s| s.as_str())
.collect();
!change_components.is_disjoint(&incident_components)
}
fn calculate_suspicion_score(&self, change: &Change, incident: &Incident) -> f64 {
let mut score = 0.0;
score += match change.change_type {
ChangeType::Configuration => 0.3,
ChangeType::Code => 0.4,
ChangeType::Database => 0.5,
ChangeType::Infrastructure => 0.6,
ChangeType::Deployment => 0.4,
ChangeType::Production => 0.7,
};
score += change.risk_level * 0.3;
let time_diff: _ = incident
.timestamp
.duration_since(change.timestamp)
.unwrap_or_default();
if time_diff.as_secs() < 3600 {
score += 0.2;
} else if time_diff.as_secs() < 86400 {
score += 0.1;
}
score.min(1.0)
}
async fn get_all_events(
&self,
) -> Result<(Vec<Incident>, Vec<Change>), Box<dyn std::error::Error>> {
let incidents: _ = self.event_collector.incidents.read().await;
let changes: _ = self.event_collector.changes.read().await;
Ok((incidents.clone(), changes.clone()))
}
}
#[derive(Debug)]
pub struct CausalInferenceEngine {
causal_graph: Arc<RwLock<HashMap<String, Vec<Causality>>>>,
}
impl CausalInferenceEngine {
pub fn new() -> Self {
Self {
causal_graph: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn build_causal_graph(
&self,
events: &[Incident],
) -> Result<(), Box<dyn std::error::Error>> {
let mut graph = self.causal_graph.write().await;
graph.clear();
for (i, event1) in events.iter().enumerate() {
for event2 in events.iter().skip(i + 1) {
if let Some(causality) = self.infer_causality(event1, event2)? {
graph
.entry(event1.id.clone())
.or_insert_with(Vec::new)
.push(causality);
}
}
}
Ok(())
}
fn infer_causality(
&self,
event1: &Incident,
event2: &Incident,
) -> Result<Option<Causality>, Box<dyn std::error::Error>> {
if event1.timestamp >= event2.timestamp {
return Ok(None);
}
let time_diff: _ = event2
.timestamp
.duration_since(event1.timestamp)
.unwrap_or_default();
let overlap: _ = self.calculate_component_overlap(event1, event2);
if time_diff.as_secs() < 3600 && overlap > 0.3 {
Ok(Some(Causality {
cause_id: event1.id.clone(),
effect_id: event2.id.clone(),
causality_type: CausalityType::Causes,
strength: overlap,
confidence: (1.0 - (time_diff.as_secs() as f64 / 3600.0)).min(1.0),
evidence: vec![
format!("时间间隔: {} 秒", time_diff.as_secs()),
format!("组件重叠度: {:.2}", overlap),
],
}))
} else {
Ok(None)
}
}
fn calculate_component_overlap(&self, event1: &Incident, event2: &Incident) -> f64 {
let components1: HashSet<&str> = event1
.affected_services
.iter()
.chain(event1.affected_components.iter())
.map(|s| s.as_str())
.collect();
let components2: HashSet<&str> = event2
.affected_services
.iter()
.chain(event2.affected_components.iter())
.map(|s| s.as_str())
.collect();
let intersection_size: _ = components1.intersection(&components2).count();
let union_size: _ = components1.union(&components2).count();
if union_size == 0 {
0.0
} else {
intersection_size as f64 / union_size as f64
}
}
pub async fn infer_root_causes(
&self,
incident: &Incident,
) -> Result<Vec<RootCause>, Box<dyn std::error::Error>> {
let graph: _ = self.causal_graph.read().await;
let mut root_causes = Vec::new();
for (cause_id, causalities) in graph.iter() {
for causality in causalities {
if causality.effect_id == incident.id {
root_causes.push(RootCause {
cause_id: cause_id.clone(),
cause_type: "event".to_string(),
description: format!("Event {} contributed to incident", cause_id),
evidence: causality.evidence.clone(),
confidence: causality.confidence,
impact_score: causality.strength,
});
}
}
}
Ok(root_causes)
}
}
impl Default for CausalInferenceEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct RootCauseAnalyzer {
event_collector: Arc<EventCollector>,
causal_inference_engine: Arc<CausalInferenceEngine>,
change_correlator: Arc<ChangeCorrelator>,
}
impl RootCauseAnalyzer {
pub fn new() -> Self {
let event_collector: _ = Arc::new(EventCollector::new());
Self {
event_collector: event_collector.clone(),
causal_inference_engine: Arc::new(CausalInferenceEngine::new()),
change_correlator: Arc::new(ChangeCorrelator::new(event_collector)),
}
}
pub async fn analyze_root_cause(
&self,
incident: &Incident,
) -> Result<RootCauseAnalysis, Box<dyn std::error::Error>> {
let timeframe: _ = Duration::from_secs(86400); let (related_incidents, related_changes) = self
.event_collector
.get_related_events(&incident.affected_services[0], timeframe)
.await?;
let all_events: _ = vec![incident.clone()]
.into_iter()
.chain(related_incidents.into_iter())
.collect::<Vec<_>>();
self.causal_inference_engine
.build_causal_graph(&all_events)
.await?;
let root_causes: _ = self
.causal_inference_engine
.infer_root_causes(incident)
.await?;
let contributing_factors: _ = self
.identify_contributing_factors(incident, &related_changes)
.await?;
let confidence_score: _ =
self.calculate_confidence_score(&root_causes, &contributing_factors)?;
let correlated_changes: _ = self
.change_correlator
.find_suspect_changes(incident, timeframe)
.await?;
let recommendations: _ =
self.generate_recommendations(&root_causes, &contributing_factors)?;
Ok(RootCauseAnalysis {
incident_id: incident.id.clone(),
root_causes,
contributing_factors,
confidence_score,
analysis_timestamp: SystemTime::now(),
recommendations,
related_changes: correlated_changes,
})
}
pub async fn analyze_change_impact(
&self,
change: &Change,
) -> Result<ChangeImpactAnalysis, Box<dyn std::error::Error>> {
let mut potential_impacts = Vec::new();
match change.change_type {
ChangeType::Configuration => {
potential_impacts.push(Impact {
impact_type: IncidentType::ServiceOutage,
affected_services: change.affected_services.clone(),
probability: 0.3,
severity: IncidentSeverity::Medium,
});
}
ChangeType::Code => {
potential_impacts.push(Impact {
impact_type: IncidentType::HighLatency,
affected_services: change.affected_services.clone(),
probability: 0.4,
severity: IncidentSeverity::Medium,
});
}
ChangeType::Database => {
potential_impacts.push(Impact {
impact_type: IncidentType::DatabaseIssue,
affected_services: change.affected_services.clone(),
probability: 0.5,
severity: IncidentSeverity::High,
});
}
ChangeType::Infrastructure => {
potential_impacts.push(Impact {
impact_type: IncidentType::ServiceOutage,
affected_services: change.affected_services.clone(),
probability: 0.6,
severity: IncidentSeverity::High,
});
}
ChangeType::Deployment => {
potential_impacts.push(Impact {
impact_type: IncidentType::DeploymentFailure,
affected_services: change.affected_services.clone(),
probability: 0.4,
severity: IncidentSeverity::Medium,
});
}
ChangeType::Production => {
potential_impacts.push(Impact {
impact_type: IncidentType::ServiceOutage,
affected_services: change.affected_services.clone(),
probability: 0.7,
severity: IncidentSeverity::Critical,
});
}
}
let mitigation_strategies: _ = vec![
"在非生产环境充分测试".to_string(),
"准备快速回滚方案".to_string(),
"分阶段部署".to_string(),
"监控关键指标".to_string(),
];
Ok(ChangeImpactAnalysis {
change_id: change.id.clone(),
risk_level: change.risk_level,
potential_impacts,
mitigation_strategies,
rollback_strategy: change.rollback_plan.clone(),
})
}
async fn identify_contributing_factors(
&self,
incident: &Incident,
changes: &[Change],
) -> Result<Vec<ContributingFactor>, Box<dyn std::error::Error>> {
let mut factors = Vec::new();
for change in changes {
if change.timestamp > incident.timestamp - Duration::from_secs(7200) {
factors.push(ContributingFactor {
factor_id: change.id.clone(),
description: format!("Recent change: {}", change.title),
contribution_level: 0.5,
related_components: change.affected_services.clone(),
});
}
}
Ok(factors)
}
fn calculate_confidence_score(
&self,
root_causes: &[RootCause],
contributing_factors: &[ContributingFactor],
) -> Result<f64, Box<dyn std::error::Error>> {
if root_causes.is_empty() {
return Ok(0.0);
}
let avg_confidence: f64 =
root_causes.iter().map(|rc| rc.confidence).sum::<f64>() / root_causes.len() as f64;
let factor_bonus: _ = (contributing_factors.len() as f64 * 0.1).min(0.3);
Ok((avg_confidence + factor_bonus).min(1.0))
}
fn generate_recommendations(
&self,
root_causes: &[RootCause],
contributing_factors: &[ContributingFactor],
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let mut recommendations = Vec::new();
for root_cause in root_causes {
if root_cause.cause_type == "event" {
recommendations.push(format!(
"Investigate root cause event: {}",
root_cause.cause_id
));
}
}
for factor in contributing_factors {
recommendations.push(format!(
"Address contributing factor: {}",
factor.description
));
}
recommendations.push("Implement monitoring and alerting".to_string());
recommendations.push("Review and update incident response procedures".to_string());
recommendations.push("Consider chaos engineering practices".to_string());
Ok(recommendations)
}
}
impl Default for RootCauseAnalyzer {
fn default() -> Self {
Self::new()
}
}