You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI部署Cloud Run:如何让Cloud Error Reporting仅展示业务异常

解决FastAPI部署Cloud Run时日志显示无关框架异常的问题

核心原因

当业务代码抛出异常后,若客户端提前断开连接(比如用户刷新页面、请求中断),Uvicorn/AnyIO会触发EndOfStream等框架级异常,导致日志中出现嵌套异常提示During handling of the above exception, another exception occurred:,掩盖了实际的业务异常。

解决方案

1. 自定义日志过滤器,屏蔽框架无关异常

给Uvicorn添加日志过滤器,过滤掉AnyIO、Starlette等框架抛出的非业务异常,只保留业务代码的异常信息。

示例代码:

import logging
import os
from typing import Any

class CloudRunLogFilter(logging.Filter):
    def filter(self, record: logging.LogRecord) -> bool:
        # 过滤AnyIO的EndOfStream异常
        if "EndOfStream" in str(record.msg) or "anyio" in str(record.pathname):
            return False
        # 过滤Starlette/Uvicorn的内部非业务异常
        if "starlette" in str(record.pathname) and "During handling of the above exception" not in str(record.msg):
            return False
        # 保留业务相关异常日志
        return True

# 启动FastAPI服务时配置日志过滤器
if __name__ == "__main__":
    import uvicorn
    uvicorn_logger = logging.getLogger("uvicorn")
    uvicorn_logger.addFilter(CloudRunLogFilter())
    uvicorn.run("main:app", host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

2. 优化全局异常处理器,避免框架二次异常

确保自定义异常处理器能正确捕获所有业务异常,返回合法HTTP响应,防止框架因响应生成失败触发额外异常。

示例代码:

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
import logging

app = FastAPI()

# 自定义业务异常类
class BusinessException(Exception):
    def __init__(self, message: str, status_code: int = 400):
        self.message = message
        self.status_code = status_code

# 全局异常处理器
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    if isinstance(exc, BusinessException):
        return JSONResponse(
            status_code=exc.status_code,
            content={"detail": exc.message}
        )
    elif isinstance(exc, HTTPException):
        return JSONResponse(
            status_code=exc.status_code,
            content={"detail": exc.detail}
        )
    else:
        # 仅记录核心异常信息,关闭exc_info避免嵌套日志
        logging.error(f"Unexpected error: {str(exc)}", exc_info=False)
        return JSONResponse(
            status_code=500,
            content={"detail": "Internal server error"}
        )

3. 调整Uvicorn日志配置

降低日志级别并简化格式,减少框架调试日志输出,聚焦业务异常。

示例启动配置:

uvicorn.run(
    "main:app",
    host="0.0.0.0",
    port=int(os.environ.get("PORT", 8080)),
    log_level="info",
    log_config={
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "default": {
                "format": "%(levelname)s: %(message)s"
            }
        },
        "handlers": {
            "default": {
                "formatter": "default",
                "class": "logging.StreamHandler"
            }
        },
        "loggers": {
            "uvicorn": {"handlers": ["default"], "level": "INFO"}
        }
    }
)

关键注意事项

  • 过滤规则需根据实际日志内容微调,避免误过滤有用的业务日志。
  • 全局异常处理器中使用exc_info=False,防止触发框架的嵌套异常日志。
  • Cloud Run会收集容器标准输出,确保Uvicorn日志直接输出到stdout/stderr即可。

内容的提问来源于stack exchange,提问作者Jasper Madrone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:33:21