"""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 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(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)