Skip to content

FastAPI 中间件 添加 trace_id

TraceIDMiddleware 中间件

py
from starlette.datastructures import Headers, MutableHeaders
from contextvars import ContextVar, Token
from uuid import uuid4


_trace_id_ctx_var: ContextVar[str] = ContextVar("trace_id", default="-")


def generate_trace_id() -> str:
    return uuid4().hex


def get_trace_id() -> str:
    return _trace_id_ctx_var.get()


def set_trace_id(trace_id: str) -> Token:
    return _trace_id_ctx_var.set(trace_id)


def reset_trace_id(token: Token) -> None:
    _trace_id_ctx_var.reset(token)



class TraceIDMiddleware:
    def __init__(self, app, header_name: str = "X-Trace-Id") -> None:
        self.app = app
        self.header_name = header_name
        self._header_name_lower = header_name.lower()

    async def __call__(self, scope, receive, send) -> None:
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        headers = Headers(scope=scope)
        trace_id = headers.get(self._header_name_lower) or generate_trace_id()
        token = set_trace_id(trace_id)
        scope.setdefault("state", {})["trace_id"] = trace_id

        async def send_wrapper(message) -> None:
            if message["type"] == "http.response.start":
                response_headers = MutableHeaders(scope=message)
                response_headers[self.header_name] = trace_id
            await send(message)

        try:
            await self.app(scope, receive, send_wrapper)
        finally:
            reset_trace_id(token)

FastAPI 注册

py
from fastapi import FastAPI
from app.middleware.trace import TraceIDMiddleware

app = FastAPI(title="My FastAPI App")
app.add_middleware(TraceIDMiddleware)

思考

多服务时 trace_id 是非常好的追踪工具.