Flask 异常处理和日志记录

当构建 Web 应用时,处理错误是至关重要的一部分。Flask 提供了灵活的错误处理机制,允许开发者自定义错误页面、捕获异常并提供友好的用户体验。在这篇博客中,我们将探讨 Flask 中的错误处理机制以及如何有效地处理各种错误情况。

一、异常处理

1.基本错误处理

Flask 使用装饰器 @app.errorhandler(code) 来处理特定的 HTTP 错误码。

from flask import Flask, render_template

app = Flask(__name__)

@app.errorhandler(404)
def handle_page_not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e):
    return render_template('400.html'), 400

2.非标准 HTTP 错误

Werkzeug 无法识别非标准 HTTP 错误,需要自定义一个 HTTPException 子类。

class InsufficientStorage(werkzeug.exceptions.HTTPException):
    code = 507
    description = 'Not enough storage space.'

app.register_error_handler(InsufficientStorage, handle_507)

raise InsufficientStorage()

3.全局错误处理

可以将异常基类注册到异常处理器,例如 HTTPException 基类或者甚至 Exception 基类。

from flask import json
from werkzeug.exceptions import HTTPException

@app.errorhandler(HTTPException)
def handle_exception(e):
    response = e.get_response()
    response.data = json.dumps({
        "code": e.code,
        "message": e.description,
    })
    response.content_type = "application/json"
    return response


@app.errorhandler(Exception)
def handle_exception(e):
    return {
        "code": 500,
        "message": str(e)
    }

异常处理器遵循异常类的继承层次。如果同时基于 HTTPException 和 Exception 注册了异常处理器, Exception 处理器不会处理 HTTPException 子类,因为 HTTPException 更有针对性。

4.蓝图错误处理器

在使用蓝图进行应用模块化中,大多数错误处理器会按预期工作,但是处理 404 和 405 错误的处理器比较特殊。这是因为蓝图不“拥有”一定的 URL 空间,所以应用实例无法知道非法 URL 访问应当调用哪个蓝图的错误处理器。如果需要基于 URL 前缀配置不同的处理策略, 那么可以使用 rquest 代理对象在应用层面进行配置。

from flask import jsonify, render_template

@app.errorhandler(404)
def page_not_found(e):
    if request.path.startswith('/blog/'):
        return render_template("blog/404.html"), 404
    else:
        return render_template("404.html"), 404

@app.errorhandler(405)
def method_not_allowed(e):
    if request.path.startswith('/api/'):
        return jsonify(message="Method Not Allowed"), 405
    else:
        return render_template("405.html"), 405

5.Flask-WTF 表单验证错误

当使用 Flask-WTF 处理表单时,可能会遇到表单验证错误。Flask 提供了一个特殊的装饰器 @app.errorhandler(wtforms.ValidationError) 来处理这类错误:

from flask import Flask, render_template
from wtforms import ValidationError

app = Flask(__name__)

@app.errorhandler(ValidationError)
def handle_validation_error(e):
    return render_template('validation_error.html', error=str(e)), 400

这个例子中的 400 是 Bad Request 错误的 HTTP 状态码。

二、日志记录

日志记录是一种重要的开发和运维工具,有助于解决以下问题:

  • 故障排查: 当应用出现问题时,日志可以提供详细的信息,有助于定位和修复错误。

  • 性能监控: 通过记录请求处理时间、数据库查询次数等信息,可以评估应用的性能状况。

  • 安全性: 记录登录尝试、异常请求等信息有助于检测潜在的安全问题。

  • 业务分析: 记录用户行为、访问量等信息,有助于分析用户行为和应用的使用情况。

在 Flask 中,日志记录是基于 Python 内置的 logging 模块实现的。Flask 应用对象(Flask 类的实例)上有一个 logger 属性,通过该属性可以获得日志记录器。通常,我们使用该日志记录器来记录应用的活动和错误。
常用的日志级别包括 'DEBUG''INFO''WARNING''ERROR''CRITICAL'。通过设置日志级别,可以控制记录哪些级别以上的日志。

@app.route('/')
def index():
    app.logger.info('访问首页')
    return 'Hello, World!'

配置日志记录(方式一)

from logging.handlers import RotatingFileHandler

# 创建文件处理程序
file_handler = RotatingFileHandler('app.log', maxBytes=1024 * 1024, backupCount=10)
file_handler.setLevel(logging.INFO)

# 添加处理程序到日志记录器
app.logger.addHandler(file_handler)

这样配置的日志处理程序将在应用根目录下创建一个 app.log 文件,并保留最多 10 个旧的日志文件备份。maxBytes 参数设置单个日志文件的最大大小。

配置日志记录(方式二)

from logging.config import dictConfig

dictConfig({
    'version': 1,
    'formatters': {
        'default': {
            'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
        }
    },
    'handlers': {
        'file_handler': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'app.log',
            'maxBytes': 1024 * 1024,
            'backupCount': 10,
            'formatter': 'default'
        },
        'console_handler': {
            'class': 'logging.StreamHandler',
            'formatter': 'default'
        }
    },
    'root': {
        'level': 'DEBUG',
        'handlers': ['file_handler', 'console_handler']
    }
})

日志格式说明
日志格式是通过格式化字符串来定义的,这个字符串中包含特定的占位符,这些占位符在实际日志记录时会被替换为相应的值。以下是一些常见的日志格式占位符和它们的含义:

  1. %(asctime)s: 记录的时间

    • 格式:YYYY-MM-DD HH:MM:SS,mmm(毫秒部分)
    • 例如:2023-03-15 12:45:30,123
  2. %(levelname)s: 日志级别

    • 例如:DEBUG, INFO, WARNING, ERROR, CRITICAL
  3. %(message)s: 日志消息文本

  4. %(name)s: 记录器的名称

  5. %(module)s: 所在模块的名称

  6. %(funcName)s: 所在函数的名称

  7. %(lineno)d: 所在代码的行号

  8. %(pathname)s: 所在文件的完整路径

  9. %(filename)s: 所在文件的文件名

  10. %(process)d: 进程ID

  11. %(thread)d: 线程ID

注入请求信息

如果要看到web请求中如 IP 地址等信息,可以继承 logging.Formatter 来注入自己的内容,以显示在日志消息中。

from flask import has_request_context, request
from flask.logging import default_handler

class RequestFormatter(logging.Formatter):
    def format(self, record):
        if has_request_context():
            record.url = request.url
            record.remote_addr = request.remote_addr
        else:
            record.url = None
            record.remote_addr = None

        return super().format(record)

formatter = RequestFormatter(
    '[%(asctime)s] %(remote_addr)s requested %(url)s\n'
    '%(levelname)s in %(module)s: %(message)s'
)
default_handler.setFormatter(formatter)
mail_handler.setFormatter(formatter)

结语

通过灵活的错误处理机制,Flask 提供了强大的工具来处理各种错误情况。有效的错误处理不仅能够提高用户体验,还有助于快速定位和修复问题,使得 Web 应用更加稳定和可靠。在开发过程中,合理利用 Flask 的错误处理机制将为你的应用增色不少。

你可能感兴趣的:(Flask,flask,python,后端)