Beginner

Add Structured Logging to FastAPI in 10 Minutes

Set up structured JSON logging in a FastAPI app and ship logs to LogFlow with automatic request context, error tracking, and custom metadata.

LogFlow TeamJuly 14, 202610 minPythonFastAPI

FastAPI's default logging is bare — uvicorn prints access logs to stdout in plain text, and your application code either uses print() or Python's built-in logging module with no structure. Neither gives you searchable, filterable logs you can alert on.

This tutorial sets up structured JSON logging in a FastAPI app and ships everything to LogFlow. By the end you'll have:

  • JSON-formatted logs with request context (method, path, status, latency)
  • Automatic error capture with stack traces
  • Custom metadata on business-critical events
  • All logs searchable in LogFlow within seconds of emission

Prerequisites

  • Python 3.9+
  • A LogFlow account (sign up free)
  • Your LogFlow API key (Settings → API Keys in the dashboard)

Step 1 — Install dependencies

pip install fastapi uvicorn structlog httpx

We use structlog for structured logging. It outputs JSON by default and integrates cleanly with Python's standard logging module.

Step 2 — Configure structlog

Create a logging_config.py file:

import structlog
import logging
import sys

def setup_logging():
    structlog.configure(
        processors=[
            structlog.contextvars.merge_contextvars,
            structlog.processors.add_log_level,
            structlog.processors.StackInfoRenderer(),
            structlog.dev.set_exc_info,
            structlog.processors.TimeStamper(fmt="iso"),
            structlog.processors.JSONRenderer(),
        ],
        wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
        context_class=dict,
        logger_factory=structlog.PrintLoggerFactory(),
        cache_logger_on_first_use=True,
    )

This gives you JSON output like:

{"event": "User signed up", "user_id": 42, "plan": "starter", "level": "info", "timestamp": "2026-07-14T09:23:15Z"}

Step 3 — Add request logging middleware

Create middleware that automatically logs every request with context:

import time
import structlog
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

logger = structlog.get_logger()

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start = time.perf_counter()

        try:
            response = await call_next(request)
            duration_ms = round((time.perf_counter() - start) * 1000)

            logger.info(
                "request_completed",
                method=request.method,
                path=request.url.path,
                status=response.status_code,
                duration_ms=duration_ms,
                client=request.client.host if request.client else None,
            )
            return response

        except Exception as exc:
            duration_ms = round((time.perf_counter() - start) * 1000)
            logger.error(
                "request_failed",
                method=request.method,
                path=request.url.path,
                duration_ms=duration_ms,
                error=str(exc),
                exc_info=True,
            )
            raise

Step 4 — Wire it into your FastAPI app

from fastapi import FastAPI
from logging_config import setup_logging
from middleware import LoggingMiddleware
import structlog

setup_logging()
logger = structlog.get_logger()

app = FastAPI()
app.add_middleware(LoggingMiddleware)

@app.get("/")
async def root():
    return {"status": "ok"}

@app.post("/orders")
async def create_order(order: dict):
    logger.info("order_created", order_id=order.get("id"), total=order.get("total"))
    return {"created": True}

Step 5 — Ship logs to LogFlow

The simplest way is to use LogFlow's REST API. Add a custom structlog processor that sends each log entry:

import httpx

LOGFLOW_API_KEY = "lf_your_api_key_here"
LOGFLOW_URL = "https://api.getlogflow.com/v1/logs"

client = httpx.AsyncClient()

async def send_to_logflow(log_entry: dict):
    level_map = {"info": "info", "warning": "warn", "error": "error", "critical": "fatal"}

    payload = [{
        "level": level_map.get(log_entry.get("level", "info"), "info"),
        "message": log_entry.get("event", ""),
        "service": "fastapi-app",
        "attributes": {
            k: str(v) for k, v in log_entry.items()
            if k not in ("event", "level", "timestamp")
        },
    }]

    try:
        await client.post(
            LOGFLOW_URL,
            json=payload,
            headers={"Authorization": f"Bearer {LOGFLOW_API_KEY}"},
            timeout=5.0,
        )
    except Exception:
        pass  # don't let logging failures crash the app

For production, batch logs and flush periodically instead of sending one at a time. Or use a log forwarder like Fluent Bit to read stdout and forward to LogFlow — see the Docker logging tutorial for that approach.

Step 6 — Add business context

The real value of structured logging is attaching business context to events:

@app.post("/payments")
async def process_payment(payment: dict):
    logger.info(
        "payment_initiated",
        user_id=payment["user_id"],
        amount=payment["amount"],
        currency=payment["currency"],
        provider="stripe",
    )

    try:
        result = await charge(payment)
        logger.info("payment_succeeded", charge_id=result["id"], user_id=payment["user_id"])
        return result
    except PaymentError as e:
        logger.error(
            "payment_failed",
            user_id=payment["user_id"],
            error_code=e.code,
            amount=payment["amount"],
        )
        raise

Now in LogFlow you can search level:error AND service:fastapi-app AND payment_failed and instantly see which users were affected, how much money was involved, and which error codes are most common.

Step 7 — Set up an alert

In the LogFlow dashboard, go to Alerts → Create Alert and set:

  • Condition: Error rate above 5%
  • Service filter: fastapi-app
  • Channel: Slack, Telegram, or email
  • Cooldown: 15 minutes

You'll get notified before your users start complaining.

What you've built

Your FastAPI app now has:

  • ✓ Structured JSON logs on every request (method, path, status, latency)
  • ✓ Automatic error capture with exception details
  • ✓ Business context on critical events (payments, orders, user actions)
  • ✓ Centralized log search and alerting in LogFlow

The entire setup is under 100 lines of code and adds negligible overhead to request processing.

Next steps

Start monitoring your logs today

Free plan available. No credit card required. Up and running in 2 minutes.

Get started free