"""Log Center 自身错误上报:将 API 运行时异常写入自己的数据库。""" import os import sys import traceback import hashlib from datetime import datetime from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession from sqlalchemy.orm import sessionmaker from .database import get_engine from .models import ErrorLog, LogStatus, Project PROJECT_ID = "log_center_api" ENVIRONMENT = os.getenv("ENVIRONMENT", "production") async def self_report_error(exc: Exception, context: dict = None): """将 Log Center API 自身的异常写入数据库。 直接操作数据库而非走 HTTP,避免循环依赖和额外开销。 任何内部错误都静默处理,绝不影响主业务。 """ try: tb = traceback.extract_tb(exc.__traceback__) last_frame = tb[-1] if tb else None error_type = type(exc).__name__ file_path = last_frame.filename if last_frame else "unknown" line_number = last_frame.lineno if last_frame else 0 stack_trace = traceback.format_exception(exc) # 生成指纹(与 main.py 中 runtime 类型的逻辑一致) raw = f"{PROJECT_ID}|{error_type}|{file_path}|{line_number}" fingerprint = hashlib.md5(raw.encode()).hexdigest() async_session = sessionmaker(get_engine(), class_=AsyncSession, expire_on_commit=False) async with async_session() as session: # 去重检查 stmt = select(ErrorLog).where(ErrorLog.fingerprint == fingerprint) result = await session.exec(stmt) existing = result.first() if existing: if existing.status not in [LogStatus.DEPLOYED, LogStatus.FIXED, LogStatus.VERIFIED]: return # 已在追踪中,跳过 # 回归:已修复的错误再次出现 existing.status = LogStatus.NEW existing.timestamp = datetime.utcnow() existing.retry_count = 0 session.add(existing) await session.commit() return # 确保 project 记录存在 proj_stmt = select(Project).where(Project.project_id == PROJECT_ID) proj_result = await session.exec(proj_stmt) if not proj_result.first(): session.add(Project(project_id=PROJECT_ID, name="Log Center API")) new_log = ErrorLog( project_id=PROJECT_ID, environment=ENVIRONMENT, level="ERROR", source="runtime", error_type=error_type, error_message=str(exc), file_path=file_path, line_number=line_number, stack_trace=stack_trace, context=context or {}, fingerprint=fingerprint, timestamp=datetime.utcnow(), ) session.add(new_log) await session.commit() except Exception: # 自身上报绝不能导致服务崩溃 traceback.print_exc(file=sys.stderr)