feat(site): 站点拨测支持接口返回内容监控(文本 + JSON 字段)
需求:监控接口返回的数据本身是否变化(如 Cloudflare ips-v4 的 IP 段清单),
而不是只看服务是否可用。两者是不同的问题:服务 200 OK 但内容变了,拨测发现不了。
后端
- SiteTask 新增 content_check_mode(off/text/json)、content_paths(JSONPath)、
content_ignore_rules、content_min_length、content_confirm_times、content_sort_lines
- 新增 monitor_site_baselines(基线多版本)与 monitor_site_content_changes(变更记录)
- pkg/siteprobe/content.go:零依赖 JSONPath($.a.b[0]、$.a[*].b)、文本/JSON 规范化、
sha256 哈希、LCS 行级 diff、字段级 diff
- 核心判定区分「采集异常」与「内容变更」:状态码非 2xx、空响应、短于最小长度、
非合法 JSON 均不算变更,避免 CDN 拦截页造成误报
- 连续 N 次(默认 2)不一致才落变更记录,抑制抖动
- 基线首次自动建立(用户选择自动接受),之后变更需人工「接受为新基线」并留审计
- 补齐历史遗留死字段:expected_content(正则断言)与 expected_length 此前
只有表结构、执行器从未校验,现已实现
- 告警引擎新增 content_changed 指标,人工处理后自动恢复
- 迁移 0006 用 AutoMigrate 实现幂等(空库 0000 已建表,静态 ALTER 会重复列)
前端
- 任务表单新增内容监控配置区(模式/JSONPath/忽略规则/最小长度/确认次数/排序)
- 详情页新增内容监控面板:当前基线、待确认变更、并排 diff、版本历史、
接受/忽略操作
验证
- 9 项单元测试(JSONPath、时间戳忽略、顺序无关、采集异常、字段级监控、diff)
- 端到端:Cloudflare ips-v4 建基线 v1 → 模拟变更 → 第 2 轮告警 → 接受为 v2
- JSON 模式实测快照仅含指定字段:{"$.db_driver":"mysql","$.version":"v1.0.2"}
// Logan.Li
This commit is contained in:
1 parent
286d7cd906
commit
539db36f2a
18 files changed
+1704
-3
No files matched your search
@@ -162,6 +162,20 @@ func (e *AlertEngine) evalSiteRule(rule model.MonitorAlertRule, task model.SiteT
|
||||
if triggered {
|
||||
msg = fmt.Sprintf("拨测 %s HTTP 状态码异常", task.Name)
|
||||
}
|
||||
case "content_changed":
|
||||
// 内容监控:服务可达但返回数据变了,与"拨测失败"是完全不同的问题。 // Logan.Li
|
||||
if !siteprobe.ContentCheckEnabled(task.ContentCheckMode) {
|
||||
return
|
||||
}
|
||||
var pending int64
|
||||
e.db.Model(&model.SiteContentChange{}).
|
||||
Where("task_id = ? AND status = ?", task.ID, model.ContentChangePending).
|
||||
Count(&pending)
|
||||
triggered = pending > 0
|
||||
value = float64(pending)
|
||||
if triggered {
|
||||
msg = fmt.Sprintf("接口 %s 返回内容已变更,待确认 %d 项", task.Name, pending)
|
||||
}
|
||||
case "ssl_expiry_days":
|
||||
if !siteprobe.IsSSLProbeTask(task) {
|
||||
return
|
||||
@@ -318,6 +332,16 @@ func (e *AlertEngine) siteRuleStillTriggered(rule model.MonitorAlertRule, task m
|
||||
return e.compareOperator(rule.Operator, task.AvgResponse, rule.Threshold)
|
||||
case "availability":
|
||||
return e.compareOperator(rule.Operator, task.SuccessRate, rule.Threshold)
|
||||
case "content_changed":
|
||||
// 仍有待确认变更 → 告警持续;人工接受/忽略后自动恢复。 // Logan.Li
|
||||
if !siteprobe.ContentCheckEnabled(task.ContentCheckMode) {
|
||||
return false
|
||||
}
|
||||
var pending int64
|
||||
e.db.Model(&model.SiteContentChange{}).
|
||||
Where("task_id = ? AND status = ?", task.ID, model.ContentChangePending).
|
||||
Count(&pending)
|
||||
return pending > 0
|
||||
case "ssl_expiry_days":
|
||||
if !siteprobe.IsSSLProbeTask(task) {
|
||||
return false
|
||||
|
||||
Reference in new issue
Block a user