技术要点
- 使用 tail -c +$offset 从上次读取位置继续,避免全量扫描
- 5xx 错误率 = 5xx 数量 / 总请求数 × 100%
- 告警收敛:同一告警 15 分钟内不重复发送
- 钉钉 Webhook 机器人发送 Markdown 格式告警
#!/bin/bash
# Nginx 5xx 监控脚本 nginx_5xx_monitor.sh
ACCESS_LOG="/var/log/nginx/access.log"
OFFSET_FILE="/tmp/nginx_log_offset"
THRESHOLD=5 # 5xx 比例阈值(%)
DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token="
# 获取上次读取位置
if [ -f "$OFFSET_FILE" ]; then
OFFSET=$(cat $OFFSET_FILE)
else
OFFSET=$(wc -c < $ACCESS_LOG)
fi
# 读取新增日志
NEW_SIZE=$(wc -c < $ACCESS_LOG)
if [ $NEW_SIZE -le $OFFSET ]; then
exit 0 # 没有新日志
fi
tail -c +$((OFFSET+1)) $ACCESS_LOG > /tmp/nginx_new.log
echo $NEW_SIZE > $OFFSET_FILE
# 统计
TOTAL=$(wc -l < /tmp/nginx_new.log)
ERR_5XX=$(awk '$9 ~ /^5[0-9]{2}$/' /tmp/nginx_new.log | wc -l)
if [ $TOTAL -gt 0 ]; then
ERR_RATE=$(awk "BEGIN {printf \"%.2f\", ($ERR_5XX/$TOTAL)*100}")
else
ERR_RATE=0
fi
# 超过阈值发送告警
if [ $(echo "$ERR_RATE > $THRESHOLD" | bc) -eq 1 ]; then
# 告警收敛:15 分钟内不重复
LOCK_FILE="/tmp/nginx_5xx_last_alert"
if [ -f $LOCK_FILE ] && [ $(($(date +%s) - $(cat $LOCK_FILE))) -lt 900 ]; then
exit 0
fi
date +%s > $LOCK_FILE
curl -s $DINGTALK_WEBHOOK -H "Content-Type: application/json" -d "{
\"msgtype\": \"markdown\",
\"markdown\": {
\"title\": \"Nginx 5xx 告警\",
\"text\": \"## Nginx 5xx 告警\n> 错误率: **${ERR_RATE}%**\n> 错误数: ${ERR_5XX}/${TOTAL}\n> 时间: $(date '+%Y-%m-%d %H:%M:%S')\"
}
}"
fi