一、 理论知识介绍:状态机监控与日志抑制策略
在编写生产环境的监控脚本时,最核心的矛盾在于**“监控精度”与“日志冗余”之间的平衡。我们采用状态机(State Machine)**的设计思想来解决这个问题。
- 分状态策略:
- **正常态(OK)**:采用“心跳抑制”逻辑。脚本不记录每一次成功的探测,而是设定一个周期(如 60s),仅在此周期结束时记录一次,证明链路存活。
- **异常态(FAIL)**:采用“高敏感”逻辑。一旦探测失败,立即写入日志,不设任何等待时间。
- **状态恢复的即时性(关键点)**:
- 当监控目标从
FAIL恢复到OK时,必须立即打破 60s 的心跳周期,记录第一条恢复日志。这是通过在失败分支中重置状态计数器来实现的。
- 当监控目标从
- 日志生命周期管理:
- 脚本应具备自愈能力,通过监测文件字节大小(Byte Size)实现日志轮转,防止因长期运行占满磁盘空间(Root Partition Full)。
二、 单步命令实践验证
在合成完整脚本前,我们可以通过以下原子命令验证核心技术点:
1. 验证网络状态码
使用 $? 变量捕获上一条命令的退出状态。
# 测试通畅 IP,返回 0
ping -c 1 -W 1 8.8.8.8 > /dev/null 2>&1; echo $?
# 测试不通 IP,返回非 0(通常为 1 或 2)
ping -c 1 -W 1 10.255.255.1 > /dev/null 2>&1; echo $?
2. 验证时间戳差值计算
利用 Unix 时间戳(Seconds since Epoch)进行逻辑判断。
# 获取当前秒数
NOW=$(date +%s)
# 模拟 70 秒前的时间点
LAST=$((NOW - 70))
# 判断间隔是否超过 60 秒
[[ $((NOW - LAST)) -ge 60 ]] && echo "触发心跳记录"
3. 验证日志轮转动作
通过 stat 获取文件属性并进行重命名。
# 模拟一个 1MB 的日志文件检查
# -c%s 仅输出字节大小
if [ $(stat -c%s "/var/log/ping.log") -ge 1048576 ]; then
mv /var/log/ping.log /var/log/ping.log.old
touch /var/log/ping.log
fi
三、 完整版代码实现(单进程稳健版)
#!/bin/bash
# ==========================
# 配置与初始化
# ==========================
IPS=("8.8.8.8" "1.1.1.1" "192.168.1.1")
INTERVAL=5 # 探测频率 (秒)
SUCCESS_LOG_INT=60 # 成功状态记录间隔 (秒)
LOG_FILE="/var/log/multi_ping.log"
MAX_SIZE=$((1024*1024)) # 1MB 轮转阈值
declare -A LAST_SUCCESS_LOG # 记录每个 IP 上次成功记录的时间戳
# ==========================
# 主循环逻辑
# ==========================
while true; do
# 1. 自动日志轮转
if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE") -ge $MAX_SIZE ]; then
mv "$LOG_FILE" "$LOG_FILE.bak"
touch "$LOG_FILE"
fi
NOW=$(date +%s)
DATE_STR=$(date '+%F %T')
for IP in "${IPS[@]}"; do
if ping -c 1 -W 1 "$IP" > /dev/null 2>&1; then
# --- 成功处理:抑制逻辑 ---
LAST=${LAST_SUCCESS_LOG[$IP]:-0}
if (( NOW - LAST >= SUCCESS_LOG_INT )); then
echo "[$DATE_STR] $IP OK" >> "$LOG_FILE"
LAST_SUCCESS_LOG[$IP]=$NOW
fi
else
# --- 失败处理:即时记录 ---
echo "[$DATE_STR] $IP FAIL" >> "$LOG_FILE"
# 关键:重置成功时间戳。确保恢复后的下一次 OK 能立刻记录。
LAST_SUCCESS_LOG[$IP]=0
fi
done
sleep "$INTERVAL"
done