Files

343 lines
14 KiB
Python

#!/usr/bin/env python3
"""
系统性能监控脚本
实时监控系统资源和应用性能指标
"""
import time
import json
import subprocess
import threading
import signal
import sys
from datetime import datetime
from pathlib import Path
import requests
from dataclasses import dataclass, asdict
from typing import Dict, List, Any, Optional
@dataclass
class SystemMetrics:
timestamp: str
cpu_percent: float
memory_percent: float
memory_used_gb: float
memory_total_gb: float
disk_usage_percent: float
network_connections: int
load_average: List[float]
@dataclass
class ApplicationMetrics:
timestamp: str
response_time: float
request_count: int
success_rate: float
queue_size: int
worker_threads: int
processed_total: int
errors: int
class SystemMonitor:
"""系统性能监控器"""
def __init__(self, app_url: str = "http://localhost:5225",
interval: float = 2.0, output_file: str = None):
self.app_url = app_url
self.interval = interval
self.running = False
# 输出文件
if output_file is None:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
self.output_file = f"logs/system_monitor_{timestamp}.log"
else:
self.output_file = output_file
# 确保日志目录存在
Path(self.output_file).parent.mkdir(exist_ok=True)
# 数据存储
self.system_metrics_history = []
self.app_metrics_history = []
# 信号处理
signal.signal(signal.SIGINT, self._signal_handler)
signal.signal(signal.SIGTERM, self._signal_handler)
print(f"📊 系统监控器初始化完成")
print(f"📝 监控日志: {self.output_file}")
print(f"🎯 应用URL: {app_url}")
print(f"⏱️ 监控间隔: {interval}秒")
def _signal_handler(self, signum, frame):
"""信号处理器"""
print(f"\n收到信号 {signum}, 停止监控...")
self.running = False
def get_system_metrics(self) -> SystemMetrics:
"""获取系统指标"""
try:
# CPU使用率
cpu_cmd = "top -l 1 -n 0 | grep 'CPU usage' | awk '{print $3}' | sed 's/%//'"
cpu_result = subprocess.run(cpu_cmd, shell=True, capture_output=True, text=True)
cpu_percent = float(cpu_result.stdout.strip()) if cpu_result.stdout.strip() else 0.0
# 内存使用情况
vm_stat = subprocess.run(['vm_stat'], capture_output=True, text=True)
if vm_stat.returncode == 0:
lines = vm_stat.stdout.split('\n')
page_size = 4096 # macOS页面大小
free_pages = 0
inactive_pages = 0
speculative_pages = 0
wired_pages = 0
active_pages = 0
for line in lines:
if 'Pages free:' in line:
free_pages = int(line.split(':')[1].strip().rstrip('.'))
elif 'Pages inactive:' in line:
inactive_pages = int(line.split(':')[1].strip().rstrip('.'))
elif 'Pages speculative:' in line:
speculative_pages = int(line.split(':')[1].strip().rstrip('.'))
elif 'Pages wired down:' in line:
wired_pages = int(line.split(':')[1].strip().rstrip('.'))
elif 'Pages active:' in line:
active_pages = int(line.split(':')[1].strip().rstrip('.'))
# 计算内存使用情况
total_pages = free_pages + inactive_pages + speculative_pages + wired_pages + active_pages
used_pages = wired_pages + active_pages
memory_total_gb = (total_pages * page_size) / (1024**3)
memory_used_gb = (used_pages * page_size) / (1024**3)
memory_percent = (used_pages / total_pages * 100) if total_pages > 0 else 0
else:
memory_total_gb = memory_used_gb = memory_percent = 0.0
# 磁盘使用率
df_result = subprocess.run(['df', '-h', '/'], capture_output=True, text=True)
disk_usage_percent = 0.0
if df_result.returncode == 0:
lines = df_result.stdout.split('\n')
if len(lines) > 1:
fields = lines[1].split()
if len(fields) >= 5:
disk_usage_percent = float(fields[4].rstrip('%'))
# 网络连接数
netstat_result = subprocess.run(['netstat', '-an'], capture_output=True, text=True)
network_connections = len([line for line in netstat_result.stdout.split('\n')
if 'ESTABLISHED' in line]) if netstat_result.returncode == 0 else 0
# 负载平均值
uptime_result = subprocess.run(['uptime'], capture_output=True, text=True)
load_average = [0.0, 0.0, 0.0]
if uptime_result.returncode == 0:
try:
uptime_line = uptime_result.stdout.strip()
load_part = uptime_line.split('load averages: ')[1]
load_values = [float(x.strip()) for x in load_part.split()]
load_average = load_values[:3]
except:
pass
return SystemMetrics(
timestamp=datetime.now().isoformat(),
cpu_percent=cpu_percent,
memory_percent=memory_percent,
memory_used_gb=memory_used_gb,
memory_total_gb=memory_total_gb,
disk_usage_percent=disk_usage_percent,
network_connections=network_connections,
load_average=load_average
)
except Exception as e:
print(f"❌ 获取系统指标失败: {e}")
return SystemMetrics(
timestamp=datetime.now().isoformat(),
cpu_percent=0, memory_percent=0, memory_used_gb=0, memory_total_gb=0,
disk_usage_percent=0, network_connections=0, load_average=[0, 0, 0]
)
def get_application_metrics(self) -> ApplicationMetrics:
"""获取应用指标"""
try:
# 测试响应时间
start_time = time.time()
health_response = requests.get(f"{self.app_url}/health", timeout=5)
response_time = time.time() - start_time
# 获取优化器统计
stats_response = requests.get(f"{self.app_url}/get_optimizer_stats", timeout=5)
optimizer_stats = stats_response.json() if stats_response.status_code == 200 else {}
# 尝试获取异步系统统计
async_stats = {}
try:
async_response = requests.get(f"{self.app_url}/get_async_system_stats", timeout=5)
if async_response.status_code == 200:
async_stats = async_response.json()
except:
pass
return ApplicationMetrics(
timestamp=datetime.now().isoformat(),
response_time=response_time,
request_count=optimizer_stats.get('processed', 0),
success_rate=100.0 if health_response.status_code == 200 else 0.0,
queue_size=optimizer_stats.get('queue_size', 0),
worker_threads=optimizer_stats.get('max_workers', 0),
processed_total=optimizer_stats.get('processed', 0),
errors=optimizer_stats.get('errors', 0)
)
except Exception as e:
print(f"❌ 获取应用指标失败: {e}")
return ApplicationMetrics(
timestamp=datetime.now().isoformat(),
response_time=0, request_count=0, success_rate=0,
queue_size=0, worker_threads=0, processed_total=0, errors=0
)
def log_metrics(self, system_metrics: SystemMetrics, app_metrics: ApplicationMetrics):
"""记录指标到日志文件"""
try:
with open(self.output_file, 'a') as f:
log_entry = {
'timestamp': datetime.now().isoformat(),
'system': asdict(system_metrics),
'application': asdict(app_metrics)
}
f.write(json.dumps(log_entry) + '\n')
except Exception as e:
print(f"❌ 写入日志失败: {e}")
def display_metrics(self, system_metrics: SystemMetrics, app_metrics: ApplicationMetrics):
"""在控制台显示指标"""
timestamp = datetime.now().strftime('%H:%M:%S')
print(f"\r📊 [{timestamp}] "
f"CPU: {system_metrics.cpu_percent:5.1f}% | "
f"MEM: {system_metrics.memory_percent:5.1f}% "
f"({system_metrics.memory_used_gb:.1f}GB/{system_metrics.memory_total_gb:.1f}GB) | "
f"DISK: {system_metrics.disk_usage_percent:4.1f}% | "
f"NET: {system_metrics.network_connections:3d} | "
f"LOAD: {system_metrics.load_average[0]:.2f} | "
f"APP_RT: {app_metrics.response_time*1000:6.1f}ms | "
f"QUEUE: {app_metrics.queue_size:4d} | "
f"PROC: {app_metrics.processed_total:6d} | "
f"ERR: {app_metrics.errors:3d}", end='', flush=True)
def start_monitoring(self):
"""开始监控"""
print(f"\n🔍 开始系统监控 (按 Ctrl+C 停止)")
print("=" * 120)
print("时间 | CPU% | 内存使用 | 磁盘% | 连接数 | 负载 | 应用RT(ms) | 队列 | 处理数 | 错误")
print("-" * 120)
self.running = True
try:
while self.running:
# 获取指标
system_metrics = self.get_system_metrics()
app_metrics = self.get_application_metrics()
# 存储历史数据
self.system_metrics_history.append(system_metrics)
self.app_metrics_history.append(app_metrics)
# 保持历史记录数量(最近500个数据点)
if len(self.system_metrics_history) > 500:
self.system_metrics_history.pop(0)
if len(self.app_metrics_history) > 500:
self.app_metrics_history.pop(0)
# 记录日志
self.log_metrics(system_metrics, app_metrics)
# 显示指标
self.display_metrics(system_metrics, app_metrics)
# 等待下次采集
time.sleep(self.interval)
except KeyboardInterrupt:
print(f"\n\n⏸️ 监控已停止")
self.generate_summary_report()
def generate_summary_report(self):
"""生成监控总结报告"""
if not self.system_metrics_history or not self.app_metrics_history:
print("📊 无足够数据生成报告")
return
print(f"\n" + "=" * 80)
print("📊 监控总结报告")
print("=" * 80)
# 系统指标统计
cpu_values = [m.cpu_percent for m in self.system_metrics_history]
memory_values = [m.memory_percent for m in self.system_metrics_history]
load_values = [m.load_average[0] for m in self.system_metrics_history]
print(f"🖥️ 系统指标:")
print(f" CPU使用率 - 平均: {sum(cpu_values)/len(cpu_values):5.1f}% "
f"最高: {max(cpu_values):5.1f}% 最低: {min(cpu_values):5.1f}%")
print(f" 内存使用率 - 平均: {sum(memory_values)/len(memory_values):5.1f}% "
f"最高: {max(memory_values):5.1f}% 最低: {min(memory_values):5.1f}%")
print(f" 系统负载 - 平均: {sum(load_values)/len(load_values):5.2f} "
f"最高: {max(load_values):5.2f}")
# 应用指标统计
response_times = [m.response_time * 1000 for m in self.app_metrics_history] # 转换为毫秒
queue_sizes = [m.queue_size for m in self.app_metrics_history]
print(f"\n📱 应用指标:")
print(f" 响应时间(ms) - 平均: {sum(response_times)/len(response_times):6.1f} "
f"最高: {max(response_times):6.1f} 最低: {min(response_times):6.1f}")
print(f" 队列深度 - 平均: {sum(queue_sizes)/len(queue_sizes):6.1f} "
f"最高: {max(queue_sizes):6d}")
# 性能趋势分析
latest_metrics = self.app_metrics_history[-10:] # 最近10个数据点
if len(latest_metrics) >= 10:
recent_avg_rt = sum(m.response_time * 1000 for m in latest_metrics) / len(latest_metrics)
print(f" 最近10次平均响应时间: {recent_avg_rt:.1f}ms")
print(f"\n📝 详细日志已保存到: {self.output_file}")
print(f"📊 总监控时间: {len(self.system_metrics_history) * self.interval:.1f}秒")
def main():
"""主函数"""
import argparse
parser = argparse.ArgumentParser(description='系统性能监控工具')
parser.add_argument('--url', default='http://localhost:5225',
help='应用服务URL (默认: http://localhost:5225)')
parser.add_argument('--interval', type=float, default=2.0,
help='监控间隔秒数 (默认: 2.0)')
parser.add_argument('--output', help='输出日志文件路径')
args = parser.parse_args()
print("🚀 系统性能监控工具")
print("=" * 50)
# 创建监控器
monitor = SystemMonitor(
app_url=args.url,
interval=args.interval,
output_file=args.output
)
# 启动监控
monitor.start_monitoring()
if __name__ == "__main__":
main()