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錯誤處理的核心技巧,讓你的應用更健壯、更易維護!

小夜