Files
SenMeshNet/electron/bridges/inspection/alertStateMonitor.cjs
zhaolei a5744e720b
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
feat(plan-management): implement alert-remediation-plan system v1.3
- 8 new store modules + matcher + initialResponse + aggregation + remediation + AlertStateMonitor + planGenerator

- alert chain auto-integrated after runInspection: freeze snapshot + generate response + real-time agg + auto-start monitor

- AlertAIPanel component + MonitoringDetailPanel expandable alert list

- PlanManagementPage: full page with list/edit/AI generate/templates/commands
2026-10-30 16:55:55 +08:00

152 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
/**
* AlertStateMonitor(D23:每 5min 定时扫活跃告警)
*
* 设计:
* - 现有 alertAggregationEngine 是"事件驱动"(来一条新告警检查一次),
* 不会主动扫存量告警的超时情况
* - AlertStateMonitor 每 5min 跑一次,检查:
* ① 告警挂了多久 → 超 escalation_fallback.auto_escalate_minutes → 自动升级
* ② remediation.status === "running" 超时 → 暂停处置 + 通知值班员
* ③ remediation.status === "completed" → 检查 auto_close_after_remediation_recovery
*/
const _CHECK_INTERVAL_MS = 5 * 60 * 1000; // 5 分钟
const _REMEDIATION_TIMEOUT_MS = 30 * 60 * 1000; // 30 分钟硬阈值
let _timer = null;
let _isRunning = false;
/**
* 启动定时监控(应在巡检模块初始化时调用一次)
* @param {object} deps - 依赖注入
* @param {function} deps.readFn - 存储读取函数
* @param {function} deps.writeFn - 存储写入函数
* @param {object} deps.alertRecordStore - alertRecordStore 模块
* @param {object} deps.aggregationEngine - aggregationEngine 模块
* @param {object} deps.remediationEngine - remediationEngine 模块
* @param {function} [deps.onNotification] - 通知回调
*/
function start(deps) {
if (_timer) return; // 已启动
const { readFn, writeFn, alertRecordStore, aggregationEngine, remediationEngine, onNotification } = deps;
async function run() {
if (_isRunning) return; // 避免重叠
_isRunning = true;
try {
const activeAlerts = alertRecordStore.listAlerts(readFn, { limit: 500 });
const eligibleAlerts = activeAlerts.filter(
(a) => a.status === "active" || a.status === "acknowledged"
);
for (const alert of eligibleAlerts) {
await _checkOne(alert, { readFn, writeFn, aggregationEngine, remediationEngine, alertRecordStore, onNotification });
}
} catch (e) {
console.error("[AlertStateMonitor] run error:", e.message);
} finally {
_isRunning = false;
}
}
_timer = setInterval(run, _CHECK_INTERVAL_MS);
console.info("[AlertStateMonitor] 已启动,每 5 分钟检查一次活跃告警");
// 立即跑一次
run();
}
function stop() {
if (_timer) {
clearInterval(_timer);
_timer = null;
console.info("[AlertStateMonitor] 已停止");
}
}
function isRunning() { return !!_timer; }
async function _checkOne(alert, deps) {
const { readFn, writeFn, aggregationEngine, remediationEngine, alertRecordStore, onNotification } = deps;
const snapshot = alert.matched_plan_snapshot;
if (!snapshot) return; // 没匹配预案的不处理
const now = Date.now();
const elapsedMin = (now - alert.triggered_at) / 60000;
// ── 检查 1:escalation_fallback.auto_escalate_minutes ──
const fallback = snapshot.escalation_fallback;
if (fallback?.auto_escalate_minutes && elapsedMin >= fallback.auto_escalate_minutes) {
// 避免重复升级:同一条告警只升级一次
const alreadyEscalated = (alert.audit_log || []).some(
(a) => a.action === "escalation_fallback_timeout" || a.action === "aggregation_escalation"
);
if (!alreadyEscalated) {
console.warn(`[AlertStateMonitor] 告警 ${alert.id} 挂了 ${elapsedMin.toFixed(0)}min,触发自动升级`);
aggregationEngine.escalateAlert(readFn, writeFn, "escalation_fallback_timeout", {
source_alert_ids: [alert.id],
target_level: fallback.escalation_target_level || alert.level,
message: `告警挂 ${elapsedMin.toFixed(0)}min 未处置,自动升级`,
});
// 追加审计
_appendAudit(alertRecordStore, readFn, writeFn, alert.id, {
ts: now, actor: "alertStateMonitor", action: "escalation_fallback_timeout",
detail: `elapsed=${elapsedMin.toFixed(0)}min`,
});
onNotification?.({ type: "escalation", alertId: alert.id });
}
}
// ── 检查 2:remediation 运行超时 ──
const remediation = alert.remediation;
if (remediation?.status === "running") {
const runningFor = now - (remediation.started_at || 0);
if (runningFor > _REMEDIATION_TIMEOUT_MS) {
console.warn(`[AlertStateMonitor] 告警 ${alert.id} 处置运行 ${(runningFor / 60000).toFixed(0)}min,超时暂停`);
remediationEngine.pause(alert.id);
_appendAudit(alertRecordStore, readFn, writeFn, alert.id, {
ts: now, actor: "alertStateMonitor", action: "remediation_timeout",
detail: `running=${(runningFor / 60000).toFixed(0)}min,自动暂停`,
});
onNotification?.({ type: "remediation_timeout", alertId: alert.id });
}
}
}
function _appendAudit(alertRecordStore, readFn, writeFn, alertId, auditEntry) {
try {
// 直接读写底层存储(避免 alertRecordStore 暴露内部方法)
const raw = readFn("netcatty_inspection_alert_records_v1");
const alerts = raw ? (typeof raw === "string" ? JSON.parse(raw) : raw) : [];
const idx = alerts.findIndex((a) => a.id === alertId);
if (idx >= 0) {
alerts[idx].audit_log = [...(alerts[idx].audit_log || []), auditEntry];
writeFn("netcatty_inspection_alert_records_v1", JSON.stringify(alerts));
}
} catch (e) {
console.warn("[AlertStateMonitor] 追加审计失败:", e.message);
}
}
function getStatus() {
return {
running: isRunning(),
interval_minutes: _CHECK_INTERVAL_MS / 60000,
remediation_timeout_minutes: _REMEDIATION_TIMEOUT_MS / 60000,
};
}
module.exports = {
start,
stop,
isRunning,
getStatus,
// 测试用
_CHECK_INTERVAL_MS,
_REMEDIATION_TIMEOUT_MS,
};