一个 Go 单二进制 的轻量自托管网站监测、日志收集与告警通知系统:定时探测网站可用性,收集外部站点上报的日志与异常,通过规则引擎发现异常(离线 / 响应变慢 / 日志爆发),调用 大语言模型自动诊断根因并给出处理决策,最后通过 邮件 / Webhook(飞书、钉钉、企微) 通知到人。
内置 免登录公开状态页(团队随时可见)、Web 管理后台(Vue 3 SPA,无需构建步骤)、AI 聊天助手(可结合系统内数据上下文问答)。
- 网站状态监测
- 两种探测类型:HTTP/HTTPS 网站(状态码 / 关键词断言)与 TCP 端口(数据库、内部 API 等
host:port连通性探测) - 多目标管理:URL、期望状态码、关键词断言、超时、探测间隔(30s~1h)、独立通知邮箱、恢复通知开关
- 状态机
up / down / unknown,原子翻转判定,故障期间不重复告警 - 探测历史、响应时间曲线、24h 可用率统计;手动「立即检查」
- 两种探测类型:HTTP/HTTPS 网站(状态码 / 关键词断言)与 TCP 端口(数据库、内部 API 等
- SSL 证书到期提醒
- 每次 HTTPS 探测自动读取证书到期日,剩余天数 ≤ 阈值(默认 7 天,可调 1-90)生成告警;已过期升级为严重级
- 目标列表 / 详情页实时显示证书剩余天数;同目标 24h 内不重复告警
- 维护模式(静默告警)
- 计划内变更 / 升级时一键进入维护(1/4/8/24 小时,最长 30 天):期间照常探测并记录历史,但不产生任何告警,状态显示「维护中」
- 到期自动恢复,也可提前手动结束;公开状态页同步显示维护标记
- 状态徽章(可嵌入 SVG)
- 每个公开目标生成 shields 风格实时徽章:
<img src="https://your-domain/api/public/badge/1"> - 显示「在线 · 24h 可用率 / 离线 / 维护中」,60 秒缓存、无需登录,可直接放进你的网站、项目主页或 README;目标详情页提供一键复制嵌入代码
- 每个公开目标生成 shields 风格实时徽章:
- 异常检测(规则引擎)
- 网站离线(up→down 翻转即告警,恢复时自动关闭对应离线异常)
- 响应时间突增(最近一次 > 24h 均值 × 系数 且 > 2s)
- 日志错误爆发(某来源 10 分钟内 error/fatal 超阈值,30 分钟冷却去重)
- 关联故障检测:同一用户的多个目标在近 10 分钟内同时离线时,自动在离线异常详情中追加关联提示(列出同时离线的目标),帮助快速定位「同一服务器 / 网络链路 / CDN / DNS」等共同原因
- 外部站点主动上报异常(token 鉴权 API)
- AI 能力(OpenAI 兼容接口)
- 自动诊断:异常产生时自动拼装上下文(探测记录 + 最近错误日志)调用 LLM,输出中文根因分析与修复建议,随告警邮件一起发出
- AI 自动决策:诊断报告末尾固定给出
DECISION: auto_resolve / watch / manual:auto_resolve:AI 判定可自动解决(如目标已恢复、一次性抖动)→ 异常自动关闭,状态页显示「AI 自动解决」watch:影响有限,继续观察manual:仍需人工处理- 安全兜底:离线类异常仅当目标当前确实在线才允许自动关闭,防止误关仍在故障的站点;后台可一键停用
- 恢复事件自动闭环:「网站已恢复」属信息性事件,创建即自动关闭,不再占用待处理列表(恢复邮件照常发送)
- AI 助手:后台聊天面板,会话持久化,可勾选最近的异常 / 日志作为上下文提问
- AI 智能报告(日报 / 周报):一键生成监测运营报告。系统自动采集统计窗口内的各目标可用率 / 平均响应、证书风险、异常事件、日志级别分布与 Top 错误来源,交由 LLM 撰写结构化中文报告(总体概览 / 各目标表现表 / 异常与事件分析 / 错误日志分析 / 按优先级的风险与建议);报告入库持久化,可在后台「智能报告」页查看、删除、一键发送到默认通知邮箱;支持管理员设置每日自动生成日报(开关 + 时间点,由调度器触发并自动发邮件)
- 日志收集(跨站点)
- 每用户可生成多个上报令牌,外部网站
POST /api/v1/logs上报(level / message / context) - 日志中心:级别 / 来源 / 关键字 / 时间范围筛选、来源统计、上下文查看
- 日志智能聚类分析:对近 7 天 warn/error/fatal 日志做消息归一化(URL / IP / 十六进制 / 路径 / 数字掩码),按「来源 + 归一化特征」聚类成故障模式,按出现次数排序展示(最多 20 组);可一键把 Top 模式交给 AI 助手做根因分析,快速定位「看起来不同、其实是同一问题」的重复错误
- 每用户可生成多个上报令牌,外部网站
- 通知
- 邮件:自实现轻量 SMTP 客户端(SSL / STARTTLS),multipart HTML 模板,发信限速保护(超限自动排队重发),全程审计日志
- Webhook:飞书 / 钉钉 / 企微 / 自定义机器人,随异常推送诊断摘要
- 通知对象:目标级邮箱列表 → 全局默认邮箱,逐级回退
- 公开状态页
- 免登录状态总览(
/),目标图标服务端代理(绕过跨域 / 防盗链限制,内置 SSRF 防护)
- 免登录状态总览(
- 用户系统
- 登录(bcrypt + 失败锁定 + Cookie 会话)、管理员 / 普通用户角色、多租户数据隔离(user_id)
| 层 | 技术 |
|---|---|
| 后端 | Go + Gin(仅 3 个核心依赖:gin / go-sql-driver / x-crypto),交叉编译单二进制、无 CGO |
| 前端 | Vue 3 + Naive UI + Chart.js + marked,静态文件本地化,无构建步骤,由 Go 直接托管 |
| 数据库 | MySQL 5.7+ / 8.x(应用启动自动迁移,无需手工建表) |
| LLM | 任意 OpenAI 兼容接口(后台可改端点 / key / 模型) |
| 部署 | systemd + Nginx 反代(或任意反代),服务器无需 Go 工具链 |
.
├── README.md
├── docs/大纲.md # 设计调研与系统大纲
└── sentinel/
├── main.go # 入口:加载配置 → 迁移 → Seed → 路由 → 调度器
├── config.example.json # 配置示例(复制为 config.json 使用)
├── go.mod / go.sum
├── internal/
│ ├── api/ # 路由、鉴权、各模块 Handler(含 /api/v1 外部上报)
│ ├── auth/ # 用户 / 会话 / 上报令牌 / bcrypt
│ ├── config/ # 配置加载(config.json + 环境变量覆盖)
│ ├── store/ # MySQL 连接、自动迁移、默认设置
│ ├── monitor/ # HTTP 探测引擎(状态码 / 关键词 / 超时)
│ ├── detector/ # 异常规则引擎 + AI 诊断 + 自动决策
│ ├── llm/ # OpenAI 兼容客户端(诊断 / 聊天 / 决策解析 / 报告撰写)
│ ├── report/ # AI 智能报告(数据采集 → LLM 生成 → 邮件发送)
│ ├── mailer/ # SMTP 客户端 + 限速队列 + HTML 模板
│ ├── webhook/ # 飞书 / 钉钉 / 企微 / 自定义
│ └── scheduler/ # 进程内定时器(探测 → 检测 → 诊断 → 发信 → 清理)
└── web/ # 前端静态文件
├── status.html # 公开状态页(/)
├── index.html # 管理后台 SPA(/admin)
└── assets/ # app.js / style.css / vendor
- Go 1.21+(仅构建时需要)
- MySQL 5.7+,创建一个空数据库
cd sentinel
cp config.example.json config.json
# 编辑 config.json,填入数据库 DSN 与监听地址
# LLM API Key 可写在 config.json 的 llm_api_key,或用环境变量 SENTINEL_LLM_API_KEY
# 两者都留空也可:首次启动后在后台「通知与设置 → AI 模型」里配置# 本机开发
go run .
# 生产构建(linux/amd64 单二进制)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o sentinel .
./sentinel -config /etc/sentinel/config.json首次启动自动完成:建表迁移、写入默认设置、创建初始管理员 admin / admin123(登录后请立即修改密码)。
- 管理后台:
https://your-domain/admin - 公开状态页:
https://your-domain/
config.json(敏感项可用环境变量覆盖):
| 字段 | 说明 | 默认值 |
|---|---|---|
app_name |
应用名称(邮件 / 页面标题) | SiteSentry 哨兵 |
base_url |
对外访问的公网地址(邮件内链接用) | http://127.0.0.1:33330 |
listen |
Go 服务监听地址 | 127.0.0.1:33330 |
db_dsn |
MySQL DSN user:pass@tcp(host:port)/db?charset=utf8mb4&parseTime=true&loc=Local |
— |
check_tick_sec |
调度器 tick 周期(秒) | 15 |
llm_api_key |
LLM Key(推荐改用环境变量) | 空 |
环境变量:SENTINEL_LLM_API_KEY(优先级高于 config.json)。
运行时设置(管理后台「通知与设置」修改,存于 settings 表,无需重启):
| 键 | 说明 |
|---|---|
llm_base_url / llm_api_key / llm_model / llm_enabled |
OpenAI 兼容端点、Key、模型名、AI 总开关 |
ai_auto_resolve |
AI 自动决策开关:开启后 AI 判定 auto_resolve 的异常自动关闭 |
smtp_host / smtp_port / smtp_mode / smtp_user / smtp_pass / smtp_from_name |
SMTP 通道(ssl 或 starttls) |
default_notify_emails |
全局默认通知邮箱(逗号分隔) |
notify_enabled |
异常告警邮件通知总开关(1 开启 / 0 暂停):关闭后所有异常(离线 / 变慢 / 证书 / 日志爆发等)不再发送告警邮件;AI 诊断、异常记录与 Webhook 不受影响,重新开启后照常发送 |
log_burst_threshold / latency_multiplier |
日志爆发阈值(条/10min)、慢响应倍数 |
ssl_warn_days |
SSL 证书到期提前告警天数(1-90,默认 7;已过期为严重级) |
report_auto / report_hour |
AI 日报自动生成:开关(0/1)与每日生成时间点(0-23 点,到点后由调度器生成日报并发邮件,每天最多一次) |
webhook_type / webhook_url |
Webhook 渠道(feishu / dingtalk / wechat / custom)与地址 |
# /etc/systemd/system/sentinel.service
[Unit]
Description=SiteSentry sentinel
After=network.target mysql.service
[Service]
Type=simple
WorkingDirectory=/opt/sentinel
ExecStart=/opt/sentinel/sentinel -config /opt/sentinel/config.json
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload && systemctl enable --now sentinelserver {
listen 443 ssl http2;
server_name status.example.com;
# 证书...
location /api/ {
proxy_pass http://127.0.0.1:33330;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 180s; # LLM 诊断可能较慢
}
location / {
proxy_pass http://127.0.0.1:33330;
proxy_set_header Host $host;
}
}路由约定:
/返回公开状态页;/admin(及子路径)返回管理后台 SPA;两者共用/assets/*静态资源与/api/*接口。
统一返回格式:{"ok": true, "data": ...} 或 {"ok": false, "error": "..."}。
- Web 端:
POST /api/auth/login后使用 Cookie 会话 - 外部上报:
Authorization: Bearer <token>(后台「上报令牌」生成)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/auth/register |
注册(首个用户自动成为 admin) |
| POST | /api/auth/login |
登录 {username, password} |
| POST | /api/auth/logout |
退出登录 |
| GET | /api/auth/me |
当前用户 |
| POST | /api/auth/password |
修改密码 {old_password, new_password} |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/targets |
目标列表(含 24h 可用率、平均耗时等统计) |
| POST | /api/targets |
新建目标 |
| PUT | /api/targets/:id |
更新(名称 / 类型 / URL / 期望状态码 / 关键词 / 间隔 / 超时 / 通知邮箱 / 恢复通知 / 图标 / 公开开关) |
| DELETE | /api/targets/:id |
删除(级联删除探测历史) |
| POST | /api/targets/:id/check |
立即检查(一次探测并即时评估异常) |
| GET | /api/targets/:id/history |
探测历史 ?hours=24 |
| POST | /api/targets/:id/maintenance |
进入维护模式 {hours: 4} 或 {until: "2026-09-05 18:00"}(1 分钟~30 天;期间只记录探测、不告警) |
| DELETE | /api/targets/:id/maintenance |
提前结束维护模式 |
目标字段示例(target_type 省略时默认 http):
{
"name": "官网", "target_type": "http", "url": "https://example.com/",
"expect_status": 200, "keyword": "",
"interval_sec": 60, "timeout_sec": 10,
"notify_emails": "", "notify_recovery": 1,
"public": 1, "icon": "https://example.com/favicon.ico"
}TCP 端口目标(只做连通性探测,url 字段填 host:port,状态码/关键词不适用):
{ "name": "MySQL", "target_type": "tcp", "url": "db.internal:3306", "interval_sec": 60, "timeout_sec": 5 }| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/logs |
日志列表 ?level=&source=&keyword=&from=&to=&page=&size= |
| GET | /api/logs/sources |
来源统计 |
| GET | /api/logs/insights |
日志智能聚类:近 7 天 warn/error/fatal 归一化后的故障模式分组 {scanned, groups:[{signature, sample, count, levels, sources, first_at, last_at}]}(按次数倒序,最多 20 组) |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/anomalies |
异常列表 ?status=&type=&severity=&page=&size= |
| GET | /api/anomalies/:id |
详情(含 AI 诊断、AI 决策、最近探测) |
| POST | /api/anomalies/:id/resolve |
手动标记已处理 |
| POST | /api/anomalies/:id/rediagnose |
重新 AI 诊断(会重新执行自动决策) |
type 取值:check_down(网站离线)/ check_recovery(网站恢复,信息性事件自动关闭)/ latency_spike(响应变慢)/ log_burst(日志爆发)/ cert_expiring(SSL 证书到期/过期)/ external(外部上报)。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET/POST | /api/ai/conversations |
会话列表 / 新建 |
| DELETE | /api/ai/conversations/:id |
删除会话 |
| GET | /api/ai/conversations/:id/messages |
消息列表 |
| POST | /api/ai/conversations/:id/messages |
发送消息(可带异常 / 日志上下文) |
| POST | /api/ai/conversations/:id/messages/stream |
SSE 流式回复 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/reports |
报告列表(最近 50 条,含状态 / 是否已发邮件) |
| GET | /api/reports/:id |
报告详情(Markdown 正文 + 指标快照 metrics) |
| POST | /api/reports |
生成报告 {kind: "daily" | "weekly", send_email?: true};同类型已有「生成中」任务时直接复用,避免重复生成 |
| POST | /api/reports/:id/send |
把报告发送到默认通知邮箱(default_notify_emails) |
| DELETE | /api/reports/:id |
删除报告 |
报告状态流转:pending(LLM 生成中,约 20-60s)→ done / failed;正文为 Markdown,后台用 marked 渲染,邮件为 HTML 版本。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/tokens |
令牌列表 |
| POST | /api/tokens |
创建 {name} |
| DELETE | /api/tokens/:id |
吊销 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/settings |
读取(普通用户可见只读子集) |
| POST | /api/settings |
保存(仅白名单键;smtp_pass 留空表示不修改) |
| POST | /api/settings/test-mail |
发送测试邮件 |
| POST | /api/settings/test-llm |
测试 LLM 连通性 |
| POST | /api/settings/test-webhook |
发送测试 Webhook |
GET/POST /api/users、PUT/DELETE /api/users/:id
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/public/status |
状态页数据(仅 public=1 的目标) |
| GET | /api/public/targets/:id |
单目标详情 |
| GET | /api/public/icon?url=<encoded> |
图标代理(服务端抓取,绕过目标站跨域 / 防盗链;内置 SSRF 防护,禁内网地址,限 1MB) |
| GET | /api/public/badge/:id |
状态徽章(shields 风格 SVG,60 秒缓存):在线 99.9%(绿)/ 离线(红)/ 维护中(灰) |
状态徽章嵌入:在任意支持 <img> 的页面(README、文档站、Wiki)中引用,无需登录:
<img src="https://your-domain/api/public/badge/1" alt="站点状态" />管理后台「目标详情」页可实时预览徽章并一键复制该代码。徽章地址中的域名请使用 base_url 设置的公网地址。
上报日志
curl -X POST https://status.example.com/api/v1/logs \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"source":"shop-api","level":"error","message":"支付回调超时","context":{"order_id":"123"}}'level:info / warn / error / fatal;error/fatal 计入日志爆发检测。
上报异常(直接进异常队列,触发 AI 诊断 + 通知)
curl -X POST https://status.example.com/api/v1/anomalies \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"source":"shop-api","severity":"critical","title":"支付网关不可用","detail":"连续 5 次 502"}'连通性自检:GET /api/v1/ping
前端自动注入(deploy/sentry.js):把网站的前端 JS 异常、未处理的 Promise 拒绝、关键资源加载失败自动上报到日志中心。脚本设计为零侵入 —— defer 异步加载、全程 try/catch、上报失败静默丢弃、单页最多 10 条且相同错误去重,上报接口挂了也不会影响主站访问。
nginx 注入方式(以 BT 面板为例):
server {
server_name example.com;
# ---- SiteSentry 前端错误上报 ----
sub_filter '</head>' '<script src="/sentry.js" defer></script></head>';
sub_filter_once on;
location / { gzip off; } # sub_filter 不处理 gzip 响应;反代站点需加 proxy_set_header Accept-Encoding "";
}把 deploy/sentry.js(替换 <REPORT_TOKEN> 为后台「上报令牌」生成的值)放到站点根目录即可。反代型站点(如 Next.js)若 /sentry.js 被转发到后端,需为它加 location = /sentry.js { root <站点目录>; } 静态规则。
- 异常创建(
status=open,恢复事件除外)→ 调度器原子认领(并发安全,保证每条异常只诊断一次、只发一封邮件) - 拼装上下文:目标信息 + 最近 10 次探测 + 最近 20 条 error/fatal 日志
- LLM 输出结构化报告(根因分析 / 可能原因 / 修复建议),末尾必须给出决策行:
DECISION: auto_resolve/DECISION: watch/DECISION: manual - 系统解析决策行(存入
anomalies.ai_decision,展示文本中剔除该行):auto_resolve且ai_auto_resolve=1:目标当前在线(或无目标)→ 自动置resolved;离线类异常若目标仍故障 → 保持open并记日志watch/manual:保持open等待人工处理
- 目标恢复(down→up 翻转)时:自动关闭该目标所有未处理的离线异常 + 创建「网站已恢复」信息事件(自动关闭)+ 发恢复邮件
users / sessions / monitor_targets / checks / api_tokens / logs / anomalies / llm_conversations / llm_messages / ai_reports / mail_queue / mail_log / settings
anomalies 关键字段:type / severity / status(open|resolved) / notified / llm_analysis / llm_at / ai_decision(auto_resolve|watch|manual) / resolved_at
config.json与构建产物已被.gitignore排除,不要提交真实密钥;发布前请确认配置为config.example.json的占位符形式- 数据库 DSN、SMTP 授权码、LLM Key 均为敏感信息,建议 LLM Key 使用环境变量
SENTINEL_LLM_API_KEY注入 - 初始管理员
admin/admin123仅首启创建,部署后请立即修改 - 图标代理内置 SSRF 防护(拒绝回环 / 私网 / 链路本地地址)
- 外部上报仅接受 Bearer token,且带 CORS 白名单
cd sentinel
go build ./... # 编译检查
go vet ./... # 静态检查
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o sentinel-linux-amd64 .前端为纯静态文件(sentinel/web/),改完直接同步到站点目录即可,无构建步骤。