埋点异常阈值告警与 Webhook 通知实现
【摘要】 背景痛点前面的日志脚本只能定时产出报表,属于“事后看数据”:页面性能突然恶化、前端错误爆发,要等人工翻看报表才能发现;流量异常(爬虫刷量、访问突降)无法自动感知;轻量静态站点不想部署 Prometheus + AlertManager 这类重型告警组件;希望告警通知直接推到 IM 工具,及时触达维护者。方案思路:在原有日志分析脚本基础上,增加阈值规则 + 环比突变检测模块,满足条件时调用 ...
背景痛点
前面的日志脚本只能定时产出报表,属于“事后看数据”:
- 页面性能突然恶化、前端错误爆发,要等人工翻看报表才能发现;
- 流量异常(爬虫刷量、访问突降)无法自动感知;
- 轻量静态站点不想部署 Prometheus + AlertManager 这类重型告警组件;
- 希望告警通知直接推到 IM 工具,及时触达维护者。
方案思路:在原有日志分析脚本基础上,增加阈值规则 + 环比突变检测模块,满足条件时调用 Webhook 接口推送结构化告警消息。
整体流程
日志拉取 → 清洗聚合 → 指标计算 → 规则判断(固定阈值 / 环比)→ 命中则调用 Webhook 推送告警
完整扩展代码
// alert-analyzer.js
const fs = require('fs');
const path = require('path');
const https = require('https');
const REPORT_PATH = './logs/report.json';
const HISTORY_PATH = './logs/history.json';
const WEBHOOK_URL = process.env.ALERT_WEBHOOK || '';
// 告警规则配置
const ALERT_RULES = [
{
name: "LCP性能恶化",
expr: (curr, prev) => curr.summary.lcpP95 > 3000,
level: "warning",
desc: "页面LCP P95超过3000ms,用户加载体验变差"
},
{
name: "前端错误突增",
expr: (curr, prev) => {
const currErrCount = curr.topError.reduce((s, item) => s + item[1], 0);
const prevErrCount = prev?.topError?.reduce((s, item) => s + item[1], 0) || 0;
return prevErrCount > 0 && currErrCount / prevErrCount > 2.5;
},
level: "critical",
desc: "前端错误总量相比上一个周期翻倍以上"
},
{
name: "流量断崖下跌",
expr: (curr, prev) => {
if (!prev?.summary?.totalPV) return false;
const ratio = curr.summary.totalPV / prev.summary.totalPV;
return ratio < 0.3 && curr.summary.totalPV < 5;
},
level: "warning",
desc: "页面访问量大幅下跌,可能站点无法访问"
}
];
// 读取历史指标快照
function loadHistory() {
if (!fs.existsSync(HISTORY_PATH)) return null;
try {
return JSON.parse(fs.readFileSync(HISTORY_PATH, 'utf-8'));
} catch {
return null;
}
}
// 保存当前指标为历史快照,用于下一轮环比
function saveCurrentSnapshot(data) {
fs.writeFileSync(HISTORY_PATH, JSON.stringify(data, null, 2), 'utf-8');
}
// Webhook推送消息
function sendWebhook(alerts, report) {
if (!WEBHOOK_URL) {
console.log("未配置ALERT_WEBHOOK,跳过推送");
return Promise.resolve();
}
const alertText = alerts.map(a => `[${a.level}] ${a.name}:${a.desc}`).join('\n');
const payload = {
msgtype: "text",
text: {
content: `【静态站点埋点告警】
生成时间:${report.meta.generateAt}
大盘PV:${report.summary.totalPV},UV:${report.summary.totalUV}
${alertText}`
}
};
return new Promise((resolve, reject) => {
const req = https.request(WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, res => {
let buf = '';
res.on('data', d => buf += d);
res.on('end', () => resolve({ status: res.statusCode, body: buf }));
});
req.on('error', reject);
req.write(JSON.stringify(payload));
req.end();
})
}
async function main() {
if (!fs.existsSync(REPORT_PATH)) {
console.log("指标报表不存在,跳过告警");
return;
}
const currReport = JSON.parse(fs.readFileSync(REPORT_PATH, 'utf-8'));
const prevReport = loadHistory();
const hitAlerts = [];
for (const rule of ALERT_RULES) {
try {
if (rule.expr(currReport, prevReport)) {
hitAlerts.push(rule);
}
} catch (e) {
console.error(`规则${rule.name}执行异常`, e);
}
}
if (hitAlerts.length > 0) {
console.log("触发告警:", hitAlerts.map(x => x.name));
await sendWebhook(hitAlerts, currReport);
} else {
console.log("无告警触发");
}
saveCurrentSnapshot(currReport);
}
main().catch(console.error);
流水线集成方式
和之前的日志分析脚本串联,在CI定时任务里顺序执行:
node log-analyzer.js
node alert-analyzer.js
环境变量注入 Webhook 地址,避免硬编码密钥:
variables:
ALERT_WEBHOOK: $WEBHOOK_SECRET
告警降噪策略(非常关键)
小站点流量波动大,很容易产生误报,增加几条降噪逻辑:
- 最小样本阈值:PV过少的时候直接关闭告警,避免没人访问触发“流量下跌”告警;
- 冷却窗口:同一告警规则在30分钟内只推送一次,防止持续刷屏;
- 黑白名单:可以配置部分页面路径不参与告警(如内部测试页);
- 区分告警等级:critical 强通知,warning 只记录、可选择静默。
补充冷却改造示例,在 history.json 中额外记录每条告警最后触发时间,规则内增加时间判断。
工程权衡
- ✅ 优点:零中间件、轻量化、复用已有的埋点报表,不需要额外存储时序库;
- ⚠️ 局限:告警粒度和CI调度周期绑定(比如6分钟/1小时),做不到秒级实时;
- ⚠️ 局限:环比对比只和上一个周期对比,不是多天均值;适合趋势发现,不适合精密监控;
- ⚠️ 局限:Webhook 适配不同IM(企业微信、飞书、钉钉)的消息体格式不一样,payload 需要微调。
后续演进方向
- 增加告警静默计划(比如夜间低峰时段关闭通知);
- 增加告警聚合,短时间多条同类告警合并成一条;
- 增加恢复通知:指标回归正常后推送“告警已恢复”消息;
- 引入Mermaid图表,在Markdown报表里绘制PV、LCP时序趋势图。
、
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)