提交 ea219194 authored 作者: 陈泽健's avatar 陈泽健

feat(monitoring): 实现自动化服务监测脚本功能

- 添加平台识别和系统识别功能
- 实现日志暴涨检测和ERROR上下文检测
- 添加内存资源消耗监控和MySQL连接数监控
- 实现监测报告MD文件生成功能
- 添加中文日志审计和报告输出功能
- 整合需求文档中的各项监测指标
上级 d6c7bfc3
''''bash ''''bash
# filepath: /data/scripts/AutomatedServiceMonitoring.sh # filepath: e:\GithubData\自动化\ubains-module-test\AuxiliaryTool\ScriptTool\自动化服务监测\AutomatedServiceMonitoring.sh
#!/usr/bin/env bash #!/usr/bin/env bash
######################################## ########################################
# 自动化服务监测 Shell 脚本(单机版) # 自动化服务监测 Shell 脚本(单机版)
# 在哪台服务器上运行,就监控哪台服务器(本机) # 在哪台服务器上运行,就监控哪台服务器(本机)
# 对齐《自动化服务监测需求文档》:
# 1. 平台识别
# 2. 系统识别
# 3.1 日志审计(暴涨 + ERROR 上下文)
# 3.2 内存资源消耗
# 3.3 MySQL 连接数
# 4. 监测日志审计(中文日志)
# 5. 监测报告输出(本脚本内生成 Markdown 报告)
######################################## ########################################
set -u # 未定义变量直接报错 set -u # 未定义变量直接报错
...@@ -15,17 +23,23 @@ set -u # 未定义变量直接报错 ...@@ -15,17 +23,23 @@ set -u # 未定义变量直接报错
# 监控间隔(秒) # 监控间隔(秒)
INTERVAL_SECONDS=60 INTERVAL_SECONDS=60
# MySQL 账号(与 PRD 保持一致) # 报告生成间隔(秒),例如 600 = 每 10 分钟写一份 MD 报告
REPORT_INTERVAL_SECONDS=600
# MySQL 账号(与需求文档一致)
MYSQL_USER="root" MYSQL_USER="root"
MYSQL_PASSWORD="dNrprU&2S" MYSQL_PASSWORD="dNrprU&2S"
# 日志文件(默认写当前目录 # 日志文件(监测运行过程
LOG_FILE="./AutomatedServiceMonitoring.sh.log" LOG_FILE="./AutomatedServiceMonitoring.sh.log"
# 报告输出目录
REPORT_DIR="./monitor_reports"
# 当前主机标识 # 当前主机标识
HOST_NAME="$(hostname)" HOST_NAME="$(hostname)"
#################### 简单日志函数 #################### #################### 简单日志函数(✅ 中文) ####################
log() { log() {
# $1: 级别 INFO/WARN/ERROR # $1: 级别 INFO/WARN/ERROR
# $2+: 消息 # $2+: 消息
...@@ -37,11 +51,8 @@ log() { ...@@ -37,11 +51,8 @@ log() {
echo "$msg" >>"$LOG_FILE" echo "$msg" >>"$LOG_FILE"
} }
#################### 平台识别 #################### #################### 1. 平台识别(✅ 已实现) ####################
detect_platform() { detect_platform() {
# 输出两个全局变量:
# PLATFORM_TYPE="new" 或 "legacy"
# BASE_PATH="/data/services" 或 "/var/www"
if [ -d "/data/services" ]; then if [ -d "/data/services" ]; then
PLATFORM_TYPE="new" PLATFORM_TYPE="new"
BASE_PATH="/data/services" BASE_PATH="/data/services"
...@@ -51,14 +62,13 @@ detect_platform() { ...@@ -51,14 +62,13 @@ detect_platform() {
fi fi
} }
#################### 系统识别(ujava/upython/upython_voice) #################### #################### 2. 系统识别(✅ 已实现) ####################
detect_systems() { detect_systems() {
HAS_UJAVA=0 HAS_UJAVA=0
HAS_UPYTHON=0 HAS_UPYTHON=0
HAS_UPYTHON_VOICE=0 HAS_UPYTHON_VOICE=0
SYSTEMS=() # bash 数组:meeting / ops / transcription SYSTEMS=() # meeting / ops / transcription
# 检查 docker 容器名
local names local names
names="$(docker ps --format '{{.Names}}' 2>/dev/null || true)" names="$(docker ps --format '{{.Names}}' 2>/dev/null || true)"
...@@ -66,20 +76,19 @@ detect_systems() { ...@@ -66,20 +76,19 @@ detect_systems() {
HAS_UJAVA=1 HAS_UJAVA=1
SYSTEMS+=("meeting") SYSTEMS+=("meeting")
fi fi
if echo "$names" | grep -q "upython_voice"; then
HAS_UPYTHON_VOICE=1
SYSTEMS+=("transcription")
fi
if echo "$names" | grep -q "upython"; then if echo "$names" | grep -q "upython"; then
HAS_UPYTHON=1 HAS_UPYTHON=1
SYSTEMS+=("ops") SYSTEMS+=("ops")
fi fi
if echo "$names" | grep -q "upython_voice"; then
HAS_UPYTHON_VOICE=1
SYSTEMS+=("transcription")
fi
} }
#################### 日志目标解析 #################### #################### 3.1 日志审计(✅ 已实现) ####################
# 目标解析(只按照文档里的预定系统日志)
resolve_log_targets() { resolve_log_targets() {
# 根据 PLATFORM_TYPE / BASE_PATH / SYSTEMS 生成日志列表
# 结果放到全局数组:LOG_TARGET_SYS[] / LOG_TARGET_PATH[]
LOG_TARGET_SYS=() LOG_TARGET_SYS=()
LOG_TARGET_PATH=() LOG_TARGET_PATH=()
...@@ -104,21 +113,41 @@ resolve_log_targets() { ...@@ -104,21 +113,41 @@ resolve_log_targets() {
fi fi
} }
#################### 日志暴涨检测(按总行数窗口) #################### # ========= 日志暴涨与 ERROR 审计的状态,供报告使用 =========
# 使用关联数组记录上一次总行数和时间戳: key=日志路径
# 暴涨状态:key = "sys_name|path"
declare -A BURST_LAST_TOTAL declare -A BURST_LAST_TOTAL
declare -A BURST_LAST_TS declare -A BURST_LAST_TS
declare -A BURST_LAST_RESULT # 最近一次判定结果:OK / BURST
declare -A BURST_LAST_DESC # 最近一次描述(窗口/增量/速率/时间段)
# ERROR 状态:key = "sys_name|path"
declare -A ERROR_LAST_COUNT
declare -A ERROR_LAST_TIME_RANGE # "start ~ end"
# 工具函数:构造 key
make_log_key() {
local sys_name="$1"
local log_path="$2"
echo "${sys_name}|${log_path}"
}
# 3.1.1 日志暴涨检测(窗口增量法)
monitor_log_burst_once() { monitor_log_burst_once() {
local sys_name="$1" local sys_name="$1"
local log_path="$2" local log_path="$2"
local window_seconds=300 local window_seconds=300
local min_lines_threshold=1000 local min_lines_threshold=1000
local rate_threshold_per_sec=5 local rate_threshold_per_sec=5
# 检查文件是否存在 local key
key="$(make_log_key "$sys_name" "$log_path")"
if [ ! -f "$log_path" ]; then if [ ! -f "$log_path" ]; then
log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 文件不存在" log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 文件不存在"
BURST_LAST_RESULT["$key"]="NO_FILE"
BURST_LAST_DESC["$key"]="日志文件不存在"
return return
fi fi
...@@ -129,13 +158,14 @@ monitor_log_burst_once() { ...@@ -129,13 +158,14 @@ monitor_log_burst_once() {
if ! [[ "$total_lines" =~ ^[0-9]+$ ]]; then if ! [[ "$total_lines" =~ ^[0-9]+$ ]]; then
log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 无法获取总行数" log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 无法获取总行数"
BURST_LAST_RESULT["$key"]="UNKNOWN"
BURST_LAST_DESC["$key"]="无法获取总行数"
return return
fi fi
local now_ts local now_ts
now_ts="$(date +%s)" now_ts="$(date +%s)"
local key="$log_path"
local last_total="${BURST_LAST_TOTAL[$key]-}" local last_total="${BURST_LAST_TOTAL[$key]-}"
local last_ts="${BURST_LAST_TS[$key]-}" local last_ts="${BURST_LAST_TS[$key]-}"
...@@ -143,29 +173,27 @@ monitor_log_burst_once() { ...@@ -143,29 +173,27 @@ monitor_log_burst_once() {
BURST_LAST_TOTAL["$key"]="$total_lines" BURST_LAST_TOTAL["$key"]="$total_lines"
BURST_LAST_TS["$key"]="$now_ts" BURST_LAST_TS["$key"]="$now_ts"
log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 初始化窗口,总行数=$total_lines" log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 初始化窗口,总行数=$total_lines"
BURST_LAST_RESULT["$key"]="INIT"
BURST_LAST_DESC["$key"]="初始化窗口,总行数=${total_lines}"
return return
fi fi
local elapsed=$(( now_ts - last_ts )) local elapsed=$(( now_ts - last_ts ))
if (( elapsed <= 0 )); then if (( elapsed <= 0 )); then elapsed=1; fi
elapsed=1
fi
local delta_lines=$(( total_lines - last_total )) local delta_lines=$(( total_lines - last_total ))
if (( delta_lines < 0 )); then if (( delta_lines < 0 )); then delta_lines=0; fi
delta_lines=0
fi
# 更新状态
BURST_LAST_TOTAL["$key"]="$total_lines" BURST_LAST_TOTAL["$key"]="$total_lines"
BURST_LAST_TS["$key"]="$now_ts" BURST_LAST_TS["$key"]="$now_ts"
if (( elapsed < window_seconds )); then if (( elapsed < window_seconds )); then
log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 累积中:窗口=${elapsed}s 未达 ${window_seconds}s,新增行数=${delta_lines}" log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 累积中:窗口=${elapsed}s 未达 ${window_seconds}s,新增行数=${delta_lines}"
BURST_LAST_RESULT["$key"]="COLLECTING"
BURST_LAST_DESC["$key"]="窗口${elapsed}s/未达${window_seconds}s,新增行数=${delta_lines}"
return return
fi fi
# shell 里做速率计算
local rate local rate
rate=$(awk -v d="$delta_lines" -v e="$elapsed" 'BEGIN{ if(e<=0){e=1}; printf "%.2f", d/e }') rate=$(awk -v d="$delta_lines" -v e="$elapsed" 'BEGIN{ if(e<=0){e=1}; printf "%.2f", d/e }')
...@@ -173,34 +201,44 @@ monitor_log_burst_once() { ...@@ -173,34 +201,44 @@ monitor_log_burst_once() {
start_ts_human="$(date -d @"$last_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')" start_ts_human="$(date -d @"$last_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
end_ts_human="$(date -d @"$now_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')" end_ts_human="$(date -d @"$now_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
local desc="窗口=${elapsed}s 新增行数=${delta_lines} 速率=${rate}行/秒 时间段=[${start_ts_human} ~ ${end_ts_human}]"
if (( delta_lines >= min_lines_threshold )) || awk -v r="$rate" -v th="$rate_threshold_per_sec" 'BEGIN{exit !(r>=th)}'; then if (( delta_lines >= min_lines_threshold )) || awk -v r="$rate" -v th="$rate_threshold_per_sec" 'BEGIN{exit !(r>=th)}'; then
log WARN "[日志打印暴涨] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 窗口=${elapsed}s 新增行数=${delta_lines} 速率=${rate}行/秒 时间段=[${start_ts_human} ~ ${end_ts_human}]" log WARN "[日志打印暴涨] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path ${desc}"
BURST_LAST_RESULT["$key"]="BURST"
BURST_LAST_DESC["$key"]="$desc"
else else
log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 窗口=${elapsed}s 新增行数=${delta_lines} 速率=${rate}行/秒 未发现暴涨" log INFO "[日志暴涨审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path ${desc},未发现暴涨"
BURST_LAST_RESULT["$key"]="OK"
BURST_LAST_DESC["$key"]="$desc"
fi fi
} }
#################### ERROR 上下文检测 #################### # 3.1.2 ERROR 上下文检测(记录 ERROR 上下 50 行的时间段)
monitor_log_errors_once() { monitor_log_errors_once() {
local sys_name="$1" local sys_name="$1"
local log_path="$2" local log_path="$2"
local max_context=50 local max_context=50
local max_sections=3 local max_sections=3
local key
key="$(make_log_key "$sys_name" "$log_path")"
if [ ! -f "$log_path" ]; then if [ ! -f "$log_path" ]; then
log INFO "[ERROR审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 文件不存在" log INFO "[ERROR审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 文件不存在"
ERROR_LAST_COUNT["$key"]=0
ERROR_LAST_TIME_RANGE["$key"]="日志文件不存在"
return return
fi fi
# 最近 5000 行
local tail_out local tail_out
tail_out="$(tail -n 5000 "$log_path" 2>/dev/null || true)" tail_out="$(tail -n 5000 "$log_path" 2>/dev/null || true)"
if [[ -z "${tail_out//[[:space:]]/}" ]]; then if [[ -z "${tail_out//[[:space:]]/}" ]]; then
log INFO "[ERROR审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近5000行无内容" log INFO "[ERROR审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近5000行无内容"
ERROR_LAST_COUNT["$key"]=0
ERROR_LAST_TIME_RANGE["$key"]="最近5000行无内容"
return return
fi fi
# ERROR 数量
local cnt local cnt
cnt="$(echo "$tail_out" | grep -c "ERROR" || true)" cnt="$(echo "$tail_out" | grep -c "ERROR" || true)"
if ! [[ "$cnt" =~ ^[0-9]+$ ]]; then if ! [[ "$cnt" =~ ^[0-9]+$ ]]; then
...@@ -209,10 +247,11 @@ monitor_log_errors_once() { ...@@ -209,10 +247,11 @@ monitor_log_errors_once() {
if (( cnt == 0 )); then if (( cnt == 0 )); then
log INFO "[ERROR审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近段落未发现 ERROR" log INFO "[ERROR审计] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近段落未发现 ERROR"
ERROR_LAST_COUNT["$key"]=0
ERROR_LAST_TIME_RANGE["$key"]="未发现 ERROR"
return return
fi fi
# 上下文抓取:对最近 5000 行做 nl 标号,取最近几处 ERROR
local ctx local ctx
ctx="$(echo "$tail_out" | nl -ba | \ ctx="$(echo "$tail_out" | nl -ba | \
awk '{if($0 ~ /ERROR/) print $1}' | tail -n "$max_sections" | \ awk '{if($0 ~ /ERROR/) print $1}' | tail -n "$max_sections" | \
...@@ -226,27 +265,30 @@ monitor_log_errors_once() { ...@@ -226,27 +265,30 @@ monitor_log_errors_once() {
done done
)" )"
# 本地提取时间戳(YYYY-MM-DD HH:MM:SS)
local start_ts end_ts local start_ts end_ts
start_ts="$(echo "$ctx" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -n1)" start_ts="$(echo "$ctx" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -n1)"
end_ts="$(echo "$ctx" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | tail -n1)" end_ts="$(echo "$ctx" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | tail -n1)"
if [[ -n "$start_ts" || -n "$end_ts" ]]; then if [[ -n "$start_ts" || -n "$end_ts" ]]; then
log WARN "[ERROR出现] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近5000行 ERROR数量=$cnt 发生时间段=[$start_ts ~ $end_ts]" log WARN "[ERROR出现] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近5000行 ERROR数量=$cnt 发生时间段=[$start_ts ~ $end_ts]"
ERROR_LAST_TIME_RANGE["$key"]="${start_ts} ~ ${end_ts}"
else else
log WARN "[ERROR出现] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近5000行 ERROR数量=$cnt" log WARN "[ERROR出现] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path 最近5000行 ERROR数量=$cnt"
ERROR_LAST_TIME_RANGE["$key"]="时间戳未解析成功"
fi fi
ERROR_LAST_COUNT["$key"]="$cnt"
log INFO "[ERROR上下文] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path log INFO "[ERROR上下文] 主机=$HOST_NAME 系统=$sys_name 日志=$log_path
$ctx" $ctx"
} }
#################### 内存监控(/proc/meminfo) #################### #################### 3.2 内存资源消耗(✅ 已实现) ####################
# 按主机维度记录峰值和平均
MEM_SAMPLES=0 MEM_SAMPLES=0
MEM_SUM_USED_MB=0 MEM_SUM_USED_MB=0
MEM_PEAK_USED_MB=0 MEM_PEAK_USED_MB=0
MEM_PEAK_TS=0 MEM_PEAK_TS=0
MEM_LAST_USED_MB=0
monitor_mem_once() { monitor_mem_once() {
local meminfo local meminfo
...@@ -268,6 +310,8 @@ monitor_mem_once() { ...@@ -268,6 +310,8 @@ monitor_mem_once() {
total_mb=$(awk -v t="$total_kb" 'BEGIN{printf "%.0f",t/1024}') total_mb=$(awk -v t="$total_kb" 'BEGIN{printf "%.0f",t/1024}')
used_mb=$(awk -v t="$total_kb" -v a="$avail_kb" 'BEGIN{u=(t-a)/1024; if(u<0)u=0; printf "%.0f",u}') used_mb=$(awk -v t="$total_kb" -v a="$avail_kb" 'BEGIN{u=(t-a)/1024; if(u<0)u=0; printf "%.0f",u}')
MEM_LAST_USED_MB="$used_mb"
MEM_SAMPLES=$((MEM_SAMPLES + 1)) MEM_SAMPLES=$((MEM_SAMPLES + 1))
MEM_SUM_USED_MB=$((MEM_SUM_USED_MB + used_mb)) MEM_SUM_USED_MB=$((MEM_SUM_USED_MB + used_mb))
if (( used_mb > MEM_PEAK_USED_MB )); then if (( used_mb > MEM_PEAK_USED_MB )); then
...@@ -286,16 +330,18 @@ monitor_mem_once() { ...@@ -286,16 +330,18 @@ monitor_mem_once() {
log INFO "[内存监测] $HOST_NAME 当前使用=${used_mb}MB 总=${total_mb}MB 平均=${avg_used}MB 峰值=${MEM_PEAK_USED_MB}MB@$peak_human" log INFO "[内存监测] $HOST_NAME 当前使用=${used_mb}MB 总=${total_mb}MB 平均=${avg_used}MB 峰值=${MEM_PEAK_USED_MB}MB@$peak_human"
} }
#################### MySQL 连接数监控 #################### #################### 3.3 MySQL 连接数(✅ 已实现) ####################
MYSQL_SAMPLES=0 MYSQL_SAMPLES=0
MYSQL_SUM_CONN=0 MYSQL_SUM_CONN=0
MYSQL_PEAK_CONN=0 MYSQL_PEAK_CONN=0
MYSQL_PEAK_TS=0 MYSQL_PEAK_TS=0
MYSQL_LAST_TOTAL="" MYSQL_LAST_TOTAL=""
MYSQL_LAST_TS="" MYSQL_LAST_TS=""
MYSQL_LAST_CONN=0
MYSQL_LAST_BURST_STATUS="UNKNOWN"
MYSQL_LAST_BURST_DESC=""
find_mysql_container() { find_mysql_container() {
# 回显容器名或空
docker ps --format '{{.Names}} {{.Image}}' 2>/dev/null | \ docker ps --format '{{.Names}} {{.Image}}' 2>/dev/null | \
awk '{ awk '{
name=$1; $1=""; image=$0; name=$1; $1=""; image=$0;
...@@ -360,9 +406,13 @@ monitor_mysql_once() { ...@@ -360,9 +406,13 @@ monitor_mysql_once() {
if [[ -z "$conn" || ! "$conn" =~ ^[0-9]+$ ]]; then if [[ -z "$conn" || ! "$conn" =~ ^[0-9]+$ ]]; then
log ERROR "[MySQL监测] $HOST_NAME => 获取连接数失败(请确认 Docker/MySQL 客户端与权限)" log ERROR "[MySQL监测] $HOST_NAME => 获取连接数失败(请确认 Docker/MySQL 客户端与权限)"
MYSQL_LAST_BURST_STATUS="ERROR"
MYSQL_LAST_BURST_DESC="无法获取连接数"
return return
fi fi
MYSQL_LAST_CONN="$conn"
local now_ts local now_ts
now_ts="$(date +%s)" now_ts="$(date +%s)"
...@@ -383,7 +433,6 @@ monitor_mysql_once() { ...@@ -383,7 +433,6 @@ monitor_mysql_once() {
log INFO "[MySQL监测] $HOST_NAME 当前连接数=$conn 平均=$avg_conn 峰值=${MYSQL_PEAK_CONN}@$peak_human" log INFO "[MySQL监测] $HOST_NAME 当前连接数=$conn 平均=$avg_conn 峰值=${MYSQL_PEAK_CONN}@$peak_human"
# 暴涨判定
local window_seconds=300 local window_seconds=300
local min_burst_conn=200 local min_burst_conn=200
local rate_threshold_per_sec=1 local rate_threshold_per_sec=1
...@@ -391,6 +440,8 @@ monitor_mysql_once() { ...@@ -391,6 +440,8 @@ monitor_mysql_once() {
if [[ -z "${MYSQL_LAST_TOTAL:-}" || -z "${MYSQL_LAST_TS:-}" ]]; then if [[ -z "${MYSQL_LAST_TOTAL:-}" || -z "${MYSQL_LAST_TS:-}" ]]; then
MYSQL_LAST_TOTAL="$conn" MYSQL_LAST_TOTAL="$conn"
MYSQL_LAST_TS="$now_ts" MYSQL_LAST_TS="$now_ts"
MYSQL_LAST_BURST_STATUS="INIT"
MYSQL_LAST_BURST_DESC="初始化窗口,总连接数=${conn}"
return return
fi fi
...@@ -409,12 +460,120 @@ monitor_mysql_once() { ...@@ -409,12 +460,120 @@ monitor_mysql_once() {
start_ts_human="$(date -d @"$((now_ts - elapsed))" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')" start_ts_human="$(date -d @"$((now_ts - elapsed))" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
end_ts_human="$(date -d @"$now_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')" end_ts_human="$(date -d @"$now_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
local desc="窗口=${elapsed}s 增量=${delta_conn} 速率=${rate}/s 时间段=[${start_ts_human} ~ ${end_ts_human}]"
if (( delta_conn >= min_burst_conn )) || awk -v r="$rate" -v th="$rate_threshold_per_sec" 'BEGIN{exit !(r>=th)}'; then if (( delta_conn >= min_burst_conn )) || awk -v r="$rate" -v th="$rate_threshold_per_sec" 'BEGIN{exit !(r>=th)}'; then
log WARN "[MySQL连接暴涨] $HOST_NAME 窗口=${elapsed}s 增量=${delta_conn} 速率=${rate}/s 时间段=[${start_ts_human} ~ ${end_ts_human}]" log WARN "[MySQL连接暴涨] $HOST_NAME ${desc}"
MYSQL_LAST_BURST_STATUS="BURST"
MYSQL_LAST_BURST_DESC="$desc"
else else
log INFO "[MySQL暴涨审计] $HOST_NAME 窗口=${elapsed}s 增量=${delta_conn} 速率=${rate}/s 未发现暴涨" log INFO "[MySQL暴涨审计] $HOST_NAME ${desc} 未发现暴涨"
MYSQL_LAST_BURST_STATUS="OK"
MYSQL_LAST_BURST_DESC="$desc"
fi fi
else
MYSQL_LAST_BURST_STATUS="COLLECTING"
MYSQL_LAST_BURST_DESC="窗口${elapsed}s/未达${window_seconds}s,当前连接数=${conn}"
fi
}
#################### 5. 监测报告输出(md 文件) ####################
write_md_report() {
mkdir -p "$REPORT_DIR" 2>/dev/null || true
local ts file_ts report_file
ts="$(date '+%Y-%m-%d %H:%M:%S')"
file_ts="$(date '+%Y%m%d_%H%M%S')"
report_file="${REPORT_DIR}/monitor_report_${HOST_NAME}_${file_ts}.md"
local platform_text
if [[ "$PLATFORM_TYPE" == "new" ]]; then
platform_text="新统一平台 (/data/services)"
else
platform_text="传统平台 (/var/www)"
fi
local systems_text="无"
if ((${#SYSTEMS[@]} > 0)); then
systems_text="${SYSTEMS[*]}"
fi
local mem_avg_used="N/A"
if (( MEM_SAMPLES > 0 )); then
mem_avg_used=$(( MEM_SUM_USED_MB / MEM_SAMPLES ))
fi
local mem_peak_time="N/A"
if [[ "$MEM_PEAK_TS" != "0" && -n "$MEM_PEAK_TS" ]]; then
mem_peak_time="$(date -d @"$MEM_PEAK_TS" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
fi
local mysql_avg_conn="N/A"
if (( MYSQL_SAMPLES > 0 )); then
mysql_avg_conn=$(( MYSQL_SUM_CONN / MYSQL_SAMPLES ))
fi fi
local mysql_peak_time="N/A"
if [[ -n "$MYSQL_PEAK_TS" && "$MYSQL_PEAK_TS" != "0" ]]; then
mysql_peak_time="$(date -d @"$MYSQL_PEAK_TS" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')"
fi
{
echo "# 自动化服务监测报告"
echo
echo "- 生成时间:${ts}"
echo "- 主机名:${HOST_NAME}"
echo "- 平台类型:${platform_text}"
echo "- 系统识别:ujava=${HAS_UJAVA},upython=${HAS_UPYTHON},upython_voice=${HAS_UPYTHON_VOICE}"
echo "- 系统列表:${systems_text}"
echo
echo "## 一、日志审计概览"
echo
resolve_log_targets
if ((${#LOG_TARGET_SYS[@]} == 0)); then
echo "- 当前未匹配到需要审计的日志目标(例如未识别到会议预定系统 ujava 容器)。"
else
for ((i=0; i<${#LOG_TARGET_SYS[@]}; i++)); do
local sys_name="${LOG_TARGET_SYS[$i]}"
local log_path="${LOG_TARGET_PATH[$i]}"
local key
key="$(make_log_key "$sys_name" "$log_path")"
local burst_status="${BURST_LAST_RESULT[$key]-未采集}"
local burst_desc="${BURST_LAST_DESC[$key]-无}"
local err_cnt="${ERROR_LAST_COUNT[$key]-0}"
local err_range="${ERROR_LAST_TIME_RANGE[$key]-无}"
echo "### 日志:${sys_name}"
echo
echo "- 日志路径:\`${log_path}\`"
echo "- 日志暴涨状态:**${burst_status}**"
echo "- 日志暴涨详情:${burst_desc}"
echo "- 最近 ERROR 数量:**${err_cnt}**"
echo "- 最近 ERROR 时间段:${err_range}"
echo
done
fi
echo "## 二、内存资源消耗"
echo
echo "- 当前内存使用:${MEM_LAST_USED_MB} MB"
echo "- 内存使用平均值:${mem_avg_used} MB"
echo "- 内存使用峰值:${MEM_PEAK_USED_MB} MB"
echo "- 峰值发生时间:${mem_peak_time}"
echo
echo "## 三、MySQL 连接数监测"
echo
echo "- 当前 MySQL 连接数:${MYSQL_LAST_CONN}"
echo "- MySQL 连接平均值:${mysql_avg_conn}"
echo "- MySQL 连接峰值:${MYSQL_PEAK_CONN}"
echo "- 峰值发生时间:${mysql_peak_time}"
echo "- 最近暴涨判定状态:**${MYSQL_LAST_BURST_STATUS}**"
echo "- 暴涨详情:${MYSQL_LAST_BURST_DESC}"
echo
echo "> 说明:本报告由 \`AutomatedServiceMonitoring.sh\` 自动生成,仅反映最近一段时间的监测结果。"
echo
} > "$report_file"
log INFO "[报告输出] 已生成监测报告:${report_file}"
} }
#################### 主循环:监控本机 #################### #################### 主循环:监控本机 ####################
...@@ -426,17 +585,17 @@ main_loop() { ...@@ -426,17 +585,17 @@ main_loop() {
log INFO "[平台识别] $HOST_NAME => 平台类型=$([[ "$PLATFORM_TYPE" == "new" ]] && echo 新统一平台(/data/services) || echo 传统平台(/var/www)) 基路径=${BASE_PATH}" log INFO "[平台识别] $HOST_NAME => 平台类型=$([[ "$PLATFORM_TYPE" == "new" ]] && echo 新统一平台(/data/services) || echo 传统平台(/var/www)) 基路径=${BASE_PATH}"
log INFO "[系统识别] $HOST_NAME => ujava=${HAS_UJAVA} upython=${HAS_UPYTHON} upython_voice=${HAS_UPYTHON_VOICE} 系统=(${SYSTEMS[*]:-})" log INFO "[系统识别] $HOST_NAME => ujava=${HAS_UJAVA} upython=${HAS_UPYTHON} upython_voice=${HAS_UPYTHON_VOICE} 系统=(${SYSTEMS[*]:-})"
local last_report_ts
last_report_ts="$(date +%s)"
while true; do while true; do
log INFO "[心跳] $HOST_NAME: 平台=$([[ "$PLATFORM_TYPE" == "new" ]] && echo || echo ) 基路径=${BASE_PATH} 系统=${SYSTEMS[*]:-}" log INFO "[心跳] $HOST_NAME: 平台=$([[ "$PLATFORM_TYPE" == "new" ]] && echo || echo ) 基路径=${BASE_PATH} 系统=${SYSTEMS[*]:-}"
resolve_log_targets
# 日志审计 # 日志审计
local i resolve_log_targets
for ((i=0; i<${#LOG_TARGET_SYS[@]}; i++)); do for ((i=0; i<${#LOG_TARGET_SYS[@]}; i++)); do
local sys_name="${LOG_TARGET_SYS[$i]}" local sys_name="${LOG_TARGET_SYS[$i]}"
local log_path="${LOG_TARGET_PATH[$i]}" local log_path="${LOG_TARGET_PATH[$i]}"
monitor_log_burst_once "$sys_name" "$log_path" monitor_log_burst_once "$sys_name" "$log_path"
monitor_log_errors_once "$sys_name" "$log_path" monitor_log_errors_once "$sys_name" "$log_path"
done done
...@@ -447,6 +606,15 @@ main_loop() { ...@@ -447,6 +606,15 @@ main_loop() {
# MySQL 监测 # MySQL 监测
monitor_mysql_once monitor_mysql_once
# 报告输出节奏控制
local now_ts
now_ts="$(date +%s)"
local elapsed=$(( now_ts - last_report_ts ))
if (( elapsed >= REPORT_INTERVAL_SECONDS )); then
write_md_report
last_report_ts="$now_ts"
fi
sleep "$INTERVAL_SECONDS" sleep "$INTERVAL_SECONDS"
done done
} }
......
...@@ -13,26 +13,19 @@ ...@@ -13,26 +13,19 @@
## 🎯 功能实现总览 ## 🎯 功能实现总览
### 服务监测 (`AutomatedServiceMonitoring.py`) ### 服务监测 (`AutomatedServiceMonitoring.sh`)
脚本路径:E:\GithubData\自动化\ubains-module-test\助工具\脚本工具\自动化服务监测\AutomatedServiceMonitoring.py 脚本路径:E:\GithubData\自动化\ubains-module-test\AuxiliaryTool\ScriptTool\自动化服务监测\AutomatedServiceMonitoring.sh
#### 检测需求 #### 检测需求
##### 1、SSH连接(✅ 已实现): ##### 1、平台识别(✅ 已实现):
支持预设服务器列表
1、192.168.5.48 root Ubains@1234
2、192.168.5.44 root Ubains@123
##### 2、平台识别(✅ 已实现):
自动检测目标服务器平台类型(检测 /data/services 目录,如果没有则是传统平台) 自动检测目标服务器平台类型(检测 /data/services 目录,如果没有则是传统平台)
##### 3、系统识别(✅ 已实现): ##### 2、系统识别(✅ 已实现):
自动检测目标服务器的系统类型(检测容器分为三种:ujava、upython、upython_voice,如果有ujava则有会议预定系统、python对应运维集控系统、upython_voice对应转录系统) 自动检测目标服务器的系统类型(检测容器分为三种:ujava、upython、upython_voice,如果有ujava则有会议预定系统、python对应运维集控系统、upython_voice对应转录系统)
##### 4、服务监测(未完成): ##### 3、服务监测(未完成):
4.1、多连接(✅ 已实现): 3.1、日志审计(✅ 已实现):
可支持连接多台服务器
4.2、日志审计(✅ 已实现):
- 根据平台类型持续审计日志信息在某个时间段是否存在暴涨的情况: - 根据平台类型持续审计日志信息在某个时间段是否存在暴涨的情况:
每次采样获取日志总行数 total_lines 和采样时间 now_ts。 每次采样获取日志总行数 total_lines 和采样时间 now_ts。
与上次采样 last_total、last_ts 比较,得到 elapsed 和 delta_lines。 与上次采样 last_total、last_ts 比较,得到 elapsed 和 delta_lines。
...@@ -58,16 +51,19 @@ ...@@ -58,16 +51,19 @@
路径:/var/www/java/api-java-meeting2.0 路径:/var/www/java/api-java-meeting2.0
日志打印:tail -f logs/ubains-INFO-AND-ERROR.log 日志打印:tail -f logs/ubains-INFO-AND-ERROR.log
4.3、内存资源消耗(✅ 已实现): 3.2、内存资源消耗(✅ 已实现):
根据平台类型持续监测服务器内存占用情况,记录峰值与峰值时的时间点,以及记录平均值。 根据平台类型持续监测服务器内存占用情况,记录峰值与峰值时的时间点,以及记录平均值。
4.4、mysql连接数(✅ 已实现): 3.3、mysql连接数(✅ 已实现):
根据平台类型持续监测mysql连接数在某个时间段是否存在暴涨,或是异常一直没断开的连接数。 根据平台类型持续监测mysql连接数在某个时间段是否存在暴涨,或是异常一直没断开的连接数。
先检查mysql容器名称,然后通过进入mysql容器内部进行查询,mysql账号为root,密码为dNrprU&2S 先检查mysql容器名称,然后通过进入mysql容器内部进行查询,mysql账号为root,密码为dNrprU&2S
4.5、emqx连接数 3.4、emqx连接数(未实现)
根据平台类型持续监测EMQX连接数量峰值、平均值,以及是否存在暴涨情况,或是判断一直没断开的异常连接。 根据平台类型持续监测EMQX连接数量峰值、平均值,以及是否存在暴涨情况,或是判断一直没断开的异常连接。
##### 5、监测日志审计(✅ 已实现): ##### 4、监测日志审计(✅ 已实现):
需要丰富日志体系,日志需要用中文打印 需要丰富日志体系,日志需要用中文打印
##### 6、对接钉钉消息发送: ##### 5、监测报告输出(未实现):
钉钉函数参考: 将本次监测的所有检测项信息输出成md文件报告,内容排版需要优美。
\ No newline at end of file
##### 5、对接邮件发送:
需要能实现将当前的监测后输出的md文件报告,以邮件的形式发送给czj@huazhaochina.com
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论