Files

184 lines
5.3 KiB
Python
Raw Permalink Normal View History

2026-03-06 11:26:35 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
2026-03-06 17:07:57 +08:00
日志记录模块
OOP设计的日志记录器
2026-03-06 11:26:35 +08:00
"""
import json
from datetime import datetime
2026-03-06 17:07:57 +08:00
from typing import List, Dict, Any, Optional
2026-03-06 11:26:35 +08:00
from pathlib import Path
2026-03-06 17:07:57 +08:00
from dataclasses import dataclass
@dataclass
class LogEvent:
"""日志事件数据类"""
timestamp: str
event_type: str
message: str
data: Dict[str, Any]
def to_dict(self) -> Dict[str, Any]:
"""转换为字典"""
return {
'timestamp': self.timestamp,
'type': self.event_type,
'message': self.message,
'data': self.data
}
2026-03-06 11:26:35 +08:00
class Logger:
"""日志记录器类"""
def __init__(self, log_dir: str = "logs", max_log_files: int = 10):
"""
初始化日志记录器
Args:
2026-03-06 17:07:57 +08:00
log_dir: 日志目录
max_log_files: 最大日志文件数
2026-03-06 11:26:35 +08:00
"""
self.log_dir = Path(log_dir)
self.max_log_files = max_log_files
2026-03-06 17:07:57 +08:00
self.events: List[LogEvent] = []
self.session_start = datetime.now()
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
self._init_directory()
2026-03-06 11:26:35 +08:00
self._cleanup_old_logs()
2026-03-06 17:07:57 +08:00
def _init_directory(self) -> None:
"""初始化日志目录"""
self.log_dir.mkdir(parents=True, exist_ok=True)
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
def _cleanup_old_logs(self) -> None:
"""清理旧日志文件"""
2026-03-06 11:26:35 +08:00
log_files = sorted(
self.log_dir.glob("network_monitor_*.log"),
key=lambda x: x.stat().st_mtime,
reverse=True
)
for old_log in log_files[self.max_log_files:]:
old_log.unlink()
2026-03-06 17:07:57 +08:00
def _generate_filename(self) -> str:
"""生成日志文件名"""
timestamp = self.session_start.strftime("%Y%m%d_%H%M%S")
return f"network_monitor_{timestamp}.log"
def log(self, event_type: str, message: str, **data) -> None:
2026-03-06 11:26:35 +08:00
"""
2026-03-06 17:07:57 +08:00
记录日志事件
2026-03-06 11:26:35 +08:00
Args:
2026-03-06 17:07:57 +08:00
event_type: 事件类型
message: 事件消息
**data: 附加数据
"""
event = LogEvent(
timestamp=datetime.now().isoformat(),
event_type=event_type,
message=message,
data=data
)
self.events.append(event)
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
def log_ping(self, success: bool, latency: float, error: Optional[str] = None) -> None:
2026-03-06 11:26:35 +08:00
"""
2026-03-06 17:07:57 +08:00
记录ping事件
2026-03-06 11:26:35 +08:00
Args:
2026-03-06 17:07:57 +08:00
success: 是否成功
latency: 延迟(毫秒)
error: 错误信息
2026-03-06 11:26:35 +08:00
"""
2026-03-06 17:07:57 +08:00
self.log(
2026-03-06 11:26:35 +08:00
"ping",
2026-03-06 17:07:57 +08:00
"Ping执行成功" if success else "Ping执行失败",
2026-03-06 11:26:35 +08:00
success=success,
2026-03-06 17:07:57 +08:00
latency_ms=latency,
error=error
2026-03-06 11:26:35 +08:00
)
2026-03-06 17:07:57 +08:00
def log_status_change(self, status: str, duration: Optional[float] = None) -> None:
2026-03-06 11:26:35 +08:00
"""
记录状态变化
Args:
2026-03-06 17:07:57 +08:00
status: 状态(connected/disconnected
duration: 持续时间
2026-03-06 11:26:35 +08:00
"""
message = f"网络状态变化: {status}"
if duration is not None:
message += f" (持续{duration:.1f}秒)"
2026-03-06 17:07:57 +08:00
self.log("status_change", message, status=status, duration_seconds=duration)
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
def log_error(self, error_type: str, error_message: str) -> None:
2026-03-06 11:26:35 +08:00
"""
记录错误
Args:
error_type: 错误类型
2026-03-06 17:07:57 +08:00
error_message: 错误消息
2026-03-06 11:26:35 +08:00
"""
2026-03-06 17:07:57 +08:00
self.log("error", f"{error_type}: {error_message}", error_type=error_type)
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
def log_shutdown(self, reason: str) -> None:
2026-03-06 11:26:35 +08:00
"""
记录关机事件
Args:
reason: 关机原因
"""
2026-03-06 17:07:57 +08:00
self.log("shutdown", f"系统关机: {reason}", reason=reason)
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
def save(self) -> str:
"""
保存日志到文件
Returns:
日志文件路径
"""
log_file = self.log_dir / self._generate_filename()
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
session = {
'session_start': self.session_start.isoformat(),
'session_end': datetime.now().isoformat(),
'total_events': len(self.events),
'events': [event.to_dict() for event in self.events]
2026-03-06 11:26:35 +08:00
}
with open(log_file, 'w', encoding='utf-8') as f:
2026-03-06 17:07:57 +08:00
json.dump(session, f, ensure_ascii=False, indent=2)
2026-03-06 11:26:35 +08:00
2026-03-06 17:07:57 +08:00
return str(log_file)
2026-03-06 11:26:35 +08:00
def get_statistics(self) -> Dict[str, Any]:
"""
2026-03-06 17:07:57 +08:00
获取统计信息
2026-03-06 11:26:35 +08:00
Returns:
2026-03-06 17:07:57 +08:00
统计信息字典
2026-03-06 11:26:35 +08:00
"""
2026-03-06 17:07:57 +08:00
ping_events = [e for e in self.events if e.event_type == "ping"]
successful_pings = [e for e in ping_events if e.data.get("success", False)]
2026-03-06 11:26:35 +08:00
return {
2026-03-06 17:07:57 +08:00
'total_events': len(self.events),
'total_pings': len(ping_events),
'successful_pings': len(successful_pings),
'failed_pings': len(ping_events) - len(successful_pings),
'success_rate': self._calculate_success_rate(ping_events, successful_pings),
'session_duration': (datetime.now() - self.session_start).total_seconds()
2026-03-06 11:26:35 +08:00
}
2026-03-06 17:07:57 +08:00
def _calculate_success_rate(self, all_pings: List[LogEvent], successful: List[LogEvent]) -> str:
"""计算成功率"""
if not all_pings:
return "0%"
return f"{(len(successful) / len(all_pings) * 100):.2f}%"