""" Log Manager - 持久化日志管理器 支持日志加载、保存、追加、清空 """ import json import os import threading from datetime import datetime from typing import List, Dict, Optional from app import config class LogManager: """日志管理器 - 线程安全""" def __init__(self, log_file: str = None, max_logs: int = None): self.log_file = log_file or config.LOG_FILE self.max_logs = max_logs or config.MAX_LOGS self._lock = threading.Lock() self.logs: List[Dict] = [] self._load_logs() def _load_logs(self): """启动时加载历史日志""" with self._lock: if os.path.exists(self.log_file): try: with open(self.log_file, 'r', encoding='utf-8') as f: data = json.load(f) self.logs = data.get('logs', []) except (json.JSONDecodeError, IOError) as e: print(f"[LogManager] 加载日志文件失败: {e}") self.logs = [] else: self.logs = [] def append(self, level: str, message: str, thread_id: Optional[str] = None) -> Dict: """ 追加新日志 返回日志条目 """ with self._lock: log_entry = { "timestamp": datetime.now().isoformat(), "level": level, "message": message, "thread_id": thread_id } self.logs.append(log_entry) # 限制日志数量(保留最新的 max_logs 条) if len(self.logs) > self.max_logs: self.logs = self.logs[-self.max_logs:] # 异步保存到文件 self._save() return log_entry def _save(self): """保存到文件(需在锁内调用)""" try: with open(self.log_file, 'w', encoding='utf-8') as f: json.dump({ "logs": self.logs, "last_updated": datetime.now().isoformat() }, f, ensure_ascii=False, indent=2) except IOError as e: print(f"[LogManager] 保存日志文件失败: {e}") def get_logs(self, level: Optional[str] = None, limit: Optional[int] = None) -> List[Dict]: """ 获取日志 :param level: 过滤级别 (None/INFO/SUCCESS/WARNING/ERROR/DEBUG) :param limit: 限制返回数量 """ with self._lock: if level: filtered = [log for log in self.logs if log['level'] == level] else: filtered = self.logs if limit: return filtered[-limit:] return filtered def search_logs(self, keyword: str, limit: Optional[int] = None) -> List[Dict]: """ 搜索日志 :param keyword: 搜索关键词 :param limit: 限制返回数量 """ with self._lock: filtered = [ log for log in self.logs if keyword.lower() in log['message'].lower() ] if limit: return filtered[-limit:] return filtered def clear(self): """清空日志""" with self._lock: self.logs = [] self._save() def get_stats(self) -> Dict: """获取日志统计信息""" with self._lock: stats = { "total": len(self.logs), "by_level": {} } for log in self.logs: level = log['level'] stats["by_level"][level] = stats["by_level"].get(level, 0) + 1 return stats def export_to_text(self) -> str: """导出为文本格式""" with self._lock: lines = [] lines.append("=" * 80) lines.append("小红书采集器日志") lines.append(f"导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") lines.append(f"总日志数: {len(self.logs)}") lines.append("=" * 80) lines.append("") for log in self.logs: timestamp = log['timestamp'][:19] # 去掉微秒 level = log['level'].ljust(8) thread = f"[{log['thread_id']}]" if log.get('thread_id') else "" message = log['message'] lines.append(f"{timestamp} | {level} | {thread} {message}") return "\n".join(lines) # 全局日志管理器实例 _log_manager_instance = None def get_log_manager() -> LogManager: """获取全局日志管理器实例""" global _log_manager_instance if _log_manager_instance is None: _log_manager_instance = LogManager() return _log_manager_instance