Files
my-project/config/openclaw/scripts/system-monitor.sh
T

239 lines
8.6 KiB
Bash
Executable File

#!/bin/bash
# Optimized CPU & Memory Monitoring System v2.0
# Heartbeat: 10 seconds (light), 60 seconds (heavy)
# Features: Service health check, threshold alerts, session file monitoring
# Optimizations: Reduced frequency, /proc instead of top, separation of concerns
LOG_DIR="/var/log/openclaw-monitor"
LOG_FILE="${LOG_FILE:-$LOG_DIR/system-metrics.log}"
ALERT_LOG="${LOG_DIR}/alerts.log"
SESSION_LOG="${LOG_DIR}/session-alerts.log"
MAX_RECORDS=5400
INTERVAL_LIGHT=10 # 轻量监控间隔(秒)
INTERVAL_HEAVY=60 # 重量监控间隔(秒)
OPENCLAW_PORTS=("18789")
# 资源阈值
CPU_THRESHOLD=80
MEM_THRESHOLD=80
LOAD_THRESHOLD=4.0
# 会话文件监控阈值
SESSION_DIR="/root/.openclaw/agents/main/sessions"
SESSION_WARN_SIZE=$((8 * 1024 * 1024)) # 8MB 警告
SESSION_NEW_SESSION_SIZE=$((5 * 1024 * 1024)) # 5MB 开新会话
SESSION_MARKER_FILE="/tmp/openclaw-session-warn.json"
SESSION_NEW_SESSION_FILE="/tmp/openclaw-session-new.json"
LAST_CPU=""
LAST_MEM=""
LAST_LOAD=""
LAST_SESSION_SIZE=0
LAST_SESSION_CHECK=0
mkdir -p "$LOG_DIR"
# 轻量监控:CPU/内存/负载(高频)
check_light() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# CPU 使用率(使用 /proc/stat 更轻量)
local cpu_line=$(cat /proc/stat 2>/dev/null | grep '^cpu ' | head -1)
if [ -n "$cpu_line" ]; then
local cpu_idle=$(echo "$cpu_line" | awk '{print $5}')
# 简单估算:假设总时间是 idle + user + nice + system + iowait + irq + softirq
local cpu_total=$(echo "$cpu_line" | awk '{print $2+$3+$4+$5+$6+$7+$8}')
local cpu_usage="0.0"
if [ -n "$cpu_total" ] && [ "$cpu_total" != "0" ]; then
cpu_usage=$(awk "BEGIN {printf \"%.1f\", (($cpu_total - $cpu_idle) / $cpu_total) * 100}")
fi
else
# 回退到 top(极少情况)
cpu_usage=$(top -bn1 2>/dev/null | awk '/Cpu\(s\)/ {gsub(/,/, "", $2); print $2}' | head -1)
fi
# 内存信息
local mem_info=$(free -m 2>/dev/null | awk 'NR==2 {print $2,$3,$7}')
local mem_total=$(echo "$mem_info" | awk '{print $1}')
local mem_used=$(echo "$mem_info" | awk '{print $2}')
local mem_avail=$(echo "$mem_info" | awk '{print $3}')
local mem_usage="0.0"
if [ -n "$mem_total" ] && [ "$mem_total" != "0" ]; then
mem_usage=$(awk "BEGIN {printf \"%.1f\", ($mem_used)*100/$mem_total}")
fi
# 负载
local load_1m=$(cat /proc/loadavg | awk '{print $1}')
# 记录轻量日志
echo "LIGHT|${timestamp}|${cpu_usage}|${mem_usage}|${load_1m}" >> "$LOG_FILE"
# 检查阈值
check_thresholds "$timestamp" "$cpu_usage" "$mem_usage" "$load_1m"
}
# 重量监控:磁盘/会话文件(低频)
check_heavy() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 交换空间
local swap_info=$(free -m 2>/dev/null | awk 'NR==3 {print $2,$3}')
local swap_total=$(echo "$swap_info" | awk '{print $1}')
local swap_used=$(echo "$swap_info" | awk '{print $2}')
local swap_usage="0.0"
if [ -n "$swap_total" ] && [ "$swap_total" != "0" ]; then
swap_usage=$(awk "BEGIN {printf \"%.1f\", ($swap_used)*100/$swap_total}")
fi
# 负载(完整)
local load_5m=$(cat /proc/loadavg | awk '{print $2}')
local load_15m=$(cat /proc/loadavg | awk '{print $3}')
# 磁盘
local disk_usage=$(df -h / 2>/dev/null | awk 'NR==2 {print $5}' | tr -d '%')
# OpenClaw 内存
local openclaw_mem="0.0"
local pids=$(pgrep -f "openclaw-gateway" 2>/dev/null)
if [ -n "$pids" ]; then
local sum=0
for pid in $pids; do
local rss=$(ps -o rss= -p "$pid" 2>/dev/null)
sum=$((sum + rss))
done
openclaw_mem=$(awk "BEGIN {printf \"%.1f\", $sum/1024}")
fi
# 服务状态
local service_status="DOWN"
for port in "${OPENCLAW_PORTS[@]}"; do
if ss -tlnp 2>/dev/null | grep -q ":${port} " || netstat -tlnp 2>/dev/null | grep -q ":${port} "; then
service_status="UP"
break
fi
done
# 记录重量日志
echo "HEAVY|${timestamp}|${swap_usage}|${load_5m}|${load_15m}|${disk_usage}|${openclaw_mem}|${service_status}" >> "$LOG_FILE"
# 检查服务状态
if [ "$service_status" = "DOWN" ]; then
echo "[CRITICAL] ${timestamp} OpenClaw SERVICE DOWN!" >> "$ALERT_LOG"
fi
# 检查会话文件(每60秒)
check_session_files "$timestamp"
}
check_session_files() {
if [ ! -d "$SESSION_DIR" ]; then
return
fi
# 获取当前主会话文件(最大文件)
local largest_file=""
local largest_size=0
local current_session_id=""
for file in "$SESSION_DIR"/*.jsonl; do
if [ -f "$file" ]; then
local size=$(stat -c%s "$file" 2>/dev/null || echo 0)
if [ "$size" -gt "$largest_size" ]; then
largest_size="$size"
largest_file="$file"
fi
fi
done
if [ "$largest_size" -eq 0 ] || [ -z "$largest_file" ]; then
return
fi
# 提取会话ID
current_session_id=$(basename "$largest_file" .jsonl)
# 8MB 警告
if [ "$largest_size" -ge "$SESSION_WARN_SIZE" ]; then
local size_mb=$(awk "BEGIN {printf \"%.1f\", $largest_size/1024/1024}")
local last_alert_size=$(cat "$SESSION_MARKER_FILE" 2>/dev/null | grep -o "\"warn\":[0-9]*" | grep -o "[0-9]*" || echo "0")
alert_threshold=$((last_alert_size / 1024 / 1024 + 1))
current_mb=$(echo "scale=1; $largest_size/1024/1024" | bc 2>/dev/null || echo "0")
if [ -n "$current_mb" ] && [ "$(echo "$current_mb >= $alert_threshold" | bc 2>/dev/null || echo "0")" -eq 1 ]; then
echo "[SESSION WARN] ${timestamp} 会话过大: ${current_session_id} (${size_mb}MB)" >> "$SESSION_LOG"
bash /root/.openclaw/scripts/send-session-alert.sh "warn" "$current_session_id" "$size_mb"
echo "{\"warn\":$largest_size,\"time\":\"$timestamp\"}" > "$SESSION_MARKER_FILE"
fi
fi
# 5MB 自动开新会话
if [ "$largest_size" -ge "$SESSION_NEW_SESSION_SIZE" ] && [ "$largest_size" -lt "$SESSION_WARN_SIZE" ]; then
local size_mb=$(awk "BEGIN {printf \"%.1f\", $largest_size/1024/1024}")
local last_processed=$(cat "$SESSION_NEW_SESSION_FILE" 2>/dev/null | grep -o "\"processed\":\"[^\"]*\"" | cut -d'"' -f4 || echo "")
if [ "$current_session_id" != "$last_processed" ]; then
echo "[SESSION NEW] ${timestamp} 会话超5MB: ${current_session_id} (${size_mb}MB)" >> "$SESSION_LOG"
bash /root/.openclaw/scripts/send-session-alert.sh "new" "$current_session_id" "$size_mb"
echo "{\"warn\":0,\"processed\":\"$current_session_id\",\"time\":\"$timestamp\"}" > "$SESSION_NEW_SESSION_FILE"
fi
fi
}
check_thresholds() {
local ts="$1" cpu="$2" mem="$3" load="$4"
# CPU 检查
cpu=$(echo "$cpu" | tr -d ' ')
if [ -n "$cpu" ] && [ "$cpu" != "0.0" ]; then
local cpu_high=$(awk "BEGIN {print ($cpu > $CPU_THRESHOLD) ? 1 : 0}")
if [ "$cpu_high" = "1" ] && [ "$LAST_CPU" != "HIGH" ]; then
echo "[ALERT] ${ts} CPU HIGH: ${cpu}% (threshold: ${CPU_THRESHOLD}%)" >> "$ALERT_LOG"
fi
LAST_CPU="$([ "$cpu_high" = "1" ] && echo "HIGH" || echo "NORMAL")"
fi
# 内存检查
if [ -n "$mem" ] && [ "$mem" != "0.0" ]; then
local mem_high=$(awk "BEGIN {print ($mem > $MEM_THRESHOLD) ? 1 : 0}")
if [ "$mem_high" = "1" ] && [ "$LAST_MEM" != "HIGH" ]; then
echo "[ALERT] ${ts} MEM HIGH: ${mem}%" >> "$ALERT_LOG"
fi
LAST_MEM="$([ "$mem_high" = "1" ] && echo "HIGH" || echo "NORMAL")"
fi
# 负载检查
if [ -n "$load" ]; then
local load_high=$(awk "BEGIN {print ($load > $LOAD_THRESHOLD) ? 1 : 0}")
if [ "$load_high" = "1" ] && [ "$LAST_LOAD" != "HIGH" ]; then
echo "[ALERT] ${ts} LOAD HIGH: ${load} (threshold: ${LOAD_THRESHOLD})" >> "$ALERT_LOG"
fi
LAST_LOAD="$([ "$load_high" = "1" ] && echo "HIGH" || echo "NORMAL")"
fi
}
cleanup() {
local line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
if [ "$line_count" -gt "$MAX_RECORDS" ]; then
local to_remove=$((line_count - MAX_RECORDS))
sed -i "1,${to_remove}d" "$LOG_FILE" 2>/dev/null
fi
}
trap cleanup EXIT
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') 监控启动 (light:${INTERVAL_LIGHT}s, heavy:${INTERVAL_HEAVY}s)"
counter=0
while true; do
# 轻量监控每次都执行
check_light
# 计数器:每6次(60秒)执行一次重量监控
counter=$((counter + 1))
if [ $counter -ge 6 ]; then
check_heavy
counter=0
fi
cleanup
sleep $INTERVAL_LIGHT
done