fix(monitor): 监控班次二 P2 批——D-5 db_sample 日历不可达去 fail-open(样本可读弱检/库不可达黄)+D-6 month_bucket 损坏件 rows=None 黄(对齐 per_date)+D-8 check_disk 阈值参数化消费注册表 warn_gb/crit_gb(旧硬编码 8/20=改表不改行为校准陷阱,crit_gb 全仓无消费)+D-9 vintage/NAS ts fromisoformat 捕 TypeError+D-10 db_sample close 进 finally+AL-1 upsert 更新 severity(同日黄→红升级即时落表,旧 UPDATE 不动=次日新 id 才纠正)+purge 收口 MON-202610-04(open 黄改 last_seen 28d=活着不删,旧 first_seen 14d 硬删无声抹持续黄+断 count 升级链;resolved 统一 30d,open 红永不删)+N-1 run_monitor_nas.sh rc 先存后拼($(date) 同词先展开重置 $?=exit 恒 0,rc 隔离下 NAS 崩溃唯一现场线索)+N-2 NAS 日历豁免短期方案(last_weekday 接休市表镜像+CI 同步测钉与 monitor_registry 相等=7f9b413 镜像纪律;10-06 旧码会把 10-05 假日当昨交易日;corpus 34h 日历日节奏实证无需豁免)+P3 归因([FATAL] 级别标记不当类型归 _global,scan_signatures 注记 test-only 勿接线)+T-2 NAS red 分支(hot_rank 文件缺)测试补缺+migration_logs 保洁(data_monitor_wrapper 双班剪 14d 前旧件,13 wrapper 无限增长收口);monitoring spec §5.1/§5.2/§5.3-④/§5.4/§5.5-④/§7.6/§12 随码(C-A1 universe 异常实串/C-A2 签名表勘正补 Expecting value/C-A6 disk 阈值表述+锚点行数刷新) [vps] [nas]
CI/CD / test (push) Successful in 45s
CI/CD / nas-deploy (push) Has been cancelled
CI/CD / nas-verify (push) Has been cancelled

This commit is contained in:
2026-10-05 15:54:13 +08:00
parent a13ce79b93
commit b954b02e75
11 changed files with 232 additions and 49 deletions
+17 -8
View File
@@ -1,8 +1,10 @@
"""alerts 告警事件存储(spec §20.15.4)——监控脚本单写、sanguo_api 只读+ack、NAS 侧经 inbox 摄取.
事件模型: 确定性 alert_id 幂等去重(重 Sight=同 id 再 upsert → count++ 即「第 N 晚」升级);
事件模型: 确定性 alert_id 幂等去重(重 Sight=同 id 再 upsert → count++ 即「第 N 晚」升级;
severity 跟随最新评估——同日黄→红升级即时落表, AL-1 10-05);
check_key(去日期维度)驱动转绿自动 resolved;人工只 ack 不 resolved。
保留期: resolved 30 天 / 黄项 14 天, 每班 purge。
保留期: resolved 30 天 / open 黄按 last_seen 28 天(MON-202610-04 收口 10-05:
活着不删) / open 红不删, 每班 purge。
"""
import json
import os
@@ -55,15 +57,18 @@ def _now() -> str:
def upsert_alert(conn: sqlite3.Connection, event: dict) -> str:
"""幂等写入一条告警事件; 重 Sight(同 alert_id 仍 open/acked)→count++/last_seen 刷新."""
"""幂等写入一条告警事件; 重 Sight(同 alert_id 仍 open/acked)→count++/
last_seen 刷新/severity 跟随最新评估(AL-1 10-05: 同日黄→红升级不再丢,
旧版 UPDATE 不动 severity=次日新 id 才纠正, open_red 角标少计一晚)."""
now = _now()
alert_id = event["alert_id"]
cur = conn.execute(
"UPDATE alerts SET count=count+1, last_seen=?, detail=?, evidence=?, title=? "
"UPDATE alerts SET count=count+1, last_seen=?, detail=?, evidence=?, "
"title=?, severity=? "
"WHERE alert_id=? AND status IN ('open','acked')",
(now, event.get("detail", ""),
json.dumps(event.get("evidence", {}), ensure_ascii=False),
event["title"], alert_id))
event["title"], event["severity"], alert_id))
if cur.rowcount == 0:
exists = conn.execute(
"SELECT 1 FROM alerts WHERE alert_id=?", (alert_id,)).fetchone()
@@ -94,14 +99,18 @@ def resolve_passed(conn: sqlite3.Connection, passed_keys, now: str = None) -> in
def purge_expired(conn: sqlite3.Connection, now: str = None) -> int:
"""resolved 留 30 天 / 黄项留 14 天(spec §20.15.4)."""
"""resolved 留 30 天 / open 黄按 last_seen 留 28 天(MON-202610-04 收口
10-05: 旧 first_seen 14d 硬删把每日重 Sight 的持续黄无声从值班页抹掉+
count 升级链断——last_seen 每次重 Sight 刷新=「活着不删」, 僵死 28d 才清;
resolved 黄统一走 resolved_at 30d, open 红永不删)."""
now = now or _now()
t = datetime.fromisoformat(now)
cut_resolved = (t - timedelta(days=30)).isoformat(timespec="seconds")
cut_yellow = (t - timedelta(days=14)).isoformat(timespec="seconds")
cut_open_yellow = (t - timedelta(days=28)).isoformat(timespec="seconds")
n = conn.execute(
"DELETE FROM alerts WHERE (status='resolved' AND resolved_at<?)"
" OR (severity='yellow' AND first_seen<?)", (cut_resolved, cut_yellow)
" OR (status IN ('open','acked') AND severity='yellow'"
" AND last_seen<?)", (cut_resolved, cut_open_yellow)
).rowcount
conn.commit()
return n