
本文介绍如何在 fastapi 中结合 loguru,通过 `contextualize` 机制为每次 http 请求动态注入 url 等上下文信息,并实时反映在日志格式中,无需修改每条日志调用,真正实现声明式、线程安全的请求级日志增强。
在 FastAPI 应用中集成 Loguru 进行请求级日志监控时,一个常见但关键的需求是:让每条日志自动携带当前请求的 URL(或其它请求元数据),且该信息需直接嵌入日志格式字符串中(如
Loguru 原生支持上下文感知日志,其核心机制是 logger.contextualize() 配合 extra 字段。实现步骤如下:
✅ 步骤一:预定义含 extra 占位符的日志格式
在初始化 Loguru 时,将自定义字段(如 request_url)作为 extra 的键写入格式字符串,并通过 logger.configure() 设置默认值(防止未 contextualize 时格式崩溃):
from loguru import logger
import sys
LOG_LEVEL = "DEBUG"
# 移除默认处理器
logger.remove()
# 定义支持 request_url 的格式(注意 {extra[request_url]})
log_format = (
"{time:YYYY-MM-DD HH:mm:ss.SSS zz} | "
"{extra[request_url]} | "
"{level: <8} | "
"Line {line: >4} ({file}): {message}"
)
# 设置全局默认 extra 值(关键!避免未 contextualize 时报错)
logger.configure(extra={"request_url": "N/A"})
# 添加控制台与文件处理器(均使用同一格式)
logger.add(
sys.stdout,
level=LOG_LEVEL,
format=log_format,
colorize=True,
backtrace=True,
diagnose=True,
)
logger.add(
"app.log",
rotation="2 MB",
level=LOG_LEVEL,
format=log_format,
colorize=False,
backtrace=True,
diagnose=True,
)✅ 步骤二:在中间件中使用 contextualize 注入请求上下文
利用 with logger.contextualize(...) 创建一个作用域化的上下文环境,确保该请求生命周期内所有日志(包括下游函数、依赖项、异常捕获等)自动携带 request_url:
from fastapi import FastAPI, Request
from starlette.responses import Response
app = FastAPI()
@app.middleware("http")
async def log_request_middleware(request: Request, call_next):
# 提取并注入 URL 路径(也可加入 method、client_host 等)
url_path = str(request.url.path)
with logger.contextualize(request_url=url_path):
logger.debug(f"Request received: {request.method} {url_path}")
try:
response: Response = await call_next(request)
logger.debug(f"Request completed with status {response.status_code}")
return response
except Exception as e:
logger.exception("Unhandled exception during request processing")
raise? 效果示例:当访问 /users/123 时,任意位置调用 logger.info("User fetched") 将输出:2024-06-15 14:22:35.102 CST | /users/123 | INFO | Line 123 (main.py): User fetched
⚠️ 注意事项与最佳实践
- 线程/协程安全:contextualize 基于 Python 的 contextvars 实现,天然支持异步协程隔离,不同请求日志上下文互不干扰;
- 字段命名规范:extra 键名应语义清晰(如 request_id, user_id, trace_id),避免与 Loguru 内置字段(如 time, level)冲突;
- 性能考量:contextualize 开销极低,但避免在高频循环中反复调用;建议在中间件顶层一次性注入;
- 扩展性建议:可结合 request.state 或 Depends() 注入更多上下文(如认证用户、请求 ID),统一管理;
- 错误防御:始终通过 logger.configure(extra={...}) 设置默认值,防止未进入中间件(如静态文件路由)时日志格式异常。
通过以上方式,你无需修改业务逻辑中的每一条 logger.xxx() 调用,即可实现“一次配置、处处生效”的请求级结构化日志,大幅提升 FastAPI 应用的可观测性与排障效率。










