diff --git a/agents/haonan/workspace/dev-workflow/stats.py b/agents/haonan/workspace/dev-workflow/stats.py index f06f7da..25c5300 100644 --- a/agents/haonan/workspace/dev-workflow/stats.py +++ b/agents/haonan/workspace/dev-workflow/stats.py @@ -37,26 +37,43 @@ def _ensure_stats_dir(): STATS_DIR.mkdir(parents=True, exist_ok=True) -# 锁文件路径 -LOCK_FILE = STATS_DIR / "stats_data.lock" - - def _acquire_lock(lock_file_path: Path): - """获取文件锁""" - lock_file_path.parent.mkdir(parents=True, exist_ok=True) - lock_fd = open(lock_file_path, 'w') - fcntl.flock(lock_fd.fileno(), fcntl.LOCK_EX) - return lock_fd + """获取文件锁 + + Raises: + OSError: 无法创建或锁定文件时 + """ + try: + lock_file_path.parent.mkdir(parents=True, exist_ok=True) + lock_fd = open(lock_file_path, 'w') + fcntl.flock(lock_fd.fileno(), fcntl.LOCK_EX) + return lock_fd + except (OSError, IOError) as e: + logger.error(f"获取文件锁失败: {e}") + raise def _release_lock(lock_fd): """释放文件锁""" - fcntl.flock(lock_fd.fileno(), fcntl.LOCK_UN) - lock_fd.close() + if lock_fd is None: + return + try: + fcntl.flock(lock_fd.fileno(), fcntl.LOCK_UN) + lock_fd.close() + except (OSError, IOError) as e: + logger.warning(f"释放文件锁时发生错误: {e}") + try: + lock_fd.close() + except Exception: + pass def _load_stats_data() -> Dict[str, Any]: - """加载统计数据(带锁保护)""" + """加载统计数据(带锁保护) + + Returns: + Dict: 统计数据,加载失败时返回空结构 + """ if not STATS_FILE.exists(): return {"events": [], "metadata": {"created_at": datetime.now().isoformat()}} @@ -66,22 +83,32 @@ def _load_stats_data() -> Dict[str, Any]: with open(STATS_FILE, 'r', encoding='utf-8') as f: return json.load(f) except json.JSONDecodeError as e: + logger.warning(f"JSON 解析失败,将备份损坏文件: {e}") # 备份损坏的文件 backup_path = STATS_FILE.with_suffix(f'.corrupted.{datetime.now().strftime("%Y%m%d_%H%M%S")}.bak') try: STATS_FILE.rename(backup_path) - except OSError: - pass # 备份失败时忽略 + logger.info(f"已备份损坏文件到: {backup_path}") + except OSError as backup_error: + logger.error(f"备份损坏文件失败: {backup_error}") return {"events": [], "metadata": {"created_at": datetime.now().isoformat()}} - except IOError: + except (OSError, IOError) as e: + logger.error(f"读取统计文件失败: {e}") + return {"events": [], "metadata": {"created_at": datetime.now().isoformat()}} + except Exception as e: + logger.error(f"加载统计数据时发生未知错误: {e}") return {"events": [], "metadata": {"created_at": datetime.now().isoformat()}} finally: - if lock_fd: - _release_lock(lock_fd) + _release_lock(lock_fd) def _atomic_write(data: Dict[str, Any]): - """原子写入统计数据(带锁保护)""" + """原子写入统计数据(带锁保护) + + Raises: + OSError: 文件操作失败时 + TypeError: 数据无法序列化为 JSON 时 + """ _ensure_stats_dir() lock_fd = None @@ -103,12 +130,20 @@ def _atomic_write(data: Dict[str, Any]): # 原子重命名 os.replace(temp_path, STATS_FILE) temp_path = None # 防止在 finally 中再次删除 + except (OSError, IOError) as e: + logger.error(f"写入统计文件失败: {e}") + raise + except (TypeError, ValueError) as e: + logger.error(f"JSON 序列化失败: {e}") + raise finally: - if lock_fd: - _release_lock(lock_fd) + _release_lock(lock_fd) # 清理临时文件 if temp_path and os.path.exists(temp_path): - os.unlink(temp_path) + try: + os.unlink(temp_path) + except OSError as e: + logger.warning(f"清理临时文件失败: {e}") def _filter_sensitive_data(data: Dict[str, Any]) -> Dict[str, Any]: