如何在FastAPI中自动为日志添加Request_id字段?
自动给包含request_id的类添加日志前缀的解决方案
你想要自动给包含self.request_id的类的日志添加Request_id={self.request_id}前缀,避免手动编写遗漏。你的原始思路(检测代码中是否存在self.request_id)在运行时难以可靠实现,推荐使用Python logging库的LoggerAdapter或自定义Filter来实现,以下是具体方案:
方案一:使用LoggerAdapter(适合类实例持有request_id的场景)
这种方式会给类内的logger绑定实例的request_id,日志输出时自动带上前缀,无需手动拼接。
步骤1:修改全局日志格式
在main.py中更新日志配置,添加%(request_id)s占位符:
import logging logging.basicConfig(format="%(levelname)s: Request_id=%(request_id)s %(message)s") logger = logging.getLogger(__name__) logger.setLevel('INFO')
步骤2:在类中使用LoggerAdapter包装logger
在包含request_id的类里,初始化logger时用LoggerAdapter绑定当前实例的request_id:
from logging import LoggerAdapter class YourServiceClass: def __init__(self, request_id): self.request_id = request_id # 包装全局logger,注入request_id参数 self.logger = LoggerAdapter(logging.getLogger(__name__), {"request_id": self.request_id}) def do_something(self): # 直接正常打日志,自动带上Request_id前缀 self.logger.info("处理用户请求") self.logger.error("请求处理失败")
这样,类内所有通过self.logger输出的日志,都会自动在开头加上Request_id={self.request_id},完全不需要手动拼接。
方案二:自定义Filter(适合FastAPI请求上下文传递request_id的场景)
如果你的request_id是来自FastAPI的请求(比如通过中间件生成),可以用自定义Filter从请求上下文自动获取request_id并注入日志:
步骤1:添加FastAPI中间件生成request_id
先通过中间件给每个请求生成并绑定request_id:
from fastapi import FastAPI, Request import uuid app = FastAPI() @app.middleware("http") async def add_request_id(request: Request, call_next): request.state.request_id = str(uuid.uuid4()) response = await call_next(request) response.headers["X-Request-ID"] = request.state.request_id return response
步骤2:编写自定义日志Filter
import logging from contextvars import ContextVar # 用ContextVar存储当前请求的request_id,避免异步环境下的上下文混乱 request_id_var = ContextVar("request_id", default="unknown") class RequestIDFilter(logging.Filter): def filter(self, record): record.request_id = request_id_var.get() return True # 更新全局日志配置,添加Filter logger = logging.getLogger(__name__) logger.addFilter(RequestIDFilter()) logging.basicConfig(format="%(levelname)s: Request_id=%(request_id)s %(message)s") logger.setLevel('INFO')
步骤3:在中间件中更新ContextVar
修改之前的中间件,把request_id存入ContextVar:
@app.middleware("http") async def add_request_id(request: Request, call_next): request_id = str(uuid.uuid4()) token = request_id_var.set(request_id) request.state.request_id = request_id try: response = await call_next(request) response.headers["X-Request-ID"] = request_id return response finally: request_id_var.reset(token)
这样,不管是在路由处理函数还是类实例中,只要使用全局logger打日志,都会自动带上当前请求的request_id前缀。
内容的提问来源于stack exchange,提问作者Dyson
相关产品推荐
相关产品推荐

