[{"data":1,"prerenderedAt":1418},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F":152,"surround-\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F":1416},[4,72],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,24,36,48,54,66],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation & Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators & Field Constraints in FastAPI & Pydantic V2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic: Production Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"JSON Schema Customization","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[29,30],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Customizing OpenAPI Schema Generation in FastAPI: Production Implementation Guide","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Mastering Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[41,42],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Handling Deeply Nested JSON Models Efficiently in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[47],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Performance Optimization for Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Pydantic V2 Migration Guide: FastAPI Production Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Migrating from Pydantic v1 to v2 without breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Type Hinting & IDE Integration in FastAPI: Advanced Pydantic Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[71],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[77,80,92,104,116,128,140],{"title":78,"path":74,"stem":79},"Core Architecture & Routing Patterns in FastAPI: A Production-Ready Blueprint","core-architecture-routing-patterns\u002Findex",{"title":81,"path":82,"stem":83,"children":84},"Application Factory Patterns in FastAPI: Production Architecture Guide","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[85,86],{"title":81,"path":82,"stem":83},{"title":87,"path":88,"stem":89,"children":90},"FastAPI App Factory Pattern for Testing and Deployment: Production Guide","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[91],{"title":87,"path":88,"stem":89},{"title":93,"path":94,"stem":95,"children":96},"Configuration Management in FastAPI: Production-Ready Patterns & Security","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[97,98],{"title":93,"path":94,"stem":95},{"title":99,"path":100,"stem":101,"children":102},"Managing Environment Variables with Pydantic Settings in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[103],{"title":99,"path":100,"stem":101},{"title":105,"path":106,"stem":107,"children":108},"Dependency Injection Strategies","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[109,110],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Error Handling & Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[121,122],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[127],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Middleware Implementation","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[133,134],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Implementing Custom Middleware for Request Tracing in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Modular Router Organization in FastAPI: Production-Grade Architecture","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[145,146],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[151],{"title":147,"path":148,"stem":149},{"id":153,"title":135,"body":154,"description":1411,"extension":1412,"meta":1413,"navigation":299,"path":136,"seo":1414,"stem":137,"__hash__":1415},"content\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex.md",{"type":155,"value":156,"toc":1398},"minimark",[157,161,171,176,188,192,203,206,526,532,549,553,560,760,765,791,795,802,1017,1022,1043,1047,1052,1059,1167,1171,1178,1222,1238,1242,1273,1277,1358,1362,1367,1373,1378,1386,1391,1394],[158,159,135],"h1",{"id":160},"implementing-custom-middleware-for-request-tracing-in-fastapi",[162,163,164,165,170],"p",{},"Debugging asynchronous API calls in production requires precise request correlation. This guide details how to implement custom middleware for request tracing within the broader ",[166,167,169],"a",{"href":168},"\u002Fcore-architecture-routing-patterns\u002F","Core Architecture & Routing Patterns"," ecosystem, ensuring every inbound request receives a unique, traceable identifier that propagates through logs, dependencies, and external service calls.",[172,173,175],"h2",{"id":174},"understanding-request-tracing-in-async-environments","Understanding Request Tracing in Async Environments",[162,177,178,179,183,184,187],{},"In synchronous Python, developers historically relied on thread-local storage for request-scoped state. In an ",[180,181,182],"code",{},"asyncio"," event loop, multiple coroutines share the same OS thread, making thread-locals unsafe and highly prone to cross-request contamination. Starlette’s middleware stack processes requests sequentially through a chain of callables, requiring a concurrency-safe mechanism for state isolation. Aligning your tracing strategy with established ",[166,185,129],{"href":186},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F"," patterns ensures predictable execution order, minimal event-loop blocking, and clean separation of concerns.",[172,189,191],{"id":190},"building-the-custom-middleware-class","Building the Custom Middleware Class",[162,193,194,195,198,199,202],{},"For production FastAPI applications, extending ",[180,196,197],{},"starlette.middleware.base.BaseHTTPMiddleware"," provides the cleanest interface for request\u002Fresponse interception. While raw ASGI middleware offers marginally lower overhead, ",[180,200,201],{},"BaseHTTPMiddleware"," safely handles request body streaming and response wrapping without manual scope management.",[162,204,205],{},"The following implementation extracts an existing trace ID from upstream proxies (e.g., API gateways, load balancers) or generates a cryptographically secure UUIDv4. It binds the identifier to an async-safe context variable and injects it into the response headers.",[207,208,213],"pre",{"className":209,"code":210,"language":211,"meta":212,"style":212},"language-python shiki shiki-themes github-light","import uuid\nfrom contextvars import ContextVar\nfrom typing import Callable, Awaitable\nfrom starlette.middleware.base import BaseHTTPMiddleware\nfrom starlette.requests import Request\nfrom starlette.responses import Response\n\n# Request-scoped context variable for trace ID propagation\ntrace_id_ctx: ContextVar[str] = ContextVar(\"trace_id\", default=\"\")\n\nclass RequestTracingMiddleware(BaseHTTPMiddleware):\n async def dispatch(\n self, request: Request, call_next: Callable[[Request], Awaitable[Response]]\n ) -> Response:\n # Extract from upstream proxy or generate new UUIDv4\n incoming_trace_id = request.headers.get(\"X-Trace-Id\")\n trace_id = incoming_trace_id or str(uuid.uuid4())\n\n # Bind trace ID to the async execution context\n token = trace_id_ctx.set(trace_id)\n try:\n response = await call_next(request)\n response.headers[\"X-Trace-Id\"] = trace_id\n return response\n finally:\n # CRITICAL: Reset context to prevent state leakage across concurrent requests\n trace_id_ctx.reset(token)\n","python","",[180,214,215,228,242,255,268,281,294,301,308,346,351,369,384,390,396,402,418,437,442,448,459,468,482,497,506,514,520],{"__ignoreMap":212},[216,217,220,224],"span",{"class":218,"line":219},"line",1,[216,221,223],{"class":222},"sD7c4","import",[216,225,227],{"class":226},"sgsFI"," uuid\n",[216,229,231,234,237,239],{"class":218,"line":230},2,[216,232,233],{"class":222},"from",[216,235,236],{"class":226}," contextvars ",[216,238,223],{"class":222},[216,240,241],{"class":226}," ContextVar\n",[216,243,245,247,250,252],{"class":218,"line":244},3,[216,246,233],{"class":222},[216,248,249],{"class":226}," typing ",[216,251,223],{"class":222},[216,253,254],{"class":226}," Callable, Awaitable\n",[216,256,258,260,263,265],{"class":218,"line":257},4,[216,259,233],{"class":222},[216,261,262],{"class":226}," starlette.middleware.base ",[216,264,223],{"class":222},[216,266,267],{"class":226}," BaseHTTPMiddleware\n",[216,269,271,273,276,278],{"class":218,"line":270},5,[216,272,233],{"class":222},[216,274,275],{"class":226}," starlette.requests ",[216,277,223],{"class":222},[216,279,280],{"class":226}," Request\n",[216,282,284,286,289,291],{"class":218,"line":283},6,[216,285,233],{"class":222},[216,287,288],{"class":226}," starlette.responses ",[216,290,223],{"class":222},[216,292,293],{"class":226}," Response\n",[216,295,297],{"class":218,"line":296},7,[216,298,300],{"emptyLinePlaceholder":299},true,"\n",[216,302,304],{"class":218,"line":303},8,[216,305,307],{"class":306},"sAwPA","# Request-scoped context variable for trace ID propagation\n",[216,309,311,314,318,321,324,327,331,334,338,340,343],{"class":218,"line":310},9,[216,312,313],{"class":226},"trace_id_ctx: ContextVar[",[216,315,317],{"class":316},"sYu0t","str",[216,319,320],{"class":226},"] ",[216,322,323],{"class":222},"=",[216,325,326],{"class":226}," ContextVar(",[216,328,330],{"class":329},"sYBdl","\"trace_id\"",[216,332,333],{"class":226},", ",[216,335,337],{"class":336},"sqxcx","default",[216,339,323],{"class":222},[216,341,342],{"class":329},"\"\"",[216,344,345],{"class":226},")\n",[216,347,349],{"class":218,"line":348},10,[216,350,300],{"emptyLinePlaceholder":299},[216,352,354,357,361,364,366],{"class":218,"line":353},11,[216,355,356],{"class":222},"class",[216,358,360],{"class":359},"s7eDp"," RequestTracingMiddleware",[216,362,363],{"class":226},"(",[216,365,201],{"class":359},[216,367,368],{"class":226},"):\n",[216,370,372,375,378,381],{"class":218,"line":371},12,[216,373,374],{"class":222}," async",[216,376,377],{"class":222}," def",[216,379,380],{"class":359}," dispatch",[216,382,383],{"class":226},"(\n",[216,385,387],{"class":218,"line":386},13,[216,388,389],{"class":226}," self, request: Request, call_next: Callable[[Request], Awaitable[Response]]\n",[216,391,393],{"class":218,"line":392},14,[216,394,395],{"class":226}," ) -> Response:\n",[216,397,399],{"class":218,"line":398},15,[216,400,401],{"class":306}," # Extract from upstream proxy or generate new UUIDv4\n",[216,403,405,408,410,413,416],{"class":218,"line":404},16,[216,406,407],{"class":226}," incoming_trace_id ",[216,409,323],{"class":222},[216,411,412],{"class":226}," request.headers.get(",[216,414,415],{"class":329},"\"X-Trace-Id\"",[216,417,345],{"class":226},[216,419,421,424,426,428,431,434],{"class":218,"line":420},17,[216,422,423],{"class":226}," trace_id ",[216,425,323],{"class":222},[216,427,407],{"class":226},[216,429,430],{"class":222},"or",[216,432,433],{"class":316}," str",[216,435,436],{"class":226},"(uuid.uuid4())\n",[216,438,440],{"class":218,"line":439},18,[216,441,300],{"emptyLinePlaceholder":299},[216,443,445],{"class":218,"line":444},19,[216,446,447],{"class":306}," # Bind trace ID to the async execution context\n",[216,449,451,454,456],{"class":218,"line":450},20,[216,452,453],{"class":226}," token ",[216,455,323],{"class":222},[216,457,458],{"class":226}," trace_id_ctx.set(trace_id)\n",[216,460,462,465],{"class":218,"line":461},21,[216,463,464],{"class":222}," try",[216,466,467],{"class":226},":\n",[216,469,471,474,476,479],{"class":218,"line":470},22,[216,472,473],{"class":226}," response ",[216,475,323],{"class":222},[216,477,478],{"class":222}," await",[216,480,481],{"class":226}," call_next(request)\n",[216,483,485,488,490,492,494],{"class":218,"line":484},23,[216,486,487],{"class":226}," response.headers[",[216,489,415],{"class":329},[216,491,320],{"class":226},[216,493,323],{"class":222},[216,495,496],{"class":226}," trace_id\n",[216,498,500,503],{"class":218,"line":499},24,[216,501,502],{"class":222}," return",[216,504,505],{"class":226}," response\n",[216,507,509,512],{"class":218,"line":508},25,[216,510,511],{"class":222}," finally",[216,513,467],{"class":226},[216,515,517],{"class":218,"line":516},26,[216,518,519],{"class":306}," # CRITICAL: Reset context to prevent state leakage across concurrent requests\n",[216,521,523],{"class":218,"line":522},27,[216,524,525],{"class":226}," trace_id_ctx.reset(token)\n",[162,527,528],{},[529,530,531],"strong",{},"Production Constraints:",[533,534,535,543],"ul",{},[536,537,538,539,542],"li",{},"The ",[180,540,541],{},"finally"," block is non-negotiable. Omitting it causes stale trace IDs to leak into subsequent requests handled by the same worker process.",[536,544,545,548],{},[180,546,547],{},"uuid.uuid4()"," is highly optimized in CPython and adds negligible overhead (\u003C0.05ms).",[172,550,552],{"id":551},"injecting-trace-ids-into-request-context","Injecting Trace IDs into Request Context",[162,554,555,556,559],{},"Once the middleware binds the trace ID to ",[180,557,558],{},"contextvars",", downstream dependencies and route handlers can access it without explicit parameter passing. This eliminates signature pollution and maintains clean separation between routing logic and observability concerns.",[207,561,563],{"className":209,"code":562,"language":211,"meta":212,"style":212},"from fastapi import Depends, FastAPI, HTTPException\nfrom starlette.status import HTTP_200_OK\n\napp = FastAPI()\n\ndef get_active_trace_id() -> str:\n \"\"\"FastAPI dependency to safely retrieve the active request trace ID.\"\"\"\n trace_id = trace_id_ctx.get()\n if not trace_id:\n raise HTTPException(status_code=500, detail=\"Trace context missing\")\n return trace_id\n\n@app.get(\"\u002Fapi\u002Fv1\u002Fhealth\", status_code=HTTP_200_OK)\nasync def health_check(trace_id: str = Depends(get_active_trace_id)) -> dict:\n # trace_id is automatically injected via DI\n return {\"status\": \"healthy\", \"trace_id\": trace_id}\n",[180,564,565,577,589,593,603,607,622,627,636,647,675,681,685,706,732,737],{"__ignoreMap":212},[216,566,567,569,572,574],{"class":218,"line":219},[216,568,233],{"class":222},[216,570,571],{"class":226}," fastapi ",[216,573,223],{"class":222},[216,575,576],{"class":226}," Depends, FastAPI, HTTPException\n",[216,578,579,581,584,586],{"class":218,"line":230},[216,580,233],{"class":222},[216,582,583],{"class":226}," starlette.status ",[216,585,223],{"class":222},[216,587,588],{"class":316}," HTTP_200_OK\n",[216,590,591],{"class":218,"line":244},[216,592,300],{"emptyLinePlaceholder":299},[216,594,595,598,600],{"class":218,"line":257},[216,596,597],{"class":226},"app ",[216,599,323],{"class":222},[216,601,602],{"class":226}," FastAPI()\n",[216,604,605],{"class":218,"line":270},[216,606,300],{"emptyLinePlaceholder":299},[216,608,609,612,615,618,620],{"class":218,"line":283},[216,610,611],{"class":222},"def",[216,613,614],{"class":359}," get_active_trace_id",[216,616,617],{"class":226},"() -> ",[216,619,317],{"class":316},[216,621,467],{"class":226},[216,623,624],{"class":218,"line":296},[216,625,626],{"class":329}," \"\"\"FastAPI dependency to safely retrieve the active request trace ID.\"\"\"\n",[216,628,629,631,633],{"class":218,"line":303},[216,630,423],{"class":226},[216,632,323],{"class":222},[216,634,635],{"class":226}," trace_id_ctx.get()\n",[216,637,638,641,644],{"class":218,"line":310},[216,639,640],{"class":222}," if",[216,642,643],{"class":222}," not",[216,645,646],{"class":226}," trace_id:\n",[216,648,649,652,655,658,660,663,665,668,670,673],{"class":218,"line":348},[216,650,651],{"class":222}," raise",[216,653,654],{"class":226}," HTTPException(",[216,656,657],{"class":336},"status_code",[216,659,323],{"class":222},[216,661,662],{"class":316},"500",[216,664,333],{"class":226},[216,666,667],{"class":336},"detail",[216,669,323],{"class":222},[216,671,672],{"class":329},"\"Trace context missing\"",[216,674,345],{"class":226},[216,676,677,679],{"class":218,"line":353},[216,678,502],{"class":222},[216,680,496],{"class":226},[216,682,683],{"class":218,"line":371},[216,684,300],{"emptyLinePlaceholder":299},[216,686,687,690,692,695,697,699,701,704],{"class":218,"line":386},[216,688,689],{"class":359},"@app.get",[216,691,363],{"class":226},[216,693,694],{"class":329},"\"\u002Fapi\u002Fv1\u002Fhealth\"",[216,696,333],{"class":226},[216,698,657],{"class":336},[216,700,323],{"class":222},[216,702,703],{"class":316},"HTTP_200_OK",[216,705,345],{"class":226},[216,707,708,711,713,716,719,721,724,727,730],{"class":218,"line":392},[216,709,710],{"class":222},"async",[216,712,377],{"class":222},[216,714,715],{"class":359}," health_check",[216,717,718],{"class":226},"(trace_id: ",[216,720,317],{"class":316},[216,722,723],{"class":222}," =",[216,725,726],{"class":226}," Depends(get_active_trace_id)) -> ",[216,728,729],{"class":316},"dict",[216,731,467],{"class":226},[216,733,734],{"class":218,"line":398},[216,735,736],{"class":306}," # trace_id is automatically injected via DI\n",[216,738,739,741,744,747,750,753,755,757],{"class":218,"line":404},[216,740,502],{"class":222},[216,742,743],{"class":226}," {",[216,745,746],{"class":329},"\"status\"",[216,748,749],{"class":226},": ",[216,751,752],{"class":329},"\"healthy\"",[216,754,333],{"class":226},[216,756,330],{"class":329},[216,758,759],{"class":226},": trace_id}\n",[162,761,762],{},[529,763,764],{},"Context Propagation Rules:",[533,766,767,780],{},[536,768,769,771,772,775,776,779],{},[180,770,558],{}," automatically propagate to child tasks spawned via ",[180,773,774],{},"asyncio.create_task()"," ",[529,777,778],{},"only if"," the task is created within the same coroutine scope.",[536,781,782,783,786,787,790],{},"For ",[180,784,785],{},"BackgroundTasks"," or external thread pools, explicitly capture ",[180,788,789],{},"trace_id_ctx.get()"," before dispatch and pass it as an argument.",[172,792,794],{"id":793},"configuration-environment-overrides","Configuration & Environment Overrides",[162,796,797,798,801],{},"Hardcoding middleware behavior violates twelve-factor principles. Use Pydantic ",[180,799,800],{},"BaseSettings"," to toggle tracing, adjust header names, and implement sampling strategies via environment variables.",[207,803,805],{"className":209,"code":804,"language":211,"meta":212,"style":212},"from pydantic_settings import BaseSettings\nfrom pydantic import Field\n\nclass TracingConfig(BaseSettings):\n enabled: bool = Field(default=True, description=\"Toggle request tracing globally\")\n header_name: str = Field(default=\"X-Trace-Id\", description=\"HTTP header used for trace propagation\")\n sampling_rate: float = Field(default=1.0, ge=0.0, le=1.0, description=\"Probability of tracing a request (0.0-1.0)\")\n\n model_config = {\"env_prefix\": \"TRACE_\"}\n\nconfig = TracingConfig()\n\n# Conditional middleware registration\nif config.enabled:\n app.add_middleware(RequestTracingMiddleware)\n",[180,806,807,819,831,835,848,880,908,957,961,981,985,995,999,1004,1012],{"__ignoreMap":212},[216,808,809,811,814,816],{"class":218,"line":219},[216,810,233],{"class":222},[216,812,813],{"class":226}," pydantic_settings ",[216,815,223],{"class":222},[216,817,818],{"class":226}," BaseSettings\n",[216,820,821,823,826,828],{"class":218,"line":230},[216,822,233],{"class":222},[216,824,825],{"class":226}," pydantic ",[216,827,223],{"class":222},[216,829,830],{"class":226}," Field\n",[216,832,833],{"class":218,"line":244},[216,834,300],{"emptyLinePlaceholder":299},[216,836,837,839,842,844,846],{"class":218,"line":257},[216,838,356],{"class":222},[216,840,841],{"class":359}," TracingConfig",[216,843,363],{"class":226},[216,845,800],{"class":359},[216,847,368],{"class":226},[216,849,850,853,856,858,861,863,865,868,870,873,875,878],{"class":218,"line":270},[216,851,852],{"class":226}," enabled: ",[216,854,855],{"class":316},"bool",[216,857,723],{"class":222},[216,859,860],{"class":226}," Field(",[216,862,337],{"class":336},[216,864,323],{"class":222},[216,866,867],{"class":316},"True",[216,869,333],{"class":226},[216,871,872],{"class":336},"description",[216,874,323],{"class":222},[216,876,877],{"class":329},"\"Toggle request tracing globally\"",[216,879,345],{"class":226},[216,881,882,885,887,889,891,893,895,897,899,901,903,906],{"class":218,"line":283},[216,883,884],{"class":226}," header_name: ",[216,886,317],{"class":316},[216,888,723],{"class":222},[216,890,860],{"class":226},[216,892,337],{"class":336},[216,894,323],{"class":222},[216,896,415],{"class":329},[216,898,333],{"class":226},[216,900,872],{"class":336},[216,902,323],{"class":222},[216,904,905],{"class":329},"\"HTTP header used for trace propagation\"",[216,907,345],{"class":226},[216,909,910,913,916,918,920,922,924,927,929,932,934,937,939,942,944,946,948,950,952,955],{"class":218,"line":296},[216,911,912],{"class":226}," sampling_rate: ",[216,914,915],{"class":316},"float",[216,917,723],{"class":222},[216,919,860],{"class":226},[216,921,337],{"class":336},[216,923,323],{"class":222},[216,925,926],{"class":316},"1.0",[216,928,333],{"class":226},[216,930,931],{"class":336},"ge",[216,933,323],{"class":222},[216,935,936],{"class":316},"0.0",[216,938,333],{"class":226},[216,940,941],{"class":336},"le",[216,943,323],{"class":222},[216,945,926],{"class":316},[216,947,333],{"class":226},[216,949,872],{"class":336},[216,951,323],{"class":222},[216,953,954],{"class":329},"\"Probability of tracing a request (0.0-1.0)\"",[216,956,345],{"class":226},[216,958,959],{"class":218,"line":303},[216,960,300],{"emptyLinePlaceholder":299},[216,962,963,966,968,970,973,975,978],{"class":218,"line":310},[216,964,965],{"class":226}," model_config ",[216,967,323],{"class":222},[216,969,743],{"class":226},[216,971,972],{"class":329},"\"env_prefix\"",[216,974,749],{"class":226},[216,976,977],{"class":329},"\"TRACE_\"",[216,979,980],{"class":226},"}\n",[216,982,983],{"class":218,"line":348},[216,984,300],{"emptyLinePlaceholder":299},[216,986,987,990,992],{"class":218,"line":353},[216,988,989],{"class":226},"config ",[216,991,323],{"class":222},[216,993,994],{"class":226}," TracingConfig()\n",[216,996,997],{"class":218,"line":371},[216,998,300],{"emptyLinePlaceholder":299},[216,1000,1001],{"class":218,"line":386},[216,1002,1003],{"class":306},"# Conditional middleware registration\n",[216,1005,1006,1009],{"class":218,"line":392},[216,1007,1008],{"class":222},"if",[216,1010,1011],{"class":226}," config.enabled:\n",[216,1013,1014],{"class":218,"line":398},[216,1015,1016],{"class":226}," app.add_middleware(RequestTracingMiddleware)\n",[162,1018,1019],{},[529,1020,1021],{},"Environment Overrides:",[533,1023,1024,1030,1036],{},[536,1025,1026,1029],{},[180,1027,1028],{},"TRACE_ENABLED=false"," disables tracing in local development or staging.",[536,1031,1032,1035],{},[180,1033,1034],{},"TRACE_SAMPLING_RATE=0.1"," traces 10% of traffic, reducing log volume in high-throughput production environments.",[536,1037,1038,1039,1042],{},"Upstream proxies (NGINX, AWS ALB) can inject custom headers by setting ",[180,1040,1041],{},"TRACE_HEADER_NAME=X-Correlation-Id",".",[172,1044,1046],{"id":1045},"debugging-performance-validation","Debugging & Performance Validation",[1048,1049,1051],"h3",{"id":1050},"_1-header-presence-testing","1. Header Presence Testing",[162,1053,1054,1055,1058],{},"Use ",[180,1056,1057],{},"fastapi.testclient.TestClient"," to assert trace ID generation and propagation without spinning up a live server.",[207,1060,1062],{"className":209,"code":1061,"language":211,"meta":212,"style":212},"from fastapi.testclient import TestClient\n\ndef test_trace_id_generation() -> None:\n client = TestClient(app)\n response = client.get(\"\u002Fapi\u002Fv1\u002Fhealth\")\n assert response.status_code == 200\n assert \"X-Trace-Id\" in response.headers\n assert len(response.headers[\"X-Trace-Id\"]) == 36 # UUIDv4 format\n",[180,1063,1064,1076,1080,1094,1104,1117,1131,1144],{"__ignoreMap":212},[216,1065,1066,1068,1071,1073],{"class":218,"line":219},[216,1067,233],{"class":222},[216,1069,1070],{"class":226}," fastapi.testclient ",[216,1072,223],{"class":222},[216,1074,1075],{"class":226}," TestClient\n",[216,1077,1078],{"class":218,"line":230},[216,1079,300],{"emptyLinePlaceholder":299},[216,1081,1082,1084,1087,1089,1092],{"class":218,"line":244},[216,1083,611],{"class":222},[216,1085,1086],{"class":359}," test_trace_id_generation",[216,1088,617],{"class":226},[216,1090,1091],{"class":316},"None",[216,1093,467],{"class":226},[216,1095,1096,1099,1101],{"class":218,"line":257},[216,1097,1098],{"class":226}," client ",[216,1100,323],{"class":222},[216,1102,1103],{"class":226}," TestClient(app)\n",[216,1105,1106,1108,1110,1113,1115],{"class":218,"line":270},[216,1107,473],{"class":226},[216,1109,323],{"class":222},[216,1111,1112],{"class":226}," client.get(",[216,1114,694],{"class":329},[216,1116,345],{"class":226},[216,1118,1119,1122,1125,1128],{"class":218,"line":283},[216,1120,1121],{"class":222}," assert",[216,1123,1124],{"class":226}," response.status_code ",[216,1126,1127],{"class":222},"==",[216,1129,1130],{"class":316}," 200\n",[216,1132,1133,1135,1138,1141],{"class":218,"line":296},[216,1134,1121],{"class":222},[216,1136,1137],{"class":329}," \"X-Trace-Id\"",[216,1139,1140],{"class":222}," in",[216,1142,1143],{"class":226}," response.headers\n",[216,1145,1146,1148,1151,1154,1156,1159,1161,1164],{"class":218,"line":303},[216,1147,1121],{"class":222},[216,1149,1150],{"class":316}," len",[216,1152,1153],{"class":226},"(response.headers[",[216,1155,415],{"class":329},[216,1157,1158],{"class":226},"]) ",[216,1160,1127],{"class":222},[216,1162,1163],{"class":316}," 36",[216,1165,1166],{"class":306}," # UUIDv4 format\n",[1048,1168,1170],{"id":1169},"_2-latency-profiling","2. Latency Profiling",[162,1172,1173,1174,1177],{},"Middleware overhead should remain sub-millisecond. Profile with ",[180,1175,1176],{},"pyinstrument"," to isolate blocking calls:",[207,1179,1183],{"className":1180,"code":1181,"language":1182,"meta":212,"style":212},"language-bash shiki shiki-themes github-light","pyinstrument -r html -o profile.html -m uvicorn main:app --host 0.0.0.0 --port 8000\n","bash",[180,1184,1185],{"__ignoreMap":212},[216,1186,1187,1189,1192,1195,1198,1201,1204,1207,1210,1213,1216,1219],{"class":218,"line":219},[216,1188,1176],{"class":359},[216,1190,1191],{"class":316}," -r",[216,1193,1194],{"class":329}," html",[216,1196,1197],{"class":316}," -o",[216,1199,1200],{"class":329}," profile.html",[216,1202,1203],{"class":316}," -m",[216,1205,1206],{"class":329}," uvicorn",[216,1208,1209],{"class":329}," main:app",[216,1211,1212],{"class":316}," --host",[216,1214,1215],{"class":316}," 0.0.0.0",[216,1217,1218],{"class":316}," --port",[216,1220,1221],{"class":316}," 8000\n",[162,1223,1224,1225,333,1228,333,1231,1234,1235,1042],{},"If P99 latency spikes, verify that no synchronous I\u002FO (e.g., ",[180,1226,1227],{},"requests",[180,1229,1230],{},"sqlite3",[180,1232,1233],{},"logging.FileHandler",") executes inside ",[180,1236,1237],{},"dispatch()",[1048,1239,1241],{"id":1240},"_3-background-task-websocket-caveats","3. Background Task & WebSocket Caveats",[533,1243,1244,1258],{},[536,1245,1246,775,1249,1251,1252,1254,1255],{},[529,1247,1248],{},"Background Tasks:",[180,1250,558],{}," do not auto-propagate to ",[180,1253,785],{}," because they run in separate event loop cycles. Capture the ID explicitly: ",[180,1256,1257],{},"bg_task_id = trace_id_ctx.get(); background_tasks.add_task(worker, bg_task_id)",[536,1259,1260,775,1263,1265,1266,1269,1270,1042],{},[529,1261,1262],{},"WebSockets:",[180,1264,201],{}," only intercepts HTTP requests. For WebSocket tracing, implement a raw ASGI middleware that inspects ",[180,1267,1268],{},"scope[\"type\"] == \"websocket\""," and attaches metadata to ",[180,1271,1272],{},"scope[\"state\"]",[172,1274,1276],{"id":1275},"common-production-pitfalls","Common Production Pitfalls",[1278,1279,1280,1296],"table",{},[1281,1282,1283],"thead",{},[1284,1285,1286,1290,1293],"tr",{},[1287,1288,1289],"th",{},"Anti-Pattern",[1287,1291,1292],{},"Impact",[1287,1294,1295],{},"Production Fix",[1297,1298,1299,1315,1339],"tbody",{},[1284,1300,1301,1305,1308],{},[1302,1303,1304],"td",{},"Using module-level globals for trace state",[1302,1306,1307],{},"Race conditions corrupt logs across concurrent requests",[1302,1309,1310,1311,1314],{},"Always use ",[180,1312,1313],{},"contextvars.ContextVar"," for async-safe isolation",[1284,1316,1317,1326,1329],{},[1302,1318,1319,1320,1323,1324],{},"Omitting ",[180,1321,1322],{},"trace_id_ctx.reset(token)"," in ",[180,1325,541],{},[1302,1327,1328],{},"Memory leaks and stale IDs persist across worker lifecycles",[1302,1330,1331,1332,1323,1335,1338],{},"Wrap ",[180,1333,1334],{},"call_next",[180,1336,1337],{},"try\u002Ffinally"," to guarantee context cleanup",[1284,1340,1341,1346,1349],{},[1302,1342,1343,1344],{},"Executing blocking I\u002FO in ",[180,1345,1237],{},[1302,1347,1348],{},"Event loop starvation, P99 latency spikes >500ms",[1302,1350,1054,1351,1354,1355],{},[180,1352,1353],{},"await"," for all network\u002FDB calls; offload sync work to ",[180,1356,1357],{},"run_in_executor",[172,1359,1361],{"id":1360},"frequently-asked-questions","Frequently Asked Questions",[162,1363,1364],{},[529,1365,1366],{},"Does custom request tracing middleware impact API latency?",[162,1368,1369,1370,1372],{},"Minimal overhead (\u003C1ms) when using ",[180,1371,558],{}," and avoiding blocking I\u002FO. The primary cost is header parsing and UUID generation, both highly optimized in CPython.",[162,1374,1375],{},[529,1376,1377],{},"How do I ensure trace IDs propagate to background tasks?",[162,1379,1380,1381,1383,1384,1042],{},"Capture the trace ID before dispatching the task using ",[180,1382,789],{}," and explicitly pass it as an argument. Inside the task, set it in a new context scope if downstream dependencies rely on ",[180,1385,558],{},[162,1387,1388],{},[529,1389,1390],{},"Can this middleware coexist with CORS and authentication middleware?",[162,1392,1393],{},"Yes. FastAPI executes middleware in reverse order of registration. Place tracing middleware closest to the application core (register it last) to ensure it captures all routed requests after CORS preflight and authentication validation.",[1395,1396,1397],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":212,"searchDepth":230,"depth":230,"links":1399},[1400,1401,1402,1403,1404,1409,1410],{"id":174,"depth":230,"text":175},{"id":190,"depth":230,"text":191},{"id":551,"depth":230,"text":552},{"id":793,"depth":230,"text":794},{"id":1045,"depth":230,"text":1046,"children":1405},[1406,1407,1408],{"id":1050,"depth":244,"text":1051},{"id":1169,"depth":244,"text":1170},{"id":1240,"depth":244,"text":1241},{"id":1275,"depth":230,"text":1276},{"id":1360,"depth":230,"text":1361},"Debugging asynchronous API calls in production requires precise request correlation. This guide details how to implement custom middleware for request…","md",{},{"title":135,"description":1411},"UlS3xJKWBMbPa5zABQ3bz8B8kbiVBAyc6E8bRpt2rHY",[1417,1417],null,1778082655325]