Flask错误处理:自定义异常与日志记录

在Web开发中,错误处理是确保应用稳定运行和用户体验良好的关键环节。当用户访问不存在的页面、服务器遇到意外情况时,合理的错误处理不仅能给用户友好的提示,还能帮助开发者快速定位问题。Flask框架提供了灵活的错误处理机制,让我们可以轻松实现自定义异常和日志记录。

为什么需要错误处理?

想象一下,如果用户访问一个不存在的页面,看到的是一堆晦涩的代码错误(比如500 Internal Server Error),这显然不友好。错误处理可以:
- 给用户返回清晰易懂的提示(比如“页面没找到”)
- 记录错误发生的详细信息,方便开发调试
- 避免程序因未处理的异常而崩溃

Flask默认错误处理

Flask内置了对常见错误的处理,比如404(页面不存在)和500(服务器内部错误)。我们可以通过@app.errorhandler装饰器自定义这些错误的响应内容。

示例:处理404和500错误

from flask import Flask

app = Flask(__name__)

# 处理404错误(页面不存在)
@app.errorhandler(404)
def page_not_found(error):
    return "哎呀,你访问的页面飞了~ 404 Not Found", 404  # 返回友好提示和状态码

# 处理500错误(服务器内部错误)
@app.errorhandler(500)
def server_error(error):
    return "服务器开小差了,喝口水再试试~ 500 Internal Server Error", 500

# 测试路由
@app.route('/')
def home():
    return "欢迎来到首页!"

if __name__ == '__main__':
    app.run(debug=True)  # debug=True时显示详细错误,但生产环境需关闭

关键点
- @app.errorhandler(code_or_exception):指定要处理的错误类型(可以是状态码或异常类)
- 返回值可以是字符串、模板或JSON,建议带上状态码(如404)
- 默认情况下,Flask在debug=True时会显示错误堆栈,但生产环境下需用自定义页面

自定义异常:让错误更“智能”

当程序逻辑中出现特定错误(如“用户不存在”)时,直接返回状态码可能不够清晰。我们可以通过自定义异常类来封装错误信息,让处理逻辑更模块化。

步骤1:定义自定义异常类

class UserNotFoundError(Exception):
    """用户不存在的自定义异常"""
    def __init__(self, user_id):
        self.user_id = user_id  # 存储错误相关信息(如用户ID)
        self.message = f"用户ID {user_id} 不存在,无法查询"
        super().__init__(self.message)  # 调用父类构造方法

步骤2:主动抛出异常

在业务逻辑中,当遇到错误场景时主动抛出自定义异常:

@app.route('/user/<int:user_id>')
def get_user(user_id):
    # 模拟数据库查询(假设只有ID=1、2的用户存在)
    valid_users = {1, 2}
    if user_id not in valid_users:
        raise UserNotFoundError(user_id)  # 抛出自定义异常
    return f"用户 {user_id} 的信息:姓名:张三"

步骤3:捕获并处理自定义异常

@app.errorhandler捕获自定义异常,并返回友好提示:

@app.errorhandler(UserNotFoundError)
def handle_user_error(error):
    # 返回错误信息和404状态码
    return error.message, 404

效果:当用户访问/user/3(不存在的用户ID)时,会返回“用户ID 3 不存在,无法查询”。

日志记录:让错误“可追溯”

日志是排查问题的关键工具。Flask基于Python的logging模块提供了日志系统,可以将错误信息记录到控制台或文件中,便于后续分析。

Flask日志基础

  • 日志级别:从低到高为 DEBUGINFOWARNINGERRORCRITICAL
  • 默认配置:Flask默认将日志输出到控制台,生产环境需手动配置文件输出

配置日志到文件

import logging
from logging.handlers import RotatingFileHandler

# 创建Flask应用
app = Flask(__name__)

# 配置日志:记录到文件,限制大小和备份
file_handler = RotatingFileHandler(
    'app.log',          # 日志文件名
    maxBytes=1024*1024, # 单个日志文件最大1MB
    backupCount=10      # 最多保留10个备份文件
)
# 设置日志格式(时间、模块、级别、内容)
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(formatter)
# 添加日志处理器到Flask的logger
app.logger.addHandler(file_handler)
# 设置日志级别为INFO(只记录INFO及以上级别)
app.logger.setLevel(logging.INFO)

记录不同类型的日志

@app.route('/user/<int:user_id>')
def get_user(user_id):
    valid_users = {1, 2}
    if user_id not in valid_users:
        # 记录错误日志(ERROR级别)
        app.logger.error(f"用户查询失败:用户ID {user_id} 不存在")
        raise UserNotFoundError(user_id)
    # 记录普通操作日志(INFO级别)
    app.logger.info(f"用户 {user_id} 查询成功")
    return f"用户 {user_id} 的信息:姓名:张三"

效果:错误日志会被同时写入控制台和app.log文件,内容类似:

2023-10-01 12:30:00,123 - flask.app - ERROR - 用户查询失败用户ID 3 不存在

综合示例:用户查询的完整错误处理

结合前面的自定义异常和日志,实现一个完整的用户查询功能:

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

# 1. 定义自定义异常
class UserNotFoundError(Exception):
    def __init__(self, user_id):
        self.user_id = user_id
        self.message = f"用户ID {user_id} 不存在"
        super().__init__(self.message)

# 2. 配置日志
app = Flask(__name__)
file_handler = RotatingFileHandler('app.log', maxBytes=1024*1024, backupCount=10)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)

# 3. 错误处理函数
@app.errorhandler(UserNotFoundError)
def handle_user_error(error):
    app.logger.error(f"已捕获用户不存在错误:{error.message}")  # 再次记录日志
    return error.message, 404

# 4. 业务路由
@app.route('/user/<int:user_id>')
def get_user(user_id):
    valid_users = {1, 2}  # 假设数据库中存在的用户ID
    if user_id not in valid_users:
        app.logger.error(f"查询不存在的用户:{user_id}")
        raise UserNotFoundError(user_id)
    app.logger.info(f"用户 {user_id} 查询成功")
    return f"用户信息:ID={user_id}, 姓名=张三"

# 5. 测试404错误
@app.errorhandler(404)
def page_not_found(error):
    app.logger.warning("访问了不存在的页面")  # 记录警告日志
    return "页面走丢了~ 请检查URL是否正确", 404

if __name__ == '__main__':
    app.run(debug=False)  # 生产环境关闭debug模式

总结

通过自定义异常和日志记录,Flask可以帮助我们:
- 给用户更友好的错误提示(避免崩溃)
- 精准定位问题(日志记录详细错误信息)
- 让错误处理逻辑更清晰(模块化设计)

关键技巧
- 用@app.errorhandler处理404/500等默认错误
- 自定义异常类封装业务错误(如用户不存在)
- 配置日志到文件,避免依赖控制台输出
- 不同日志级别区分重要性(ERROR记录关键错误)

希望这篇文章能帮你快速掌握Flask错误处理的核心技巧,让你的应用更健壮、更易维护!

小夜