[{"data":1,"prerenderedAt":1061},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002F":1059},[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":177,"body":312,"description":1028,"extension":1029,"meta":1030,"navigation":511,"path":178,"seo":1057,"stem":179,"__hash__":1058},"content\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Findex.md",{"type":313,"value":314,"toc":1019},"minimark",[315,319,323,342,476,481,484,609,613,616,771,777,780,783,837,841,844,848,851,940,944,982,986,1015],[316,317,177],"h1",{"id":318},"observability-and-tracing-in-fastapi",[320,321,322],"p",{},"Observability is the ability to answer questions about a running system from the outside, using three correlated signals — structured logs, metrics, and distributed traces — all tied together by a shared request identifier.",[320,324,325,326,331,332,336,337,341],{},"This topic is part of ",[327,328,330],"a",{"href":329},"\u002Fasync-background-tasks-observability\u002F","Async, Background Tasks and Observability",". It builds directly on the correlation ID assigned in ",[327,333,335],{"href":334},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F","middleware"," and the consistent ",[327,338,340],{"href":339},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002F","error envelope",", turning per-request context into a queryable production picture.",[343,344,345,472],"figure",{},[346,347,355,356,355,360,355,364,355,373,355,380,355,385,355,393,355,399,355,402,355,406,355,409,355,413,355,418,355,423,355,430,355,435,355,438,355,441,355,444,355,447,355,454,355,458,355,462,355,465,355,469],"svg",{"viewBox":348,"role":349,"ariaLabelledBy":350,"xmlns":353,"style":354},"0 0 760 230","img",[351,352],"obs-title","obs-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;font-family:Inter,system-ui,sans-serif","\n  ",[357,358,359],"title",{"id":351},"The three observability signals flowing to a collector",[361,362,363],"desc",{"id":352},"A FastAPI request emits logs, metrics, and traces, all carrying a shared correlation ID, which flow to an OpenTelemetry collector and on to their backends for querying and alerting.",[365,366],"rect",{"x":367,"y":368,"width":369,"height":370,"rx":371,"style":372},"30","92","160","58","9","fill:#E0F2F1;stroke:#009688;stroke-width:1.8px",[374,375,379],"text",{"x":376,"y":377,"style":378},"110","116","fill:#00695C;font-size:12.5px;font-weight:700;text-anchor:middle","FastAPI request",[374,381,384],{"x":376,"y":382,"style":383},"134","fill:#6B7280;font-size:10px;text-anchor:middle","correlation id",[365,386],{"x":387,"y":388,"width":389,"height":390,"rx":391,"style":392},"300","28","150","40","8","fill:#FFFFFF;stroke:#4DB6AC;stroke-width:1.5px",[374,394,398],{"x":395,"y":396,"style":397},"375","53","fill:#00695C;font-size:11.5px;text-anchor:middle","Logs",[365,400],{"x":387,"y":401,"width":389,"height":390,"rx":391,"style":392},"100",[374,403,405],{"x":395,"y":404,"style":397},"125","Metrics",[365,407],{"x":387,"y":408,"width":389,"height":390,"rx":391,"style":392},"172",[374,410,412],{"x":395,"y":411,"style":397},"197","Traces",[365,414],{"x":415,"y":401,"width":416,"height":390,"rx":391,"style":417},"540","190","fill:#E0F2F1;stroke:#009688;stroke-width:1.6px",[374,419,422],{"x":420,"y":404,"style":421},"635","fill:#00695C;font-size:11.5px;font-weight:600;text-anchor:middle","Collector → backends",[424,425],"line",{"x1":416,"y1":426,"x2":427,"y2":428,"style":429},"112","296","50","stroke:#00796B;stroke-width:1.5px",[431,432],"polygon",{"points":433,"style":434},"292,46 298,48 294,55","fill:#00796B",[424,436],{"x1":416,"y1":437,"x2":427,"y2":437,"style":429},"120",[431,439],{"points":440,"style":434},"296,116 304,120 296,124",[424,442],{"x1":416,"y1":443,"x2":427,"y2":416,"style":429},"128",[431,445],{"points":446,"style":434},"292,185 298,190 290,193",[424,448],{"x1":449,"y1":450,"x2":451,"y2":452,"style":453},"450","48","600","98","stroke:#4DB6AC;stroke-width:1.4px",[431,455],{"points":456,"style":457},"596,94 602,98 593,101","fill:#4DB6AC",[424,459],{"x1":449,"y1":437,"x2":460,"y2":437,"style":461},"538","stroke:#4DB6AC;stroke-width:1.5px",[431,463],{"points":464,"style":457},"538,116 546,120 538,124",[424,466],{"x1":449,"y1":467,"x2":451,"y2":468,"style":453},"192","142",[431,470],{"points":471,"style":457},"596,146 602,142 593,139",[473,474,475],"figcaption",{},"One request emits all three signals carrying the same correlation ID, so a collector can route them to their backends and you can pivot between them.",[477,478,480],"h2",{"id":479},"core-mechanics-structured-logs-with-context","Core Mechanics: Structured Logs with Context",[320,482,483],{},"The base layer is structured logging keyed by the correlation ID. JSON logs that always carry the request ID let you reconstruct a single request's story with one filter, and they are the substrate the other two signals link back to.",[485,486,491],"pre",{"className":487,"code":488,"language":489,"meta":490,"style":490},"language-python shiki shiki-themes github-light","import logging\n\nfrom app.tracing import request_id_ctx   # contextvar set by tracing middleware\n\n\nclass ContextFilter(logging.Filter):\n    def filter(self, record: logging.LogRecord) -> bool:\n        record.request_id = request_id_ctx.get()   # Always present on every record.\n        return True\n","python","",[492,493,494,506,513,531,536,541,566,585,600],"code",{"__ignoreMap":490},[495,496,498,502],"span",{"class":424,"line":497},1,[495,499,501],{"class":500},"sD7c4","import",[495,503,505],{"class":504},"sgsFI"," logging\n",[495,507,509],{"class":424,"line":508},2,[495,510,512],{"emptyLinePlaceholder":511},true,"\n",[495,514,516,519,522,524,527],{"class":424,"line":515},3,[495,517,518],{"class":500},"from",[495,520,521],{"class":504}," app.tracing ",[495,523,501],{"class":500},[495,525,526],{"class":504}," request_id_ctx   ",[495,528,530],{"class":529},"sAwPA","# contextvar set by tracing middleware\n",[495,532,534],{"class":424,"line":533},4,[495,535,512],{"emptyLinePlaceholder":511},[495,537,539],{"class":424,"line":538},5,[495,540,512],{"emptyLinePlaceholder":511},[495,542,544,547,551,554,557,560,563],{"class":424,"line":543},6,[495,545,546],{"class":500},"class",[495,548,550],{"class":549},"s7eDp"," ContextFilter",[495,552,553],{"class":504},"(",[495,555,556],{"class":549},"logging",[495,558,559],{"class":504},".",[495,561,562],{"class":549},"Filter",[495,564,565],{"class":504},"):\n",[495,567,569,572,576,579,582],{"class":424,"line":568},7,[495,570,571],{"class":500},"    def",[495,573,575],{"class":574},"sYu0t"," filter",[495,577,578],{"class":504},"(self, record: logging.LogRecord) -> ",[495,580,581],{"class":574},"bool",[495,583,584],{"class":504},":\n",[495,586,588,591,594,597],{"class":424,"line":587},8,[495,589,590],{"class":504},"        record.request_id ",[495,592,593],{"class":500},"=",[495,595,596],{"class":504}," request_id_ctx.get()   ",[495,598,599],{"class":529},"# Always present on every record.\n",[495,601,603,606],{"class":424,"line":602},9,[495,604,605],{"class":500},"        return",[495,607,608],{"class":574}," True\n",[477,610,612],{"id":611},"production-implementation-opentelemetry-tracing","Production Implementation: OpenTelemetry Tracing",[320,614,615],{},"OpenTelemetry auto-instruments FastAPI, the database, and HTTP clients, producing spans that form a trace of each request across services. You add manual spans only around the business operations you want timed.",[485,617,619],{"className":487,"code":618,"language":489,"meta":490,"style":490},"from opentelemetry import trace\nfrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\n\ntracer = trace.get_tracer(\"orders\")\n\n\ndef instrument(app) -> None:\n    FastAPIInstrumentor.instrument_app(app)   # Auto spans for every request.\n\n\nasync def place_order(order: Order) -> None:\n    # A manual span times a business operation inside the auto-generated request span.\n    with tracer.start_as_current_span(\"place_order\", attributes={\"order.id\": order.id}):\n        await persist(order)\n",[492,620,621,633,645,649,666,670,674,690,698,702,707,726,732,762],{"__ignoreMap":490},[495,622,623,625,628,630],{"class":424,"line":497},[495,624,518],{"class":500},[495,626,627],{"class":504}," opentelemetry ",[495,629,501],{"class":500},[495,631,632],{"class":504}," trace\n",[495,634,635,637,640,642],{"class":424,"line":508},[495,636,518],{"class":500},[495,638,639],{"class":504}," opentelemetry.instrumentation.fastapi ",[495,641,501],{"class":500},[495,643,644],{"class":504}," FastAPIInstrumentor\n",[495,646,647],{"class":424,"line":515},[495,648,512],{"emptyLinePlaceholder":511},[495,650,651,654,656,659,663],{"class":424,"line":533},[495,652,653],{"class":504},"tracer ",[495,655,593],{"class":500},[495,657,658],{"class":504}," trace.get_tracer(",[495,660,662],{"class":661},"sYBdl","\"orders\"",[495,664,665],{"class":504},")\n",[495,667,668],{"class":424,"line":538},[495,669,512],{"emptyLinePlaceholder":511},[495,671,672],{"class":424,"line":543},[495,673,512],{"emptyLinePlaceholder":511},[495,675,676,679,682,685,688],{"class":424,"line":568},[495,677,678],{"class":500},"def",[495,680,681],{"class":549}," instrument",[495,683,684],{"class":504},"(app) -> ",[495,686,687],{"class":574},"None",[495,689,584],{"class":504},[495,691,692,695],{"class":424,"line":587},[495,693,694],{"class":504},"    FastAPIInstrumentor.instrument_app(app)   ",[495,696,697],{"class":529},"# Auto spans for every request.\n",[495,699,700],{"class":424,"line":602},[495,701,512],{"emptyLinePlaceholder":511},[495,703,705],{"class":424,"line":704},10,[495,706,512],{"emptyLinePlaceholder":511},[495,708,710,713,716,719,722,724],{"class":424,"line":709},11,[495,711,712],{"class":500},"async",[495,714,715],{"class":500}," def",[495,717,718],{"class":549}," place_order",[495,720,721],{"class":504},"(order: Order) -> ",[495,723,687],{"class":574},[495,725,584],{"class":504},[495,727,729],{"class":424,"line":728},12,[495,730,731],{"class":529},"    # A manual span times a business operation inside the auto-generated request span.\n",[495,733,735,738,741,744,747,751,753,756,759],{"class":424,"line":734},13,[495,736,737],{"class":500},"    with",[495,739,740],{"class":504}," tracer.start_as_current_span(",[495,742,743],{"class":661},"\"place_order\"",[495,745,746],{"class":504},", ",[495,748,750],{"class":749},"sqxcx","attributes",[495,752,593],{"class":500},[495,754,755],{"class":504},"{",[495,757,758],{"class":661},"\"order.id\"",[495,760,761],{"class":504},": order.id}):\n",[495,763,765,768],{"class":424,"line":764},14,[495,766,767],{"class":500},"        await",[495,769,770],{"class":504}," persist(order)\n",[320,772,773,774,559],{},"The full instrumentation walk-through, including exporting to a collector, is in ",[327,775,183],{"href":776},"\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002F",[477,778,405],{"id":779},"metrics",[320,781,782],{},"Expose request rate, error rate, and latency per route, plus saturation signals such as pool usage, so you can both alert on user-facing symptoms and see resource limits approaching.",[485,784,786],{"className":487,"code":785,"language":489,"meta":490,"style":490},"from prometheus_client import Histogram\n\n# Latency distribution per route — the basis for percentile SLOs.\nREQUEST_LATENCY = Histogram(\"http_request_seconds\", \"Request latency\", [\"route\"])\n",[492,787,788,800,804,809],{"__ignoreMap":490},[495,789,790,792,795,797],{"class":424,"line":497},[495,791,518],{"class":500},[495,793,794],{"class":504}," prometheus_client ",[495,796,501],{"class":500},[495,798,799],{"class":504}," Histogram\n",[495,801,802],{"class":424,"line":508},[495,803,512],{"emptyLinePlaceholder":511},[495,805,806],{"class":424,"line":515},[495,807,808],{"class":529},"# Latency distribution per route — the basis for percentile SLOs.\n",[495,810,811,814,817,820,823,825,828,831,834],{"class":424,"line":533},[495,812,813],{"class":574},"REQUEST_LATENCY",[495,815,816],{"class":500}," =",[495,818,819],{"class":504}," Histogram(",[495,821,822],{"class":661},"\"http_request_seconds\"",[495,824,746],{"class":504},[495,826,827],{"class":661},"\"Request latency\"",[495,829,830],{"class":504},", [",[495,832,833],{"class":661},"\"route\"",[495,835,836],{"class":504},"])\n",[477,838,840],{"id":839},"async-and-performance-notes","Async and Performance Notes",[320,842,843],{},"Telemetry must be cheap and non-blocking, because it runs on every request. Export spans and metrics through a batching exporter that buffers and flushes in the background rather than writing synchronously per request, and sample traces under heavy load so instrumentation never becomes the bottleneck it is meant to observe.",[477,845,847],{"id":846},"testing-strategy","Testing Strategy",[320,849,850],{},"Assert that the correlation ID propagates into logs and that key spans are emitted:",[485,852,854],{"className":487,"code":853,"language":489,"meta":490,"style":490},"def test_request_id_in_logs(client, caplog):\n    client.get(\"\u002Fhealth\", headers={\"x-request-id\": \"trace-9\"})\n    assert any(getattr(r, \"request_id\", \"\") == \"trace-9\" for r in caplog.records)\n",[492,855,856,866,895],{"__ignoreMap":490},[495,857,858,860,863],{"class":424,"line":497},[495,859,678],{"class":500},[495,861,862],{"class":549}," test_request_id_in_logs",[495,864,865],{"class":504},"(client, caplog):\n",[495,867,868,871,874,876,879,881,883,886,889,892],{"class":424,"line":508},[495,869,870],{"class":504},"    client.get(",[495,872,873],{"class":661},"\"\u002Fhealth\"",[495,875,746],{"class":504},[495,877,878],{"class":749},"headers",[495,880,593],{"class":500},[495,882,755],{"class":504},[495,884,885],{"class":661},"\"x-request-id\"",[495,887,888],{"class":504},": ",[495,890,891],{"class":661},"\"trace-9\"",[495,893,894],{"class":504},"})\n",[495,896,897,900,903,905,908,911,914,916,919,922,925,928,931,934,937],{"class":424,"line":515},[495,898,899],{"class":500},"    assert",[495,901,902],{"class":574}," any",[495,904,553],{"class":504},[495,906,907],{"class":574},"getattr",[495,909,910],{"class":504},"(r, ",[495,912,913],{"class":661},"\"request_id\"",[495,915,746],{"class":504},[495,917,918],{"class":661},"\"\"",[495,920,921],{"class":504},") ",[495,923,924],{"class":500},"==",[495,926,927],{"class":661}," \"trace-9\"",[495,929,930],{"class":500}," for",[495,932,933],{"class":504}," r ",[495,935,936],{"class":500},"in",[495,938,939],{"class":504}," caplog.records)\n",[477,941,943],{"id":942},"failure-modes-and-debugging","Failure Modes and Debugging",[945,946,947,955,961,971],"ul",{},[948,949,950,954],"li",{},[951,952,953],"strong",{},"Unstructured logs."," Free-text logs cannot be filtered by request; emit JSON with the ID.",[948,956,957,960],{},[951,958,959],{},"Synchronous exporters."," Exporting per request on the hot path adds latency; batch and sample.",[948,962,963,966,967,559],{},[951,964,965],{},"Context loss in tasks."," Background jobs lose the trace unless you propagate it, per ",[327,968,970],{"href":969},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002F","Background Task Processing",[948,972,973,976,977,981],{},[951,974,975],{},"Missing saturation metrics."," Latency alone hides an exhausting pool; measure ",[327,978,980],{"href":979},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F","database pool"," usage too.",[477,983,985],{"id":984},"related-reading","Related Reading",[945,987,988,996,1003],{},[948,989,990,993,994,559],{},[951,991,992],{},"Up to the section:"," ",[327,995,330],{"href":329},[948,997,998,993,1001,559],{},[951,999,1000],{},"Hands-on guide:",[327,1002,183],{"href":776},[948,1004,1005,993,1008,1011,1012,559],{},[951,1006,1007],{},"Composes with:",[327,1009,287],{"href":1010},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F"," and ",[327,1013,1014],{"href":339},"Error Handling and Global Exceptions",[1016,1017,1018],"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 .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}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);}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":490,"searchDepth":508,"depth":508,"links":1020},[1021,1022,1023,1024,1025,1026,1027],{"id":479,"depth":508,"text":480},{"id":611,"depth":508,"text":612},{"id":779,"depth":508,"text":405},{"id":839,"depth":508,"text":840},{"id":846,"depth":508,"text":847},{"id":942,"depth":508,"text":943},{"id":984,"depth":508,"text":985},"Instrument FastAPI for production: structured logging with correlation IDs, OpenTelemetry distributed tracing, Prometheus metrics, and tying logs, traces, and metrics together.","md",{"slug":1031,"type":1032,"breadcrumb":1033,"datePublished":1042,"dateModified":1043,"faq":1044},"observability-and-tracing","cluster",[1034,1037,1039],{"label":1035,"path":1036},"Home","\u002F",{"label":1038,"path":329},"Async, Background Tasks & Observability",{"label":1040,"path":1041},"Observability & Tracing","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002F","2026-02-13","2026-06-18",[1045,1048,1051,1054],{"q":1046,"a":1047},"What are the three signals of observability and how do they differ?","Logs are discrete events with context, metrics are aggregated numeric time series, and traces show the path and timing of a single request across services. Logs tell you what happened, metrics tell you how much and how often, and traces tell you where the time went. Production systems need all three, correlated by a shared request identifier.",{"q":1049,"a":1050},"Do I have to instrument FastAPI manually for OpenTelemetry?","Mostly no. The OpenTelemetry FastAPI instrumentation auto-creates spans for requests, and the database and HTTP client instrumentations add spans for those calls. You add manual spans only around custom business operations you want timed. The auto-instrumentation gives you a usable trace graph with little code.",{"q":1052,"a":1053},"How do I connect a log line to a trace?","Inject the active trace and span IDs into your structured log records, alongside the correlation ID. With all three present, a log line links directly to its trace in your backend, so you can jump from an error log to the full request timeline that produced it.",{"q":1055,"a":1056},"What should I measure with metrics in a FastAPI app?","At minimum, request rate, error rate, and latency distribution per route — the signals that define your service level objectives — plus saturation metrics such as database pool usage and queue depth. These tell you both how users experience the service and how close you are to a resource limit.",{"title":177,"description":1028},"1bUPuTCj8ovzTcf2Den5ZMYpvBOI0SmP6jH7a6qysNQ",[1060,1060],null,1781809863387]