Files
Neo-ZQYY/apps/backend/app/trace/error_handler.py
Neo 6f8f12314f feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:03:48 +08:00

118 lines
3.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
异常/错误全链路追踪 — ERROR span 记录辅助函数
集成到全局异常处理器http_exception_handler / unhandled_exception_handler
为 HTTPException 和未捕获异常分别记录 ERROR span。
- HTTPException → ERROR span异常类型、status_code、detail、发生层级
- 未捕获异常 → ERROR span异常类型、消息、堆栈摘要前 5 行)
- 数据库异常psycopg2.Error→ DB_ERROR span错误码、消息、触发 SQL
所有函数均为安全调用trace 未激活时静默跳过,不影响请求处理。
"""
from __future__ import annotations
import traceback
from datetime import datetime
from starlette.exceptions import HTTPException
from app.trace.context import SpanType, TraceSpan, get_current_trace
def record_http_exception(exc: HTTPException) -> None:
"""为 HTTPException 记录 ERROR span。
记录内容异常类型、status_code、detail、发生层级exception_handler
trace 未激活时静默跳过。
"""
ctx = get_current_trace()
if ctx is None:
return
ctx.add_span(TraceSpan(
span_type=SpanType.ERROR,
module="trace.error_handler",
function="record_http_exception",
description_zh=f"HTTP 异常: {exc.status_code} {exc.detail}",
description_en=f"HTTP exception: {exc.status_code} {exc.detail}",
params={},
result_summary=f"{exc.status_code}",
duration_ms=0.0,
timestamp=datetime.now().isoformat(),
extra={
"exception_type": type(exc).__name__,
"status_code": exc.status_code,
"detail": str(exc.detail) if exc.detail else "",
"layer": "exception_handler",
},
))
def record_unhandled_exception(exc: Exception) -> None:
"""为未捕获异常记录 ERROR span。
记录内容:异常类型、消息、堆栈摘要(最后 5 行)。
trace 未激活时静默跳过。
"""
ctx = get_current_trace()
if ctx is None:
return
# 堆栈摘要:取最后 5 行,避免 span 过大
tb_lines = traceback.format_exception(type(exc), exc, exc.__traceback__)
stack_summary = "".join(tb_lines[-5:]) if len(tb_lines) > 5 else "".join(tb_lines)
ctx.add_span(TraceSpan(
span_type=SpanType.ERROR,
module="trace.error_handler",
function="record_unhandled_exception",
description_zh=f"未捕获异常: {type(exc).__name__}: {exc}",
description_en=f"Unhandled exception: {type(exc).__name__}: {exc}",
params={},
result_summary=type(exc).__name__,
duration_ms=0.0,
timestamp=datetime.now().isoformat(),
extra={
"exception_type": type(exc).__name__,
"message": str(exc),
"stack_summary": stack_summary,
"layer": "exception_handler",
},
))
def record_db_exception(exc: Exception, sql: str = "") -> None:
"""为数据库异常记录 DB_ERROR span。
记录内容PostgreSQL 错误码(如有)、消息、触发 SQL。
trace 未激活时静默跳过。
"""
ctx = get_current_trace()
if ctx is None:
return
# 尝试提取 psycopg2 错误码
pgcode = getattr(exc, "pgcode", None) or ""
ctx.add_span(TraceSpan(
span_type=SpanType.DB_ERROR,
module="trace.error_handler",
function="record_db_exception",
description_zh=f"数据库异常: {type(exc).__name__}: {exc}",
description_en=f"Database exception: {type(exc).__name__}: {exc}",
params={},
result_summary=type(exc).__name__,
duration_ms=0.0,
timestamp=datetime.now().isoformat(),
extra={
"exception_type": type(exc).__name__,
"message": str(exc),
"pgcode": pgcode,
"sql": sql,
"layer": "database",
},
))