""" 执行日志服务 - ExecutionLogger: 工作流执行的日志记录器(per-execution) - ExecutionLoggerService: Agent 执行的日志服务(全局单例) """ from typing import Dict, Any, Optional, List from datetime import datetime from sqlalchemy.orm import Session from app.models.execution_log import ExecutionLog import asyncio import logging logger = logging.getLogger(__name__) class ExecutionLogger: """执行日志记录器(工作流/编排执行上下文)""" def __init__(self, execution_id: str, db: Session): self.execution_id = execution_id self.db = db def log( self, level: str, message: str, node_id: Optional[str] = None, node_type: Optional[str] = None, data: Optional[Dict[str, Any]] = None, duration: Optional[int] = None ): try: log_entry = ExecutionLog( execution_id=self.execution_id, node_id=node_id, node_type=node_type, level=level.upper(), message=message, data=data, duration=duration, timestamp=datetime.utcnow() ) self.db.add(log_entry) self.db.commit() log_method = getattr(logger, level.lower(), logger.info) log_msg = f"[执行 {self.execution_id}]" if node_id: log_msg += f" [节点 {node_id}]" log_msg += f" {message}" log_method(log_msg) except Exception as e: logger.error(f"记录执行日志失败: {str(e)}") def info(self, message: str, **kwargs): self.log("INFO", message, **kwargs) def warn(self, message: str, **kwargs): self.log("WARN", message, **kwargs) def error(self, message: str, **kwargs): self.log("ERROR", message, **kwargs) def debug(self, message: str, **kwargs): self.log("DEBUG", message, **kwargs) def log_node_start(self, node_id: str, node_type: str, input_data: Optional[Dict[str, Any]] = None): self.info( f"节点 {node_id} ({node_type}) 开始执行", node_id=node_id, node_type=node_type, data={"input": input_data} if input_data else None ) def log_node_complete(self, node_id: str, node_type: str, output_data: Optional[Dict[str, Any]] = None, duration: Optional[int] = None): self.info( f"节点 {node_id} ({node_type}) 执行完成", node_id=node_id, node_type=node_type, data={"output": output_data} if output_data else None, duration=duration ) def log_node_error(self, node_id: str, node_type: str, error: Exception, duration: Optional[int] = None): self.error( f"节点 {node_id} ({node_type}) 执行失败: {str(error)}", node_id=node_id, node_type=node_type, data={"error": str(error), "error_type": type(error).__name__}, duration=duration ) class ExecutionLoggerService: """Agent 执行日志服务(全局单例,记录 Agent 每次执行的完整信息)""" def log_execution_sync( self, *, agent_id: Optional[str] = None, agent_name: Optional[str] = None, goal_id: Optional[str] = None, task_id: Optional[str] = None, user_id: Optional[str] = None, session_id: Optional[str] = None, input_text: Optional[str] = None, output_text: Optional[str] = None, output_truncated: bool = False, success: bool = True, error_message: Optional[str] = None, latency_ms: int = 0, iterations_used: int = 0, tool_calls_made: int = 0, tool_chain: Optional[List[Dict[str, Any]]] = None, llm_calls: Optional[List[Dict[str, Any]]] = None, steps: Optional[List[Dict[str, Any]]] = None, model: Optional[str] = None, provider: Optional[str] = None, ) -> Optional[str]: """同步写入执行日志,返回日志 ID。""" from app.core.database import SessionLocal from app.models.agent_execution_log import AgentExecutionLog db: Optional[Session] = None try: db = SessionLocal() entry = AgentExecutionLog( agent_id=agent_id, agent_name=agent_name, goal_id=goal_id, task_id=task_id, user_id=user_id, session_id=session_id, input_text=input_text[:5000] if input_text else None, output_text=output_text[:10000] if output_text else None, output_truncated=output_truncated, success=success, error_message=error_message[:2000] if error_message else None, latency_ms=latency_ms, iterations_used=iterations_used, tool_calls_made=tool_calls_made, tool_chain=tool_chain, llm_calls=llm_calls, steps=steps, model=model, provider=provider, ) db.add(entry) db.commit() db.refresh(entry) return str(entry.id) except Exception as e: logger.warning("写入 Agent 执行日志失败: %s", e) if db: try: db.rollback() except Exception: pass return None finally: if db: try: db.close() except Exception: pass async def log_execution(self, **kwargs) -> Optional[str]: """异步写入执行日志(线程池)。""" loop = asyncio.get_running_loop() return await loop.run_in_executor(None, lambda: self.log_execution_sync(**kwargs)) def log_execution_fire_and_forget(self, **kwargs): """Fire-and-forget 写入。""" try: asyncio.ensure_future(self.log_execution(**kwargs)) except Exception: pass # 全局单例 execution_logger = ExecutionLoggerService()