php做网站验证码的设计,螺蛳粉的软文推广,菏泽市城乡建设局网站,做网站毕业实训报告开篇#xff1a;一个让人抓狂的下午
“接口挂了#xff0c;返回 500。”
看到这条消息#xff0c;你打开服务器日志#xff0c;心想#xff1a;来吧#xff0c;看看是什么妖魔鬼怪。
2024-01-15 14:30:00 | INFO | 应用启动完成
2024-01-15 14:30:05 | INFO | 收到请求: …开篇一个让人抓狂的下午“接口挂了返回 500。”看到这条消息你打开服务器日志心想来吧看看是什么妖魔鬼怪。2024-01-15 14:30:00 | INFO | 应用启动完成 2024-01-15 14:30:05 | INFO | 收到请求: POST /api/generate然后……就没了。没有错误信息没有堆栈跟踪什么都没有。Bug 像个忍者来无影去无踪。你开始加 print部署再加 print再部署……三小时后终于发现是某个变量是 None。三个小时就为了找一个空指针。罪魁祸首翻开代码你看到了这行try:resultsome_function()exceptException:pass# 就是这行某位前人写的代码捕获了所有异常然后——什么都不做。异常就这么被吞掉了悄无声息像被灭霸打了个响指。如果你也经历过这种绝望这篇文章就是为你准备的。异常处理的三道防线好的异常处理就像洋葱——一层一层的而且可能让你流泪。抛出异常业务异常未知异常HTTP 请求第一道防线: 全局异常处理器第二道防线: 业务异常处理第三道防线: 端点级 try-except业务逻辑异常类型?返回友好错误提示记录日志 返回通用错误三道防线各司其职防线职责比喻全局异常处理器兜底所有漏网之鱼最后一道城墙业务异常处理处理意料之中的错误前线哨兵端点级 try-except精细化异常恢复贴身保镖记住异常不会消失只会被藏起来。我们的目标是让每个异常都无处可藏。第一步给异常办个身份证裸奔的raise Exception(出错了)是不够的。我们需要给异常分门别类# app/core/exceptions.pyfromtypingimportOptional,AnyclassAppException(Exception):应用异常基类 —— 所有业务异常的祖宗def__init__(self,message:str,code:strUNKNOWN_ERROR,status_code:int500,details:Optional[Any]None):self.messagemessage self.codecode# 错误码前端靠这个判断self.status_codestatus_code# HTTP 状态码self.detailsdetails# 额外信息想塞啥塞啥super().__init__(message)classValidationError(AppException):参数不对400 伺候def__init__(self,message:str,details:AnyNone):super().__init__(message,VALIDATION_ERROR,400,details)classNotFoundError(AppException):找不到404 安排def__init__(self,resource:str,resource_id:str):super().__init__(f{resource}not found:{resource_id},NOT_FOUND,404,{resource:resource,id:resource_id})classGenerationError(AppException):AI 罢工了def__init__(self,message:str,model:strNone):super().__init__(message,GENERATION_ERROR,500,{model:model})classExternalServiceError(AppException):第三方服务挂了锅不在我def__init__(self,service:str,message:str):super().__init__(f{service}error:{message},EXTERNAL_SERVICE_ERROR,502,# 502 上游挂了{service:service})有了这套身份证系统每个异常都有错误码前端可以根据 code 显示不同的提示状态码HTTP 语义正确运维监控不会瞎报警详情调试时的救命稻草第二步设立全局关卡接下来在 FastAPI 里注册异常处理器。把它想象成机场安检——每个异常都得过这一道# app/main.pyfromfastapiimportFastAPI,Requestfromfastapi.responsesimportJSONResponsefromapp.core.exceptionsimportAppExceptionfromapp.core.loggerimportloggerimporttraceback appFastAPI()app.exception_handler(AppException)asyncdefapp_exception_handler(request:Request,exc:AppException):业务异常处理器 —— 处理可预期的坏消息 logger.warning(f业务异常 [{exc.code}]:{exc.message},extra{path:request.url.path,method:request.method,code:exc.code,details:exc.details})returnJSONResponse(status_codeexc.status_code,content{success:False,error:{code:exc.code,message:exc.message,details:exc.details}})app.exception_handler(Exception)asyncdefglobal_exception_handler(request:Request,exc:Exception):全局异常处理器 —— 最后一道城墙专治各种意外惊喜 error_detail{code:INTERNAL_ERROR,message:An internal error occurred. Please try again later.}# 开发环境把底裤都给你看ifsettings.DEBUG:error_detail[message]str(exc)error_detail[type]type(exc).__name__ error_detail[traceback]traceback.format_exc().split(\n)# 不管什么环境日志里必须有完整信息logger.error(f未处理的异常:{type(exc).__name__}:{str(exc)},extra{path:request.url.path,method:request.method,traceback:traceback.format_exc()# 完整堆栈一个字都不能少})returnJSONResponse(status_code500,content{success:False,error:error_detail})划重点AppException用WARNING级别——这是意料之中的错误未知Exception用ERROR级别——这是意料之外的惊喜traceback.format_exc()是你的好朋友完整堆栈一览无余生产环境别暴露内部错误不然黑客会感谢你的坦诚第三步端点级精细作战全局处理器是最后防线但有些异常需要在端点级别就地解决# app/api/endpoints/generate.pyrouter.post(/shot-image)asyncdefgenerate_shot_image(request:GenerateShotImageRequest):生成分镜图片 —— 一个充满意外的端点# 参数校验先礼后兵ifnotrequest.prompt.strip():raiseValidationError(Prompt cannot be empty)try:adapterImageGenerationAdapterFactory.get_current_adapter()resultawaitadapter.generate_shot_image(promptrequest.prompt,widthrequest.width,heightrequest.height)ifnotresult.get(success):raiseGenerationError(result.get(error,Unknown generation error),modelsettings.IMAGE_MODEL)return{success:True,data:result}exceptGenerationError:raise# 已经是业务异常放它走excepttorch.cuda.OutOfMemoryError:# 显存爆了给个人话提示raiseGenerationError(GPU out of memory. Please try a smaller image size.)exceptExceptionase:# 未知异常先记录再转换logger.error(fUnexpected error in generate_shot_image:{e})raiseGenerationError(fGeneration failed:{str(e)})异常处理策略表情况处理方式理由已是业务异常直接raise已经有身份证了已知特定异常转换为业务异常给它办个身份证未知异常记日志 转换先留案底再处理第四步日志配置——案发现场的监控探头推荐 loguru比标准库的 logging 好用一万倍真的# app/core/logger.pyfromloguruimportloggerimportsys logger.remove()# 先清场# 控制台输出花里胡哨但好用logger.add(sys.stdout,formatgreen{time:YYYY-MM-DD HH:mm:ss}/green | level{level: 8}/level | cyan{name}/cyan:cyan{function}/cyan:cyan{line}/cyan | level{message}/level,levelDEBUG)# 文件日志朴实无华但可靠logger.add(logs/app_{time:YYYY-MM-DD}.log,format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name}:{function}:{line} | {message},levelINFO,rotation00:00,# 每天零点轮转retention30 days,# 保留 30 天compressionzip# 自动压缩省空间)# 错误日志单独伺候VIP 待遇logger.add(logs/error_{time:YYYY-MM-DD}.log,format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name}:{function}:{line} | {message}\n{exception},levelERROR,rotation00:00,retention90 days# 错误日志多留几天翻旧账用得上)第五步请求追踪——给每个请求一个案件编号importtimeimportuuidapp.middleware(http)asyncdeflog_requests(request:Request,call_next):请求日志中间件 —— 来了就得登记request_idstr(uuid.uuid4())[:8]# 8 位够用了logger.info(f[{request_id}] --{request.method}{request.url.path})starttime.time()responseawaitcall_next(request)durationtime.time()-start logger.info(f[{request_id}] --{response.status_code}in{duration:.2f}s)# 响应头里也带上方便前端response.headers[X-Request-ID]request_idreturnresponse效果展示2024-01-15 14:30:00 | INFO | [a1b2c3d4] -- POST /api/generate/shot-image 2024-01-15 14:30:12 | INFO | [a1b2c3d4] -- 200 in 12.34s前端说接口报错了你只需要问一句“Request ID 多少”然后grep a1b2c3d4 logs/error_*.log破案。红线绝对禁止的写法立个规矩刻在 DNA 里# 禁止禁止禁止try:something()exceptException:pass# 这是在犯罪这种代码的危害异常凭空消失debug 时怀疑人生埋下定时炸弹不知道什么时候爆炸让接手的同事想打人正确姿势# 姿势一只捕获特定异常try:resultmaybe_fail()exceptFileNotFoundError:resultdefault_value# 这个异常我能处理exceptPermissionErrorase:logger.warning(f权限不足:{e})raise# 这个我处理不了抛出去# 姿势二实在要忽略至少留个遗言try:optional_operation()exceptSomeSpecificErrorase:logger.debug(f忽略的异常有意为之:{e})快速抄作业清单原则做法不吞异常except: pass写一次绩效扣一分分层处理全局 → 业务 → 端点层层设防日志完整堆栈、请求 ID、路径一个都不能少错误码标准化定义业务异常类别裸抛 Exception环境区分开发给详情生产给脸色请求可追踪X-Request-ID追凶利器总结异常处理三原则把这三句话贴在工位上要么处理它——你知道怎么应对这个异常要么记录它——你不知道怎么处理但要留下证据要么抛出它——让更上层的人来处理但绝不能忽视它。有了这套体系下次接口报 500你只需要拿到 Request ID搜索错误日志看完整堆栈定位问题整个过程一分钟。再也不用三小时排查一个空指针了。