export class TimelineAnalyzer {
constructor() {
this.eventCorrelator = new EventCorrelator();
this.causalChainDetector = new CausalChainDetector();
this.reportGenerator = new AutoReportGenerator();
this.knowledgeBase = new OperationalKnowledgeBase();
}
// 24์๊ฐ ๊ธฐ์ค์ผ๋ก ๋ณ๊ฒฝ
async analyzeIncidentTimeline(startTime = null, endTime = null) {
// ๊ธฐ๋ณธ๊ฐ: ์ง๋ 24์๊ฐ
if (!startTime || !endTime) {
endTime = new Date();
startTime = new Date(endTime.getTime() - 24 * 60 * 60 * 1000); // 24์๊ฐ ์
}
const analysis = {
timeline_period: { start: startTime, end: endTime },
normal_period: null, // ์ธ์ ๊น์ง ์ ์์ด์๋์ง
incident_start: null, // ์ธ์ ๋ถํฐ ๋ฌธ์ ์ธ์ง
what_happened: [], // ๋ฌด์์ด ๋ฐ์ํ๋์ง
where_occurred: [], // ์ด๋์ ๋ฐ์ํ๋์ง
why_happened: null, // ์ ๋ฐ์ํ๋์ง (๊ทผ๋ณธ์์ธ)
how_to_respond: [], // ์ด๋ป๊ฒ ๋์ํ ์ง
business_impact: null,
lessons_learned: [],
prevention_recommendations: [],
causal_chain: [] // ๊ธฐ์กด ํธํ์ฑ ์ ์ง
};
// 1. 24์๊ฐ ๋์์ ๋ชจ๋ ์๋ฒ ๋ฐ์ดํฐ ์์ง
const timeSeriesData = await this.collectTimeSeriesData(startTime, endTime);
// 2. ์ ์/๋น์ ์ ๊ตฌ๊ฐ ์๋ณ
const { normalPeriod, incidentPeriods } = this.identifyNormalAndIncidentPeriods(timeSeriesData);
analysis.normal_period = normalPeriod;
analysis.incident_start = incidentPeriods.length > 0 ? incidentPeriods[0].start : null;
// 3. ๊ธฐ์กด ๋ก์ง ํธํ์ฑ ์ ์ง
const chronologicalEvents = this.buildChronologicalEventMap(timeSeriesData);
analysis.causal_chain = await this.detectCausalChain(chronologicalEvents);
// 4. 6ํ์์น ๊ธฐ๋ฐ ๋ถ์
analysis.what_happened = await this.analyzeWhatHappened(timeSeriesData, incidentPeriods);
analysis.where_occurred = this.analyzeWhereOccurred(analysis.what_happened);
analysis.why_happened = await this.analyzeWhyHappened(analysis.what_happened);
analysis.how_to_respond = await this.generateResponsePlan(analysis);
// 5. ๊ธฐ์กด ๋ก์ง ํธํ์ฑ ์ ์ง
analysis.business_impact = this.calculateBusinessImpact(analysis.causal_chain);
return analysis;
}
// ์ ์/๋น์ ์ ๊ตฌ๊ฐ ์๋ณ (์๋ก์ด ํต์ฌ ๊ธฐ๋ฅ)
identifyNormalAndIncidentPeriods(timeSeriesData) {
const periods = {
normalPeriod: { start: null, end: null },
incidentPeriods: []
};
// ์๊ฐ์์ผ๋ก ์ด์ ์งํ ํ์ง
const anomalies = [];
for (const [serverId, metrics] of Object.entries(timeSeriesData)) {
metrics.forEach(dataPoint => {
const serverAnomalies = this.detectAnomaliesInDataPoint(dataPoint, serverId);
anomalies.push(...serverAnomalies.map(a => ({...a, timestamp: dataPoint.timestamp, server_id: serverId})));
});
}
// ์๊ฐ์ ์ ๋ ฌ
anomalies.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
if (anomalies.length === 0) {
// 24์๊ฐ ๋ด๋ด ์ ์
periods.normalPeriod = {
start: new Date(Date.now() - 24 * 60 * 60 * 1000),
end: new Date(),
status: "์์ ์ ์"
};
} else {
// ์ฒซ ๋ฒ์งธ ์ด์ ์งํ ์ ๊น์ง๊ฐ ์ ์ ๊ตฌ๊ฐ
periods.normalPeriod = {
start: new Date(Date.now() - 24 * 60 * 60 * 1000),
end: new Date(anomalies[0].timestamp),
duration: this.calculateDuration(new Date(Date.now() - 24 * 60 * 60 * 1000), new Date(anomalies[0].timestamp))
};
// ์ด์ ์งํ ๊ตฌ๊ฐ๋ค ์๋ณ
let currentIncident = {
start: new Date(anomalies[0].timestamp),
end: null,
events: []
};
anomalies.forEach((anomaly, index) => {
currentIncident.events.push(anomaly);
// ๋ค์ ์ด์ ์งํ์ 30๋ถ ์ด์ ์ฐจ์ด๋๋ฉด ๋ณ๊ฐ ์ฌ๊ฑด์ผ๋ก ์ฒ๋ฆฌ
if (index < anomalies.length - 1) {
const timeDiff = new Date(anomalies[index + 1].timestamp) - new Date(anomaly.timestamp);
if (timeDiff > 30 * 60 * 1000) { // 30๋ถ
currentIncident.end = new Date(anomaly.timestamp);
periods.incidentPeriods.push(currentIncident);
currentIncident = {
start: new Date(anomalies[index + 1].timestamp),
end: null,
events: []
};
}
} else {
// ๋ง์ง๋ง ์ด์ ์งํ
currentIncident.end = new Date();
}
});
if (currentIncident.events.length > 0) {
periods.incidentPeriods.push(currentIncident);
}
}
return periods;
}
// What: ๋ฌด์์ด ๋ฐ์ํ๋์ง ๋ถ์
async analyzeWhatHappened(timeSeriesData, incidentPeriods) {
const whatHappened = [];
incidentPeriods.forEach(period => {
const periodAnalysis = {
timeframe: `${new Date(period.start).toLocaleString()} ~ ${new Date(period.end).toLocaleString()}`,
duration: this.calculateDuration(period.start, period.end),
primary_symptoms: [],
secondary_effects: [],
affected_metrics: [],
severity_level: 'unknown'
};
// ์ฃผ์ ์ฆ์ ์๋ณ
const symptomsByType = {};
period.events.forEach(event => {
if (!symptomsByType[event.type]) {
symptomsByType[event.type] = [];
}
symptomsByType[event.type].push(event);
});
// ์ฃผ์ ์ฆ์ ์ ๋ฆฌ
for (const [type, events] of Object.entries(symptomsByType)) {
const servers = [...new Set(events.map(e => e.server_id))];
periodAnalysis.primary_symptoms.push({
type: this.translateEventType(type),
affected_servers: servers,
count: events.length,
description: this.generateSymptomDescription(type, servers, events)
});
}
// ์ฌ๊ฐ๋ ๊ณ์ฐ
const criticalEvents = period.events.filter(e => e.severity === 'critical');
if (criticalEvents.length >= 3) {
periodAnalysis.severity_level = 'critical';
} else if (criticalEvents.length >= 1) {
periodAnalysis.severity_level = 'warning';
} else {
periodAnalysis.severity_level = 'info';
}
whatHappened.push(periodAnalysis);
});
return whatHappened;
}
// Where: ์ด๋์ ๋ฐ์ํ๋์ง ๋ถ์
analyzeWhereOccurred(whatHappened) {
const locations = {
affected_systems: [],
impact_zones: [],
propagation_path: []
};
whatHappened.forEach(incident => {
incident.primary_symptoms.forEach(symptom => {
// ์ํฅ๋ฐ์ ์์คํ
๋ถ๋ฅ
symptom.affected_servers.forEach(server => {
const systemType = this.classifySystemType(server);
if (!locations.affected_systems.find(s => s.type === systemType)) {
locations.affected_systems.push({
type: systemType,
servers: [server],
criticality: this.getSystemCriticality(systemType)
});
} else {
const existing = locations.affected_systems.find(s => s.type === systemType);
if (!existing.servers.includes(server)) {
existing.servers.push(server);
}
}
});
});
});
// ์ ํ ๊ฒฝ๋ก ๋ถ์
locations.propagation_path = this.analyzePropagationPath(whatHappened);
return locations;
}
// Why: ์ ๋ฐ์ํ๋์ง ๊ทผ๋ณธ์์ธ ๋ถ์
async analyzeWhyHappened(whatHappened) {
const rootCauseAnalysis = {
immediate_cause: null, // ์ง์ ์ ์์ธ
underlying_cause: null, // ๊ทผ๋ณธ์ ์์ธ
contributing_factors: [], // ๊ธฐ์ฌ ์์ธ๋ค
hypothesis_confidence: 0, // ๊ฐ์ค ์ ๋ขฐ๋
evidence_strength: 'low' // ์ฆ๊ฑฐ ๊ฐ๋
};
if (whatHappened.length === 0) {
return {
conclusion: "๋ถ์ ๊ธฐ๊ฐ ๋์ ํน๋ณํ ์ด์ ์งํ๊ฐ ๋ฐ๊ฒฌ๋์ง ์์์ต๋๋ค.",
system_health: "excellent"
};
}
// ๊ฐ์ฅ ์ด๊ธฐ ์ฆ์๋ถํฐ ๋ถ์
const earliestIncident = whatHappened[0];
const firstSymptom = earliestIncident.primary_symptoms[0];
// ์ง์ ์ ์์ธ ์ถ๋ก
rootCauseAnalysis.immediate_cause = this.inferImmediateCause(firstSymptom);
// ๊ทผ๋ณธ์ ์์ธ ์ถ๋ก (์ง์๋ฒ ์ด์ค ํ์ฉ)
rootCauseAnalysis.underlying_cause = await this.knowledgeBase.inferRootCause(
firstSymptom,
earliestIncident,
whatHappened
);
// ๊ธฐ์ฌ ์์ธ๋ค ์๋ณ
rootCauseAnalysis.contributing_factors = this.identifyContributingFactors(whatHappened);
// ์ ๋ขฐ๋ ๊ณ์ฐ
rootCauseAnalysis.hypothesis_confidence = this.calculateHypothesisConfidence(rootCauseAnalysis);
return rootCauseAnalysis;
}
// How: ์ด๋ป๊ฒ ๋์ํ ์ง ๊ณํ ์์ฑ
async generateResponsePlan(analysis) {
const responsePlan = {
immediate_actions: [], // ์ฆ์ ์กฐ์น (0-30๋ถ)
short_term_actions: [], // ๋จ๊ธฐ ์กฐ์น (1-24์๊ฐ)
medium_term_actions: [], // ์ค๊ธฐ ์กฐ์น (1-7์ผ)
long_term_prevention: [], // ์ฅ๊ธฐ ์๋ฐฉ (1๊ฐ์+)
monitoring_enhancement: [], // ๋ชจ๋ํฐ๋ง ๊ฐํ
escalation_criteria: [], // ์์ค์ปฌ๋ ์ด์
๊ธฐ์ค
rollback_procedures: [] // ๋กค๋ฐฑ ์ ์ฐจ
};
// ์ง์๋ฒ ์ด์ค์์ ๋์ ๋ฐฉ์ ์กฐํ
if (analysis.why_happened && analysis.why_happened.underlying_cause) {
const kbResponse = await this.knowledgeBase.getResponseProcedures(
analysis.why_happened.underlying_cause,
analysis.what_happened,
analysis.where_occurred
);
responsePlan.immediate_actions = kbResponse.immediate || [];
responsePlan.short_term_actions = kbResponse.short_term || [];
responsePlan.medium_term_actions = kbResponse.medium_term || [];
responsePlan.long_term_prevention = kbResponse.prevention || [];
}
// ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ (์ง์๋ฒ ์ด์ค์ ์๋ ๊ฒฝ์ฐ)
if (responsePlan.immediate_actions.length === 0) {
responsePlan.immediate_actions = this.generateDefaultImmediateActions(analysis);
}
return responsePlan;
}
// ์๊ฐ์ ์ด๋ฒคํธ ๋งต ๊ตฌ์ถ (AI์ ํต์ฌ ๋ก์ง)
buildChronologicalEventMap(timeSeriesData) {
const events = [];
for (const [serverId, metrics] of Object.entries(timeSeriesData)) {
metrics.forEach(dataPoint => {
// ๊ฐ ๋ฉํธ๋ฆญ์์ ์ด์ ์งํ ํ์ง
const anomalies = this.detectAnomaliesInDataPoint(dataPoint, serverId);
anomalies.forEach(anomaly => {
events.push({
timestamp: dataPoint.timestamp,
server_id: serverId,
event_type: anomaly.type,
severity: anomaly.severity,
metric_values: dataPoint.metrics,
description: anomaly.description,
potential_causes: anomaly.potential_causes || []
});
});
});
}
// ์๊ฐ์ ์ ๋ ฌ
return events.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
}
// ์ธ๊ณผ๊ด๊ณ ์ฒด์ธ ํ์ง (AI ์ถ๋ก ์ ํต์ฌ)
async detectCausalChain(chronologicalEvents) {
const causalChain = [];
const correlationWindow = 300000; // 5๋ถ ์๋์ฐ
for (let i = 0; i < chronologicalEvents.length; i++) {
const currentEvent = chronologicalEvents[i];
const relatedEvents = this.findRelatedEvents(currentEvent, chronologicalEvents, correlationWindow);
if (relatedEvents.length > 0) {
const chainSegment = {
trigger_event: currentEvent,
cascading_effects: relatedEvents,
causal_relationship: await this.analyzeCausalRelationship(currentEvent, relatedEvents),
confidence_score: this.calculateConfidenceScore(currentEvent, relatedEvents),
timeline_position: i
};
causalChain.push(chainSegment);
}
}
return this.optimizeCausalChain(causalChain);
}
// ์ธ๊ณผ๊ด๊ณ ๋ถ์ (AI ์ถ๋ก )
async analyzeCausalRelationship(triggerEvent, effects) {
const relationships = [];
// ์๋ฒ ๊ฐ ์์กด์ฑ ๊ธฐ๋ฐ ๋ถ์
if (triggerEvent.server_id.includes('db-') && effects.some(e => e.server_id.includes('web-'))) {
relationships.push({
type: 'database_dependency',
explanation: '๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ ๋ฌธ์ ๊ฐ ์น ์๋ฒ ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์ณค์ต๋๋ค.',
confidence: 0.9
});
}
// ๋คํธ์ํฌ ์ ํ ํจํด ๋ถ์
if (triggerEvent.event_type === 'network_latency' &&
effects.some(e => e.event_type === 'connection_timeout')) {
relationships.push({
type: 'network_propagation',
explanation: '๋คํธ์ํฌ ์ง์ฐ์ด ์ฐ๊ฒฐ ํ์์์์ผ๋ก ํ์ฐ๋์์ต๋๋ค.',
confidence: 0.85
});
}
// ๋ฆฌ์์ค ๊ฒฝ์ ํจํด ๋ถ์
if (triggerEvent.event_type === 'high_cpu' &&
effects.some(e => e.event_type === 'memory_pressure')) {
relationships.push({
type: 'resource_competition',
explanation: 'CPU ๊ณผ๋ถํ๋ก ์ธํ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ด ๋ฐ์ํ์ต๋๋ค.',
confidence: 0.8
});
}
return relationships;
}
// ๋น์ฆ๋์ค ์ํฅ๋ ๊ณ์ฐ
calculateBusinessImpact(causalChain) {
let totalImpact = 0;
const affectedServices = new Set();
const impactMetrics = {
service_availability: 100,
response_time_degradation: 0,
transaction_failure_rate: 0,
user_experience_score: 100
};
causalChain.forEach(segment => {
// ์ํฅ๋ฐ์ ์๋น์ค ์๋ณ
affectedServices.add(segment.trigger_event.server_id);
segment.cascading_effects.forEach(effect => {
affectedServices.add(effect.server_id);
});
// ์ํฅ๋ ์ ์ ๊ณ์ฐ
if (segment.trigger_event.severity === 'critical') {
totalImpact += 30;
impactMetrics.service_availability -= 15;
} else if (segment.trigger_event.severity === 'warning') {
totalImpact += 10;
impactMetrics.response_time_degradation += 10;
}
});
return {
total_impact_score: Math.min(totalImpact, 100),
affected_services: Array.from(affectedServices),
metrics: impactMetrics,
estimated_revenue_impact: this.estimateRevenueImpact(totalImpact),
user_impact_assessment: this.assessUserImpact(impactMetrics)
};
}
// ๋๋ฏธ ๋ฐ์ดํฐ ์ฐ๋ (์์ฐ์ฉ)
async collectTimeSeriesData(startTime, endTime) {
// ์ค์ ๋ก๋ DataSourceAdapter๋ฅผ ํตํด ๋ฐ์ดํฐ ์์ง
// ์์ฐ์ฉ์ผ๋ก๋ ๋๋ฏธ ๋ฐ์ดํฐ ์์ฑ
const dummyGenerator = new (await import('../utils/DummyDataGenerator.js')).DummyDataGenerator();
return dummyGenerator.generateTimeSeriesForPeriod(startTime, endTime);
}
// ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ค
detectAnomaliesInDataPoint(dataPoint, serverId) {
const anomalies = [];
const metrics = dataPoint.metrics;
if (metrics.cpu && metrics.cpu.usage_percent > 85) {
anomalies.push({
type: 'high_cpu',
severity: metrics.cpu.usage_percent > 95 ? 'critical' : 'warning',
description: `CPU ์ฌ์ฉ๋ฅ ${metrics.cpu.usage_percent.toFixed(1)}% ์๊ณ์น ์ด๊ณผ`,
potential_causes: ['high_traffic', 'inefficient_process', 'resource_leak']
});
}
if (metrics.memory && metrics.memory.usage_percent > 90) {
anomalies.push({
type: 'memory_pressure',
severity: 'critical',
description: `๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ ${metrics.memory.usage_percent.toFixed(1)}% ์ํ ์์ค`,
potential_causes: ['memory_leak', 'large_dataset', 'buffer_overflow']
});
}
return anomalies;
}
findRelatedEvents(currentEvent, allEvents, windowMs) {
const currentTime = new Date(currentEvent.timestamp);
return allEvents.filter(event => {
const eventTime = new Date(event.timestamp);
const timeDiff = Math.abs(eventTime - currentTime);
return timeDiff <= windowMs && event !== currentEvent;
});
}
calculateConfidenceScore(triggerEvent, relatedEvents) {
let confidence = 0.5; // ๊ธฐ๋ณธ ์ ๋ขฐ๋
// ์๊ฐ์ ๊ทผ์ ์ฑ
const avgTimeDiff = relatedEvents.reduce((sum, event) => {
return sum + Math.abs(new Date(event.timestamp) - new Date(triggerEvent.timestamp));
}, 0) / relatedEvents.length;
if (avgTimeDiff < 60000) confidence += 0.3; // 1๋ถ ์ด๋ด
else if (avgTimeDiff < 300000) confidence += 0.2; // 5๋ถ ์ด๋ด
// ์๋ฒ ๊ฐ ์์กด์ฑ
const hasLogicalDependency = this.checkServerDependency(triggerEvent.server_id,
relatedEvents.map(e => e.server_id));
if (hasLogicalDependency) confidence += 0.2;
return Math.min(confidence, 1.0);
}
checkServerDependency(serverId1, serverIds) {
// ๋
ผ๋ฆฌ์ ์์กด์ฑ ์ฒดํฌ (DB โ Web, Web โ Cache ๋ฑ)
const dependencies = {
'db-': ['web-', 'api-'],
'web-': ['cache-', 'cdn-'],
'k8s-master': ['k8s-worker']
};
for (const [prefix, dependents] of Object.entries(dependencies)) {
if (serverId1.startsWith(prefix)) {
return serverIds.some(id => dependents.some(dep => id.startsWith(dep)));
}
}
return false;
}
// ์ด๋ฒคํธ ์ ํ ํจํด ๋ถ์
analyzePropagationPattern(causalChain) {
if (causalChain.length === 0) return { pattern: 'none', flow: [] };
const flow = causalChain.map(segment => segment.trigger_event.server_id);
// ์ ํ ํจํด ์นดํ
๊ณ ๋ฆฌ ํ์ง
let pattern = 'random';
// ๊ณ์ธต์ ์ ํ (์: DB โ App โ Web)
const tiers = ['db-', 'cache-', 'app-', 'api-', 'web-'];
const tierFlow = flow.map(server => {
for (let i = 0; i < tiers.length; i++) {
if (server.startsWith(tiers[i])) return i;
}
return -1;
}).filter(tier => tier !== -1);
if (tierFlow.length > 1 && this.isMonotonic(tierFlow)) {
pattern = 'tiered_propagation';
}
// ๋ฐฉ์ฌํ ์ ํ (์ค์ โ ์ฌ๋ฌ ๋
ธ๋)
const uniqueServers = new Set(flow);
if (uniqueServers.size > 3 && uniqueServers.size / flow.length > 0.7) {
pattern = 'radial_propagation';
}
return {
pattern,
flow,
affected_tiers: this.getAffectedTiers(flow)
};
}
isMonotonic(array) {
let increasing = true;
let decreasing = true;
for (let i = 1; i < array.length; i++) {
increasing = increasing && array[i] >= array[i - 1];
decreasing = decreasing && array[i] <= array[i - 1];
}
return increasing || decreasing;
}
getAffectedTiers(serverIds) {
const tiers = {
'database': 0,
'cache': 0,
'application': 0,
'api': 0,
'web': 0,
'kubernetes': 0
};
serverIds.forEach(id => {
if (id.includes('db-')) tiers.database++;
else if (id.includes('cache-')) tiers.cache++;
else if (id.includes('app-')) tiers.application++;
else if (id.includes('api-')) tiers.api++;
else if (id.includes('web-')) tiers.web++;
else if (id.includes('k8s-')) tiers.kubernetes++;
});
return tiers;
}
// ๊ทผ๋ณธ ์์ธ ๊ฐ์ค ์์ฑ
generateRootCauseHypotheses(initialEvent, propagationPattern) {
const hypotheses = [];
// ์ด๋ฒคํธ ์ ํ๋ณ ๊ฐ์ค
if (initialEvent.event_type === 'high_cpu') {
hypotheses.push({
category: 'resource_exhaustion',
technical_explanation: 'CPU ๋ถํ ์ฆ๊ฐ๋ก ์ธํ ์์ ์์ง',
confidence: 0.85,
probable_triggers: ['traffic_spike', 'inefficient_query', 'background_job'],
immediate_fix: '๋ฆฌ์์ค ์ฆ์ค ๋๋ ๋ถํ ๋ถ์ฐ'
});
} else if (initialEvent.event_type === 'memory_pressure') {
hypotheses.push({
category: 'memory_leak',
technical_explanation: '๋ฉ๋ชจ๋ฆฌ ๋์๋ก ์ธํ ์ ์ง์ ์์ ์์ง',
confidence: 0.9,
probable_triggers: ['application_bug', 'memory_fragmentation'],
immediate_fix: 'ํ๋ก์ธ์ค ์ฌ์์ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ํ๋กํ์ผ๋ง'
});
} else if (initialEvent.event_type === 'network_latency') {
hypotheses.push({
category: 'network_congestion',
technical_explanation: '๋คํธ์ํฌ ํผ์ก์ผ๋ก ์ธํ ์ง์ฐ ๋ฐ์',
confidence: 0.8,
probable_triggers: ['bandwidth_saturation', 'dns_issues', 'routing_problems'],
immediate_fix: '๋คํธ์ํฌ ํธ๋ํฝ ์ต์ ํ ๋ฐ ๋ผ์ฐํ
์กฐ์ '
});
}
// ์๋ฒ ์ ํ๋ณ ๊ฐ์ค
if (initialEvent.server_id.includes('db-')) {
hypotheses.push({
category: 'database_bottleneck',
technical_explanation: '๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ณ๋ชฉ ํ์',
confidence: 0.75,
probable_triggers: ['slow_query', 'index_missing', 'lock_contention'],
immediate_fix: '์ฟผ๋ฆฌ ์ต์ ํ ๋ฐ ์ธ๋ฑ์ค ๊ฐ์ '
});
} else if (initialEvent.server_id.includes('k8s-')) {
hypotheses.push({
category: 'kubernetes_issue',
technical_explanation: '์ฟ ๋ฒ๋คํฐ์ค ํด๋ฌ์คํฐ ๋ฌธ์ ',
confidence: 0.7,
probable_triggers: ['pod_scheduling', 'etcd_consistency', 'resource_limits'],
immediate_fix: '๋ฌธ์ ํ๋ ์๋ณ ๋ฐ ์ฌ๋ฐฐํฌ'
});
}
return hypotheses;
}
// ๊ฐ์ฅ ๊ฐ๋ฅ์ฑ ๋์ ๊ทผ๋ณธ ์์ธ ์ ํ
selectMostLikelyRootCause(hypotheses) {
if (hypotheses.length === 0) return null;
// ์ ๋ขฐ๋๊ฐ ๊ฐ์ฅ ๋์ ๊ฐ์ค ์ ํ
return hypotheses.reduce((best, current) => {
return (current.confidence > best.confidence) ? current : best;
}, hypotheses[0]);
}
// ๊ทผ๋ณธ ์์ธ ์ ๋ขฐ๋ ๊ณ์ฐ
calculateRootCauseConfidence(rootCause, causalChain) {
if (!rootCause) return 0;
let confidence = rootCause.confidence;
// ์ด๋ฒคํธ ์ฒด์ธ ๊ธธ์ด๊ฐ ๊ธธ์๋ก ์ ๋ขฐ๋ ๊ฐ์
confidence *= Math.max(0.5, 1 - (causalChain.length * 0.05));
return Math.min(confidence, 1.0);
}
// ๊ทผ๋ณธ ์์ธ ์ง์ง ์ฆ๊ฑฐ ์์ง
gatherSupportingEvidence(rootCause, causalChain) {
if (!rootCause || causalChain.length === 0) return [];
const evidence = [];
// ์นดํ
๊ณ ๋ฆฌ๋ณ ์ฆ๊ฑฐ ์์ง
switch (rootCause.category) {
case 'resource_exhaustion':
evidence.push(
'์ง์์ ์ธ CPU ์ฌ์ฉ๋ฅ ์ฆ๊ฐ ํจํด',
'์ด๋ฒคํธ ๋ฐ์ ์ ์ ์ง์ ์ธ ๋ถํ ์ฆ๊ฐ'
);
break;
case 'memory_leak':
evidence.push(
'๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ ํ์ ์ฆ๊ฐ',
'์ฌ์์ ํ ์ ์ํ ํจํด'
);
break;
case 'network_congestion':
evidence.push(
'๋คํธ์ํฌ ์ง์ฐ ์๊ฐ ๊ธ์ฆ',
'ํจํท ์์ค ์ฆ๊ฐ'
);
break;
}
// ์ฌ๊ฑด ์ฒด์ธ์์ ์ฆ๊ฑฐ ์ถ์ถ
causalChain.forEach(segment => {
if (segment.trigger_event.event_type === 'high_cpu' && rootCause.category === 'resource_exhaustion') {
evidence.push(`${segment.trigger_event.server_id}์ CPU ์ฌ์ฉ๋ฅ ${segment.trigger_event.metric_values.cpu.usage_percent}% ๊ด์ธก`);
} else if (segment.trigger_event.event_type === 'memory_pressure' && rootCause.category === 'memory_leak') {
evidence.push(`${segment.trigger_event.server_id}์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ ${segment.trigger_event.metric_values.memory.usage_percent}% ๊ด์ธก`);
}
});
return evidence;
}
// ๊ตํ ์์ฑ
generateLessonsLearned(analysis) {
if (!analysis.root_cause_analysis?.primary_root_cause) return [];
const rootCause = analysis.root_cause_analysis.primary_root_cause;
const lessons = [];
// ๊ทผ๋ณธ ์์ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ๊ตํ
switch (rootCause.category) {
case 'resource_exhaustion':
lessons.push(
'์์ ๋ชจ๋ํฐ๋ง ์๊ณ์น ์กฐ์ ํ์',
'์๋ ์ค์ผ์ผ๋ง ์ ์ฑ
๊ฒํ ',
'๋ถํ ํ
์คํธ๋ฅผ ํตํ ์ฉ๋ ๊ณํ ์๋ฆฝ'
);
break;
case 'memory_leak':
lessons.push(
'์ ๊ธฐ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ํ๋กํ์ผ๋ง ํ์',
'์ ํ๋ฆฌ์ผ์ด์
์ฌ์์ ์ ์ฑ
์๋ฆฝ',
'๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์งํ ๋ชจ๋ํฐ๋ง ๊ฐํ'
);
break;
case 'database_bottleneck':
lessons.push(
'์ฟผ๋ฆฌ ์ต์ ํ ๋ฐ ์ธ๋ฑ์ฑ ์ ๋ต ์ฌ๊ฒํ ',
'DB ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๊ฐํ',
'์ฝ๊ธฐ/์ฐ๊ธฐ ๋ถ๋ฆฌ ๊ณ ๋ ค'
);
break;
}
return lessons;
}
// ์๋ฐฉ ๊ณํ ์์ฑ
generatePreventionPlan(analysis) {
if (!analysis.root_cause_analysis?.primary_root_cause) return [];
const rootCause = analysis.root_cause_analysis.primary_root_cause;
const recommendations = [];
// ๋จ๊ธฐ ์กฐ์น
switch (rootCause.category) {
case 'resource_exhaustion':
recommendations.push({
timeline: 'short_term',
description: '์๋ฒ ์์ ์ฆ์ค ๋๋ ๋ถํ ๋ถ์ฐ',
priority: 'high'
});
break;
case 'memory_leak':
recommendations.push({
timeline: 'short_term',
description: '๋ฉ๋ชจ๋ฆฌ ๋์ ๋๋ฒ๊น
๋ฐ ํจ์น ์ ์ฉ',
priority: 'high'
});
break;
case 'database_bottleneck':
recommendations.push({
timeline: 'short_term',
description: '๋ฌธ์ ์ฟผ๋ฆฌ ์ต์ ํ ๋ฐ ์ธ๋ฑ์ค ์กฐ์ ',
priority: 'high'
});
break;
}
// ์ฅ๊ธฐ ์กฐ์น
recommendations.push({
timeline: 'long_term',
description: '์ ์ฌ ์ฅ์ ๋ฐฉ์ง๋ฅผ ์ํ ๋ชจ๋ํฐ๋ง ๊ฐํ',
priority: 'medium'
});
recommendations.push({
timeline: 'long_term',
description: '์๋ํ๋ ์ฅ์ ๋์ ํ๋ก์ธ์ค ๊ตฌ์ถ',
priority: 'medium'
});
return recommendations;
}
// ์์ ๋งค์ถ ์ํฅ ์ถ์
estimateRevenueImpact(totalImpact) {
// ์ค์ ๋ก๋ ๋น์ฆ๋์ค ๋ฐ์ดํฐ ๊ธฐ๋ฐ ๊ณ์ฐ
// ์์ฐ์ฉ ๋๋ฏธ ๋ก์ง
if (totalImpact > 80) return '์ฌ๊ฐ (์ถ์ 10% ์ด์ ๋งค์ถ ์์ค)';
if (totalImpact > 50) return '์๋นํจ (์ถ์ 5-10% ๋งค์ถ ์์ค)';
if (totalImpact > 20) return '๊ฒฝ๋ฏธํจ (์ถ์ 1-5% ๋งค์ถ ์์ค)';
return '๋ฏธ๋ฏธํจ (์ถ์ 1% ๋ฏธ๋ง ๋งค์ถ ์์ค)';
}
// ์ฌ์ฉ์ ์ํฅ ํ๊ฐ
assessUserImpact(impactMetrics) {
const availability = impactMetrics.service_availability;
const responseTime = impactMetrics.response_time_degradation;
if (availability < 70) return '์ฌ๊ฐํ ์๋น์ค ์ค๋จ์ผ๋ก ๋๋ถ๋ถ์ ์ฌ์ฉ์ ์ํฅ๋ฐ์';
if (availability < 90) return '์๋นํ ์๋น์ค ์ ํ๋ก ๋ง์ ์ฌ์ฉ์ ์ํฅ๋ฐ์';
if (responseTime > 50) return '์๋น์ค ์๋ต ์๊ฐ ์ง์ฐ์ผ๋ก ์ฌ์ฉ์ ๊ฒฝํ ์ ํ';
return '์ผ๋ถ ์ฌ์ฉ์๋ง ์ํฅ๋ฐ์ ๊ฒฝ๋ฏธํ ์๋น์ค ์ง์ฐ';
}
// ์ธ๊ณผ๊ด๊ณ ์ฒด์ธ ์ต์ ํ
optimizeCausalChain(causalChain) {
if (causalChain.length <= 1) return causalChain;
// ์ค๋ณต ์ด๋ฒคํธ ์ ๊ฑฐ
const uniqueChain = [];
const eventIds = new Set();
causalChain.forEach(segment => {
const eventId = `${segment.trigger_event.server_id}:${segment.trigger_event.event_type}`;
if (!eventIds.has(eventId)) {
eventIds.add(eventId);
uniqueChain.push(segment);
}
});
// ์ ๋ขฐ๋ ๊ธฐ๋ฐ ์ ๋ ฌ
uniqueChain.sort((a, b) => {
// ์ฐ์ ์๊ฐ์
const timeA = new Date(a.trigger_event.timestamp);
const timeB = new Date(b.trigger_event.timestamp);
if (timeA - timeB !== 0) return timeA - timeB;
// ๋์ผ ์๊ฐ์ด๋ฉด ์ ๋ขฐ๋์
return b.confidence_score - a.confidence_score;
});
return uniqueChain;
}
// ์๋ก ์ถ๊ฐ๋ ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ค
calculateDuration(start, end) {
const diffMs = new Date(end) - new Date(start);
const hours = Math.floor(diffMs / (1000 * 60 * 60));
const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
if (hours > 0) {
return `${hours}์๊ฐ ${minutes}๋ถ`;
} else {
return `${minutes}๋ถ`;
}
}
translateEventType(eventType) {
const translations = {
'high_cpu': 'CPU ์ฌ์ฉ๋ฅ ๊ณผ๋ถํ',
'memory_pressure': '๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ',
'disk_full': '๋์คํฌ ์ฉ๋ ๋ถ์กฑ',
'network_latency': '๋คํธ์ํฌ ์ง์ฐ',
'connection_timeout': '์ฐ๊ฒฐ ํ์์์',
'service_unavailable': '์๋น์ค ์ค๋จ'
};
return translations[eventType] || eventType;
}
generateSymptomDescription(type, servers, events) {
const serverCount = servers.length;
const eventCount = events.length;
const eventType = this.translateEventType(type);
if (serverCount === 1) {
return `${servers[0]} ์๋ฒ์์ ${eventType} ํ์์ด ${eventCount}ํ ๋ฐ์`;
} else {
return `${serverCount}๋ ์๋ฒ์์ ${eventType} ํ์์ด ์ด ${eventCount}ํ ๋ฐ์`;
}
}
classifySystemType(serverId) {
if (serverId.includes('k8s-')) return 'Kubernetes';
if (serverId.includes('db-')) return 'Database';
if (serverId.includes('web-')) return 'Web Server';
if (serverId.includes('cache-')) return 'Cache';
if (serverId.includes('api-')) return 'API';
if (serverId.includes('app-')) return 'Application';
return 'Unknown';
}
getSystemCriticality(systemType) {
const criticalityMap = {
'Database': 'critical',
'Kubernetes': 'high',
'API': 'high',
'Application': 'medium',
'Web Server': 'medium',
'Cache': 'low'
};
return criticalityMap[systemType] || 'medium';
}
analyzePropagationPath(whatHappened) {
const allServers = [];
const allSymptoms = [];
whatHappened.forEach(incident => {
incident.primary_symptoms.forEach(symptom => {
allSymptoms.push({
type: symptom.type,
servers: symptom.affected_servers,
time: incident.timeframe.split(' ~ ')[0]
});
allServers.push(...symptom.affected_servers);
});
});
// ์๊ฐ์์ผ๋ก ์ ๋ ฌ
allSymptoms.sort((a, b) => new Date(a.time) - new Date(b.time));
return allSymptoms.map(symptom => ({
time: symptom.time,
symptom_type: symptom.type,
servers: symptom.servers
}));
}
inferImmediateCause(firstSymptom) {
// ์ง์ ์ ์์ธ ์ถ๋ก
const symptomCauses = {
'CPU ์ฌ์ฉ๋ฅ ๊ณผ๋ถํ': {
cause: 'CPU ์์ ๊ฒฝ์',
explanation: '๊ณ ๋ถํ ํ๋ก์ธ์ค๋ก ์ธํ CPU ์ฌ์ฉ๋ฅ ๊ธ์ฆ'
},
'๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ': {
cause: '๋ฉ๋ชจ๋ฆฌ ๋ฆฌ์์ค ๊ณ ๊ฐ',
explanation: '๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋ฌผ๋ฆฌ์ ํ๊ณ์ ๋๋ฌ'
},
'๋คํธ์ํฌ ์ง์ฐ': {
cause: '๋คํธ์ํฌ ํผ์ก',
explanation: '๊ณผ๋ํ ํธ๋ํฝ ๋๋ ๋คํธ์ํฌ ๊ตฌ์ฑ ๋ฌธ์ '
}
};
return symptomCauses[firstSymptom.type] || {
cause: '์์คํ
๋ฆฌ์์ค ์ด์',
explanation: '์ ํํ ์์ธ ์ถ๊ฐ ์กฐ์ฌ ํ์'
};
}
identifyContributingFactors(whatHappened) {
const factors = [];
// ๋ค์ํ ์ฆ์ ์ ํ ๋ถ์
const symptomTypes = new Set();
whatHappened.forEach(incident => {
incident.primary_symptoms.forEach(symptom => {
symptomTypes.add(symptom.type);
});
});
// ๋์๋ค๋ฐ์ ์ฆ์์ด ์๋ ๊ฒฝ์ฐ
if (symptomTypes.size > 1) {
factors.push({
factor: '๋ณตํฉ ์ด๋ฒคํธ ๋ฐ์',
significance: 'high',
explanation: `${symptomTypes.size}๊ฐ์ง ์ ํ์ ์ฆ์์ด ๋์๋ค๋ฐ์ ์ผ๋ก ๋ฐ์`
});
}
// ์ฆ์ ์ง์ ์๊ฐ์ด ๊ธด ๊ฒฝ์ฐ
const longestIncident = whatHappened.reduce((longest, current) => {
const currentDuration = this.parseDuration(current.duration);
const longestDuration = longest ? this.parseDuration(longest.duration) : 0;
return currentDuration > longestDuration ? current : longest;
}, null);
if (longestIncident && this.parseDuration(longestIncident.duration) > 30) {
factors.push({
factor: '์ฅ๊ธฐ ์ง์์ฑ ์ด๋ฒคํธ',
significance: 'medium',
explanation: `๊ฐ์ฅ ๊ธด ์ฅ์ ๊ฐ ${longestIncident.duration} ๋์ ์ง์๋จ`
});
}
return factors;
}
parseDuration(durationString) {
// "2์๊ฐ 30๋ถ" ๋๋ "45๋ถ" ํ์์ ๋ฌธ์์ด์์ ๋ถ ๋จ์๋ก ๋ณํ
const hourMatch = durationString.match(/(\d+)์๊ฐ/);
const minuteMatch = durationString.match(/(\d+)๋ถ/);
let totalMinutes = 0;
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
if (minuteMatch) totalMinutes += parseInt(minuteMatch[1]);
return totalMinutes;
}
calculateHypothesisConfidence(rootCauseAnalysis) {
if (!rootCauseAnalysis.underlying_cause) return 0;
let confidence = rootCauseAnalysis.underlying_cause.confidence || 0.5;
// ์ฌ๋ฌ ์์ธ ์กด์ฌ ์ ์ ๋ขฐ๋ ๊ฐ์
if (rootCauseAnalysis.contributing_factors.length > 1) {
confidence *= 0.9;
}
return Math.min(confidence, 1.0);
}
generateDefaultImmediateActions(analysis) {
const actions = [];
// ์ํฅ๋ฐ์ ์์คํ
๊ธฐ๋ฐ ๊ธฐ๋ณธ ์กฐ์น
if (analysis.where_occurred && analysis.where_occurred.affected_systems) {
analysis.where_occurred.affected_systems.forEach(system => {
switch (system.type) {
case 'Database':
actions.push('๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ ์ฌ์ค์ ');
actions.push('๊ธด ํธ๋์ญ์
ํ์ธ ๋ฐ ๋กค๋ฐฑ ๊ฒํ ');
break;
case 'Web Server':
actions.push('์น ์๋ฒ ํ๋ก์ธ์ค ์ฌ์์');
actions.push('๋ก๋ ๋ฐธ๋ฐ์ ์ํ ํ์ธ');
break;
case 'Kubernetes':
actions.push('๋ฌธ์ ํ๋ ์ฌ์์');
actions.push('๋
ธ๋ ์ํ ํ์ธ');
break;
}
});
}
// ๊ธฐ๋ณธ ๋์ ์กฐ์น
if (actions.length === 0) {
actions.push('์ํฅ๋ฐ์ ์๋น์ค ์ํ ์ ๊ฒ');
actions.push('๋ฌธ์ ์๋ฒ ์ฌ๋ถํ
๊ณ ๋ ค');
actions.push('์ถ๊ฐ ๋ชจ๋ํฐ๋ง ๊ฐํ');
}
return actions;
}
}
// ์ด๋ฒคํธ ์๊ด๊ด๊ณ ๋ถ์๊ธฐ
class EventCorrelator {
correlateEvents(events) {
// ์ด๋ฒคํธ ๊ฐ ์๊ด๊ด๊ณ ๋ถ์ ๋ก์ง
return [];
}
}
// ์ธ๊ณผ๊ด๊ณ ์ฒด์ธ ํ์ง๊ธฐ
class CausalChainDetector {
detectCausalChains(correlatedEvents) {
// ์ธ๊ณผ๊ด๊ณ ์ฒด์ธ ํ์ง ๋ก์ง
return [];
}
}
// ๋๋ฏธ ํด๋์ค (์ค์ ๊ตฌํ์์๋ ๊ต์ฒด)
class DummyDataGenerator {
generateTimeSeriesForPeriod(startTime, endTime) {
// ๋๋ฏธ ํ์์๋ฆฌ์ฆ ์์ฑ ๋ก์ง
return {};
}
}
// ์๊ฐ ๋ถ์ ๋ฆฌํฌํธ ์์ฑ๊ธฐ
class AutoReportGenerator {
generateReport(analysis) {
// ๋ฆฌํฌํธ ์์ฑ ๋ก์ง
return {};
}
}
// ์ด์ ์ง์๋ฒ ์ด์ค (๋ณ๋ ํ์ผ๋ก ๊ตฌํ๋จ)
class OperationalKnowledgeBase {
constructor() {
// ์ค์ ๊ตฌํ์ ๋ณ๋ ํ์ผ์ ์์
}
}