
本文介绍如何利用 Flask 的请求钩子机制,在日志中自动添加每个 API 请求的响应耗时(如 0.45 s),无需修改业务逻辑,兼容现有 logging 配置,支持高可读性格式化输出。
本文介绍如何利用 flask 的请求钩子机制,在日志中自动添加每个 api 请求的响应耗时(如 `0.45 s`),无需修改业务逻辑,兼容现有 logging 配置,支持高可读性格式化输出。
在 Flask 应用中实现响应时间监控,关键在于无侵入式地捕获请求生命周期。推荐使用 @app.before_request 和 @app.after_request 钩子函数,结合 Flask 的上下文全局变量 g 存储请求起始时间,并在响应返回前计算耗时,最终注入到日志中。
以下是一个完整、生产就绪的实现方案:
✅ 核心实现(兼容您现有的 logger)
from flask import Flask, g, request
import time
import logging
# 假设您已定义 setup_logger 和 logger 实例(如问题中所示)
# logger = logging.getLogger("MY_APP")
@app.before_request
def record_start_time():
g.start_time = time.time()
@app.after_request
def log_response_time(response):
if hasattr(g, 'start_time'):
elapsed = time.time() - g.start_time
# 构造带耗时的日志前缀:[0.45 s]
duration_tag = f"[{elapsed:.2f} s]"
# 获取请求信息(路径、方法、状态码)
method = request.method
path = request.path
status_code = response.status_code
# 使用您的 logger 输出结构化日志(替代 werkzeug 默认日志)
logger.info(
f"{duration_tag} {method} {path} → {status_code} ({elapsed:.3f}s)",
extra={'pathname': request.endpoint or 'unknown'}
)
return response? 说明:此方式不干扰 Werkzeug 默认访问日志,而是额外输出一条含耗时的自定义日志。若您希望直接改造默认日志格式(如让 [0.45 s] 出现在原有日志行中),需替换 Werkzeug 的 logging.Logger 或使用 werkzeug.serving.WSGIRequestHandler 自定义日志器——但该方式复杂度高、易出错,不推荐。
✅ 示例输出效果
启用后,日志将新增如下条目(与您原有日志并存):
[2024-04-19-04:20:02][MY_APP][INFO][app.py][0.45 s] GET /available/space → 200 (0.452s) [2024-04-19-04:20:05][MY_APP][INFO][app.py][1.21 s] POST /upload → 201 (1.213s)
⚠️ 注意事项与最佳实践
- 避免在 after_request 中抛出异常:若 g.start_time 未设置(如静态文件请求或异常中断),应做健壮性检查(如示例中 hasattr(g, 'start_time'))。
- 精度选择:time.time() 在多数场景足够;如需微秒级精度,可用 time.perf_counter()(但注意其不可用于跨进程时间对比)。
- 异步视图兼容性:Flask 2.0+ 支持 async def 视图,此时 g 在协程中仍有效,但需确保 before/after_request 钩子本身为同步函数(当前版本限制)。
- 性能开销极低:两次函数调用 + 一次浮点减法,对 QPS > 1k 的服务影响可忽略。
- 扩展建议:可进一步将耗时数据上报至 Prometheus(通过 flask-prometheus-exporter)或写入结构化 JSON 日志供 ELK 分析。
通过该方案,您无需修改任何路由函数,即可统一获得全接口响应时间指标,快速定位慢接口,为性能优化提供坚实数据支撑。


















