[{"data":1,"prerenderedAt":1154},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002F":1152},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic","\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},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Customizing OpenAPI Schema Generation in FastAPI","\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",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Handling Deeply Nested JSON Models Efficiently","\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",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"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",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"FastAPI App Factory Pattern for Testing and Deployment","\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",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"Managing Environment Variables with Pydantic Settings","\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",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"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",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"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",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"Implementing Custom Middleware for Request Tracing","\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",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"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",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":189,"body":312,"description":1103,"extension":1104,"meta":1105,"navigation":447,"path":190,"seo":1150,"stem":191,"__hash__":1151},"content\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex.md",{"type":313,"value":314,"toc":1090},"minimark",[315,319,326,354,368,373,376,380,390,394,399,562,566,715,719,790,794,926,930,950,954,1061,1065,1086],[316,317,189],"h1",{"id":318},"structured-json-logging-with-request-ids-in-fastapi",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,338,345,348,351],"ul",{},[330,331,332,333,337],"li",{},"Store the correlation ID in a ",[334,335,336],"code",{},"contextvar"," so it is ambient for the whole request.",[330,339,340,341,344],{},"A logging filter injects ",[334,342,343],{},"request_id"," into every record automatically.",[330,346,347],{},"A JSON formatter makes logs machine-parseable and filterable.",[330,349,350],{},"Add trace and span IDs so a log line links to its trace.",[330,352,353],{},"Configure the handler, filter, and formatter once at startup.",[320,355,356,357,362,363,367],{},"This guide builds the logging substrate that ",[358,359,361],"a",{"href":360},"\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002F","Observability and Tracing"," relies on, extending the ",[358,364,366],{"href":365},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F","request-tracing middleware",".",[369,370,372],"h2",{"id":371},"the-problem-this-solves","The Problem This Solves",[320,374,375],{},"When an incident spans many log lines and several services, free-text logs are nearly useless — you cannot reliably isolate one request. Structured JSON logs keyed by a correlation ID turn that into a single, precise filter, and they link cleanly to traces and metrics.",[369,377,379],{"id":378},"prerequisites","Prerequisites",[327,381,382,387],{},[330,383,384,385,367],{},"Tracing middleware that sets a correlation ID in a ",[334,386,336],{},[330,388,389],{},"A log backend that ingests JSON (most do).",[369,391,393],{"id":392},"step-by-step-implementation","Step-by-Step Implementation",[395,396,398],"h3",{"id":397},"_1-the-contextvar-and-filter","1. The contextvar and filter",[400,401,406],"pre",{"className":402,"code":403,"language":404,"meta":405,"style":405},"language-python shiki shiki-themes github-light","# app\u002Flogging_setup.py\nimport logging\nfrom contextvars import ContextVar\n\nrequest_id_ctx: ContextVar[str] = ContextVar(\"request_id\", default=\"-\")\n\n\nclass ContextFilter(logging.Filter):\n    def filter(self, record: logging.LogRecord) -> bool:\n        record.request_id = request_id_ctx.get()   # Present on every record.\n        return True\n","python","",[334,407,408,417,428,442,449,487,492,497,521,539,553],{"__ignoreMap":405},[409,410,413],"span",{"class":411,"line":412},"line",1,[409,414,416],{"class":415},"sAwPA","# app\u002Flogging_setup.py\n",[409,418,420,424],{"class":411,"line":419},2,[409,421,423],{"class":422},"sD7c4","import",[409,425,427],{"class":426},"sgsFI"," logging\n",[409,429,431,434,437,439],{"class":411,"line":430},3,[409,432,433],{"class":422},"from",[409,435,436],{"class":426}," contextvars ",[409,438,423],{"class":422},[409,440,441],{"class":426}," ContextVar\n",[409,443,445],{"class":411,"line":444},4,[409,446,448],{"emptyLinePlaceholder":447},true,"\n",[409,450,452,455,459,462,465,468,472,475,479,481,484],{"class":411,"line":451},5,[409,453,454],{"class":426},"request_id_ctx: ContextVar[",[409,456,458],{"class":457},"sYu0t","str",[409,460,461],{"class":426},"] ",[409,463,464],{"class":422},"=",[409,466,467],{"class":426}," ContextVar(",[409,469,471],{"class":470},"sYBdl","\"request_id\"",[409,473,474],{"class":426},", ",[409,476,478],{"class":477},"sqxcx","default",[409,480,464],{"class":422},[409,482,483],{"class":470},"\"-\"",[409,485,486],{"class":426},")\n",[409,488,490],{"class":411,"line":489},6,[409,491,448],{"emptyLinePlaceholder":447},[409,493,495],{"class":411,"line":494},7,[409,496,448],{"emptyLinePlaceholder":447},[409,498,500,503,507,510,513,515,518],{"class":411,"line":499},8,[409,501,502],{"class":422},"class",[409,504,506],{"class":505},"s7eDp"," ContextFilter",[409,508,509],{"class":426},"(",[409,511,512],{"class":505},"logging",[409,514,367],{"class":426},[409,516,517],{"class":505},"Filter",[409,519,520],{"class":426},"):\n",[409,522,524,527,530,533,536],{"class":411,"line":523},9,[409,525,526],{"class":422},"    def",[409,528,529],{"class":457}," filter",[409,531,532],{"class":426},"(self, record: logging.LogRecord) -> ",[409,534,535],{"class":457},"bool",[409,537,538],{"class":426},":\n",[409,540,542,545,547,550],{"class":411,"line":541},10,[409,543,544],{"class":426},"        record.request_id ",[409,546,464],{"class":422},[409,548,549],{"class":426}," request_id_ctx.get()   ",[409,551,552],{"class":415},"# Present on every record.\n",[409,554,556,559],{"class":411,"line":555},11,[409,557,558],{"class":422},"        return",[409,560,561],{"class":457}," True\n",[395,563,565],{"id":564},"_2-a-json-formatter","2. A JSON formatter",[400,567,569],{"className":402,"code":568,"language":404,"meta":405,"style":405},"import json\n\n\nclass JsonFormatter(logging.Formatter):\n    def format(self, record: logging.LogRecord) -> str:\n        payload = {\n            \"level\": record.levelname,\n            \"logger\": record.name,\n            \"request_id\": getattr(record, \"request_id\", \"-\"),\n            \"message\": record.getMessage(),\n        }\n        if record.exc_info:\n            payload[\"exc\"] = self.formatException(record.exc_info)\n        return json.dumps(payload)\n",[334,570,571,578,582,586,604,617,627,635,643,666,674,679,688,707],{"__ignoreMap":405},[409,572,573,575],{"class":411,"line":412},[409,574,423],{"class":422},[409,576,577],{"class":426}," json\n",[409,579,580],{"class":411,"line":419},[409,581,448],{"emptyLinePlaceholder":447},[409,583,584],{"class":411,"line":430},[409,585,448],{"emptyLinePlaceholder":447},[409,587,588,590,593,595,597,599,602],{"class":411,"line":444},[409,589,502],{"class":422},[409,591,592],{"class":505}," JsonFormatter",[409,594,509],{"class":426},[409,596,512],{"class":505},[409,598,367],{"class":426},[409,600,601],{"class":505},"Formatter",[409,603,520],{"class":426},[409,605,606,608,611,613,615],{"class":411,"line":451},[409,607,526],{"class":422},[409,609,610],{"class":457}," format",[409,612,532],{"class":426},[409,614,458],{"class":457},[409,616,538],{"class":426},[409,618,619,622,624],{"class":411,"line":489},[409,620,621],{"class":426},"        payload ",[409,623,464],{"class":422},[409,625,626],{"class":426}," {\n",[409,628,629,632],{"class":411,"line":494},[409,630,631],{"class":470},"            \"level\"",[409,633,634],{"class":426},": record.levelname,\n",[409,636,637,640],{"class":411,"line":499},[409,638,639],{"class":470},"            \"logger\"",[409,641,642],{"class":426},": record.name,\n",[409,644,645,648,651,654,657,659,661,663],{"class":411,"line":523},[409,646,647],{"class":470},"            \"request_id\"",[409,649,650],{"class":426},": ",[409,652,653],{"class":457},"getattr",[409,655,656],{"class":426},"(record, ",[409,658,471],{"class":470},[409,660,474],{"class":426},[409,662,483],{"class":470},[409,664,665],{"class":426},"),\n",[409,667,668,671],{"class":411,"line":541},[409,669,670],{"class":470},"            \"message\"",[409,672,673],{"class":426},": record.getMessage(),\n",[409,675,676],{"class":411,"line":555},[409,677,678],{"class":426},"        }\n",[409,680,682,685],{"class":411,"line":681},12,[409,683,684],{"class":422},"        if",[409,686,687],{"class":426}," record.exc_info:\n",[409,689,691,694,697,699,701,704],{"class":411,"line":690},13,[409,692,693],{"class":426},"            payload[",[409,695,696],{"class":470},"\"exc\"",[409,698,461],{"class":426},[409,700,464],{"class":422},[409,702,703],{"class":457}," self",[409,705,706],{"class":426},".formatException(record.exc_info)\n",[409,708,710,712],{"class":411,"line":709},14,[409,711,558],{"class":422},[409,713,714],{"class":426}," json.dumps(payload)\n",[395,716,718],{"id":717},"_3-configure-once-at-startup","3. Configure once at startup",[400,720,722],{"className":402,"code":721,"language":404,"meta":405,"style":405},"def configure_logging() -> None:\n    handler = logging.StreamHandler()\n    handler.addFilter(ContextFilter())\n    handler.setFormatter(JsonFormatter())\n    root = logging.getLogger()\n    root.handlers = [handler]\n    root.setLevel(logging.INFO)\n",[334,723,724,740,750,755,760,770,780],{"__ignoreMap":405},[409,725,726,729,732,735,738],{"class":411,"line":412},[409,727,728],{"class":422},"def",[409,730,731],{"class":505}," configure_logging",[409,733,734],{"class":426},"() -> ",[409,736,737],{"class":457},"None",[409,739,538],{"class":426},[409,741,742,745,747],{"class":411,"line":419},[409,743,744],{"class":426},"    handler ",[409,746,464],{"class":422},[409,748,749],{"class":426}," logging.StreamHandler()\n",[409,751,752],{"class":411,"line":430},[409,753,754],{"class":426},"    handler.addFilter(ContextFilter())\n",[409,756,757],{"class":411,"line":444},[409,758,759],{"class":426},"    handler.setFormatter(JsonFormatter())\n",[409,761,762,765,767],{"class":411,"line":451},[409,763,764],{"class":426},"    root ",[409,766,464],{"class":422},[409,768,769],{"class":426}," logging.getLogger()\n",[409,771,772,775,777],{"class":411,"line":489},[409,773,774],{"class":426},"    root.handlers ",[409,776,464],{"class":422},[409,778,779],{"class":426}," [handler]\n",[409,781,782,785,788],{"class":411,"line":494},[409,783,784],{"class":426},"    root.setLevel(logging.",[409,786,787],{"class":457},"INFO",[409,789,486],{"class":426},[395,791,793],{"id":792},"_4-set-the-id-in-middleware","4. Set the ID in middleware",[400,795,797],{"className":402,"code":796,"language":404,"meta":405,"style":405},"import uuid\n\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\n\nclass RequestIdMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request, call_next):\n        rid = request.headers.get(\"x-request-id\") or str(uuid.uuid4())\n        token = request_id_ctx.set(rid)\n        try:\n            return await call_next(request)\n        finally:\n            request_id_ctx.reset(token)   # Avoid leaking the value across requests.\n",[334,798,799,806,810,822,826,830,844,858,883,893,900,911,918],{"__ignoreMap":405},[409,800,801,803],{"class":411,"line":412},[409,802,423],{"class":422},[409,804,805],{"class":426}," uuid\n",[409,807,808],{"class":411,"line":419},[409,809,448],{"emptyLinePlaceholder":447},[409,811,812,814,817,819],{"class":411,"line":430},[409,813,433],{"class":422},[409,815,816],{"class":426}," starlette.middleware.base ",[409,818,423],{"class":422},[409,820,821],{"class":426}," BaseHTTPMiddleware\n",[409,823,824],{"class":411,"line":444},[409,825,448],{"emptyLinePlaceholder":447},[409,827,828],{"class":411,"line":451},[409,829,448],{"emptyLinePlaceholder":447},[409,831,832,834,837,839,842],{"class":411,"line":489},[409,833,502],{"class":422},[409,835,836],{"class":505}," RequestIdMiddleware",[409,838,509],{"class":426},[409,840,841],{"class":505},"BaseHTTPMiddleware",[409,843,520],{"class":426},[409,845,846,849,852,855],{"class":411,"line":494},[409,847,848],{"class":422},"    async",[409,850,851],{"class":422}," def",[409,853,854],{"class":505}," dispatch",[409,856,857],{"class":426},"(self, request, call_next):\n",[409,859,860,863,865,868,871,874,877,880],{"class":411,"line":499},[409,861,862],{"class":426},"        rid ",[409,864,464],{"class":422},[409,866,867],{"class":426}," request.headers.get(",[409,869,870],{"class":470},"\"x-request-id\"",[409,872,873],{"class":426},") ",[409,875,876],{"class":422},"or",[409,878,879],{"class":457}," str",[409,881,882],{"class":426},"(uuid.uuid4())\n",[409,884,885,888,890],{"class":411,"line":523},[409,886,887],{"class":426},"        token ",[409,889,464],{"class":422},[409,891,892],{"class":426}," request_id_ctx.set(rid)\n",[409,894,895,898],{"class":411,"line":541},[409,896,897],{"class":422},"        try",[409,899,538],{"class":426},[409,901,902,905,908],{"class":411,"line":555},[409,903,904],{"class":422},"            return",[409,906,907],{"class":422}," await",[409,909,910],{"class":426}," call_next(request)\n",[409,912,913,916],{"class":411,"line":681},[409,914,915],{"class":422},"        finally",[409,917,538],{"class":426},[409,919,920,923],{"class":411,"line":690},[409,921,922],{"class":426},"            request_id_ctx.reset(token)   ",[409,924,925],{"class":415},"# Avoid leaking the value across requests.\n",[369,927,929],{"id":928},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,931,932,938,944],{},[330,933,934,937],{},[323,935,936],{},"Uvicorn access logs."," They use their own format; align them with your JSON formatter or log access in middleware.",[330,939,940,943],{},[323,941,942],{},"Background tasks."," A job runs outside the request context; forward the ID into the task and re-set the contextvar.",[330,945,946,949],{},[323,947,948],{},"Sensitive data."," Never log secrets or full request bodies; log identifiers and shapes.",[369,951,953],{"id":952},"verification","Verification",[400,955,957],{"className":402,"code":956,"language":404,"meta":405,"style":405},"import json\n\n\ndef test_logs_are_json_with_request_id(client, capsys):\n    client.get(\"\u002Fhealth\", headers={\"x-request-id\": \"rid-7\"})\n    line = capsys.readouterr().out.strip().splitlines()[-1]\n    record = json.loads(line)                 # Parses as JSON.\n    assert record[\"request_id\"] == \"rid-7\"\n",[334,958,959,965,969,973,983,1011,1030,1043],{"__ignoreMap":405},[409,960,961,963],{"class":411,"line":412},[409,962,423],{"class":422},[409,964,577],{"class":426},[409,966,967],{"class":411,"line":419},[409,968,448],{"emptyLinePlaceholder":447},[409,970,971],{"class":411,"line":430},[409,972,448],{"emptyLinePlaceholder":447},[409,974,975,977,980],{"class":411,"line":444},[409,976,728],{"class":422},[409,978,979],{"class":505}," test_logs_are_json_with_request_id",[409,981,982],{"class":426},"(client, capsys):\n",[409,984,985,988,991,993,996,998,1001,1003,1005,1008],{"class":411,"line":451},[409,986,987],{"class":426},"    client.get(",[409,989,990],{"class":470},"\"\u002Fhealth\"",[409,992,474],{"class":426},[409,994,995],{"class":477},"headers",[409,997,464],{"class":422},[409,999,1000],{"class":426},"{",[409,1002,870],{"class":470},[409,1004,650],{"class":426},[409,1006,1007],{"class":470},"\"rid-7\"",[409,1009,1010],{"class":426},"})\n",[409,1012,1013,1016,1018,1021,1024,1027],{"class":411,"line":489},[409,1014,1015],{"class":426},"    line ",[409,1017,464],{"class":422},[409,1019,1020],{"class":426}," capsys.readouterr().out.strip().splitlines()[",[409,1022,1023],{"class":422},"-",[409,1025,1026],{"class":457},"1",[409,1028,1029],{"class":426},"]\n",[409,1031,1032,1035,1037,1040],{"class":411,"line":494},[409,1033,1034],{"class":426},"    record ",[409,1036,464],{"class":422},[409,1038,1039],{"class":426}," json.loads(line)                 ",[409,1041,1042],{"class":415},"# Parses as JSON.\n",[409,1044,1045,1048,1051,1053,1055,1058],{"class":411,"line":499},[409,1046,1047],{"class":422},"    assert",[409,1049,1050],{"class":426}," record[",[409,1052,471],{"class":470},[409,1054,461],{"class":426},[409,1056,1057],{"class":422},"==",[409,1059,1060],{"class":470}," \"rid-7\"\n",[369,1062,1064],{"id":1063},"related-reading","Related Reading",[327,1066,1067,1075],{},[330,1068,1069,1072,1073,367],{},[323,1070,1071],{},"Up to the topic:"," ",[358,1074,361],{"href":360},[330,1076,1077,1072,1080,1083,1084,367],{},[323,1078,1079],{},"Related guides:",[358,1081,183],{"href":1082},"\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002F"," and ",[358,1085,287],{"href":365},[1087,1088,1089],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 .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":405,"searchDepth":419,"depth":419,"links":1091},[1092,1093,1094,1100,1101,1102],{"id":371,"depth":419,"text":372},{"id":378,"depth":419,"text":379},{"id":392,"depth":419,"text":393,"children":1095},[1096,1097,1098,1099],{"id":397,"depth":430,"text":398},{"id":564,"depth":430,"text":565},{"id":717,"depth":430,"text":718},{"id":792,"depth":430,"text":793},{"id":928,"depth":419,"text":929},{"id":952,"depth":419,"text":953},{"id":1063,"depth":419,"text":1064},"Set up structured JSON logging in FastAPI: a JSON formatter, a contextvar-backed request ID filter, binding trace IDs, and shipping logs that correlate with traces and metrics.","md",{"slug":1106,"type":1107,"breadcrumb":1108,"datePublished":1120,"dateModified":1121,"howto":1122,"faq":1140},"structured-json-logging-with-request-ids","long_tail",[1109,1112,1115,1117],{"label":1110,"path":1111},"Home","\u002F",{"label":1113,"path":1114},"Async, Background Tasks & Observability","\u002Fasync-background-tasks-observability\u002F",{"label":1116,"path":360},"Observability & Tracing",{"label":1118,"path":1119},"Structured JSON Logging with Request IDs","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002F","2026-02-22","2026-06-18",{"name":1123,"steps":1124},"Set up structured JSON logging with request IDs",[1125,1128,1131,1134,1137],{"name":1126,"text":1127},"Store the request ID in a contextvar","Set the correlation ID in a contextvar from tracing middleware so it is ambient per request.",{"name":1129,"text":1130},"Write a logging filter","Add a filter that reads the contextvar and injects request_id into every record.",{"name":1132,"text":1133},"Use a JSON formatter","Format records as JSON so they are machine-parseable by your log backend.",{"name":1135,"text":1136},"Include trace identifiers","Add the active trace and span IDs to records so logs link to traces.",{"name":1138,"text":1139},"Configure once at startup","Apply the handler, filter, and formatter when the application starts.",[1141,1144,1147],{"q":1142,"a":1143},"Why use JSON logs instead of plain text?","JSON logs are machine-parseable, so a log backend can index every field and let you filter by request_id, level, route, or trace_id precisely. Plain text forces fragile regex parsing and makes correlation across services painful. Structured logs are the substrate that ties logs to traces and metrics.",{"q":1145,"a":1146},"How does the request ID reach every log line without passing it around?","Store it in a contextvar set by the tracing middleware. A logging filter reads that contextvar for each record and injects request_id, so any log emitted anywhere in the request — including deep in service code — carries the ID automatically, with no parameter threading.",{"q":1148,"a":1149},"Should application logs and access logs share a format?","Yes, ideally. Emitting both as JSON with the same request_id field lets you join an access log line to the application logs for the same request. Configure Uvicorn's access logger with the same formatter, or disable it and log access yourself in middleware.",{"title":189,"description":1103},"9cHbrSlxmPxJTTLjaPJ1R_RnzedH4pnlzDlDH_RdTXQ",[1153,1153],null,1781809863644]