Python实现方法全局自动捕获异常,避免Web服务器停止
解决方案:全局异常捕获与日志记录
嘿,这个需求其实挺常见的,而且你之前对装饰器的理解有点小偏差哦——装饰器可不只是在方法执行前跑代码,它能完全包裹住目标方法的整个执行流程,包括捕获异常!这正好能解决你的问题,不用逐个修改现有方法,就能实现全局异常捕获、日志记录,还能让服务器一直跑下去。
一、先纠正装饰器的认知误区
装饰器本质是一个包装函数,它可以把目标方法的执行逻辑完全包裹起来:既可以在方法执行前做准备,也能在执行后做收尾,更关键的是——它能捕获方法执行过程中抛出的所有异常,完美匹配你的需求。
二、实现通用的异常捕获装饰器
先写一个通用的装饰器,它会帮你完成:捕获异常、写入日志文件、保证应用不崩溃这三件事。示例代码如下:
import logging from functools import wraps # 配置日志:将错误信息写入server_errors.log文件 logging.basicConfig( filename='server_errors.log', level=logging.ERROR, format='%(asctime)s - %(funcName)s - %(levelname)s - %(message)s' ) def catch_and_log_exceptions(func): @wraps(func) # 保留原函数的元信息(比如函数名) def wrapper(*args, **kwargs): try: # 执行原方法 return func(*args, **kwargs) except Exception as e: # 记录异常日志,包含堆栈信息(方便排查问题) logging.error(f"异常详情:{str(e)}", exc_info=True) # 如果是Web接口方法,这里可以返回友好的错误响应(比如JSON) # 比如return {"error": "服务器内部错误"}, 500 return None # 根据你的业务需求调整返回值 return wrapper
三、批量应用装饰器到所有方法
不用逐个手动给方法加装饰器,有两种高效的方式:
1. 自动装饰类的所有方法
如果你的plus、by等方法都在同一个类里,可以写一个工具装饰器,自动给类内所有方法加上异常捕获:
def decorate_all_methods(decorator): def decorate(cls): # 遍历类的所有属性,给可调用的方法加上装饰器 for attr_name, attr_value in vars(cls).items(): if callable(attr_value): setattr(cls, attr_name, decorator(attr_value)) return cls return decorate # 给你的Web服务器类加上这个装饰器,所有方法自动获得异常捕获能力 @decorate_all_methods(catch_and_log_exceptions) class WebServer: def plus(self, a, b): return a + b def by(self, a, b): if b == 0: raise ValueError("除数不能为0") return a / b def split(self, s, sep): return s.split(sep) def takeaway(self, a, b): return a - b
2. 手动批量装饰(适合零散方法)
如果你的方法不在同一个类里,也可以手动批量给它们加装饰器:
# 假设你的方法是独立函数 def plus(a, b): return a + b def by(a, b): if b == 0: raise ValueError("除数不能为0") return a / b # 批量应用装饰器 plus = catch_and_log_exceptions(plus) by = catch_and_log_exceptions(by) split = catch_and_log_exceptions(split) takeaway = catch_and_log_exceptions(takeaway)
四、如果是基于Web框架的服务器(比如Flask/Django)
如果你的Web服务器是用Flask、Django这类框架开发的,还有更优雅的全局方案——用框架自带的全局异常处理中间件:
Flask示例
from flask import Flask, jsonify app = Flask(__name__) # 全局异常捕获处理器 @app.errorhandler(Exception) def handle_all_exceptions(e): logging.error(f"未处理异常:{str(e)}", exc_info=True) return jsonify({"error": "服务器内部错误"}), 500 # 你的路由方法(无需手动加装饰器) @app.route('/plus/<int:a>/<int:b>') def plus(a, b): return str(a + b) @app.route('/by/<int:a>/<int:b>') def by(a, b): return str(a / b)
Django示例
自定义全局异常中间件,然后在settings.py中配置:
# 自定义中间件文件 middleware.py import logging from django.http import JsonResponse class GlobalExceptionMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): try: response = self.get_response(request) except Exception as e: logging.error(f"未处理异常:{str(e)}", exc_info=True) return JsonResponse({"error": "服务器内部错误"}, status=500) return response # 在settings.py的MIDDLEWARE列表中添加这个中间件 MIDDLEWARE = [ # 其他中间件... 'your_app.middleware.GlobalExceptionMiddleware', ]
五、关键注意事项
- 日志一定要记录异常堆栈信息(也就是代码里的
exc_info=True),不然你只知道报错了,却不知道错在哪一行,很难排查。 - 不要捕获
KeyboardInterrupt这类终止程序的信号,否则你可能没法正常关闭服务器。 - 如果是Web接口,捕获异常后要返回合适的HTTP状态码(比如500),避免客户端一直处于等待状态。
这样一来,不管你的哪个方法抛出异常,都会被全局捕获,错误日志会写入文件,服务器也能继续运行,完全不用逐个修改现有方法~
内容的提问来源于stack exchange,提问作者Máxima Alekz
相关产品推荐
相关产品推荐

