Lightweight self-hosted observability for FastAPI.
Project description
⚡ fastapi-flare
Lightweight self-hosted observability for FastAPI.
Backed by Redis Streams or SQLite — no SaaS, no overhead.
What is fastapi-flare?
fastapi-flare is a self-hosted error tracking library for FastAPI applications. It automatically captures HTTP and unhandled exceptions, stores them in Redis Streams, and exposes a beautiful dark-theme dashboard — all with a single line of code.
No external services. No configuration files. No noise.
Features
| 🚀 One-line setup | setup(app) and you're done |
| 🔍 Auto-capture | HTTP 4xx/5xx and unhandled Python exceptions |
| 🖥️ Admin dashboard | Built-in at /flare — dark theme, filters, pagination |
| 🗄️ Dual storage | Redis Streams (production) or SQLite (zero-infra) |
| 🔥 Fire-and-forget | Logging never blocks or affects your request handlers |
| ⚙️ Background worker | Async task drains queue to stream every 5 seconds |
| 🕒 Retention policies | Time-based (default 7 days) + count-based (10k entries) |
| 🔐 Auth-ready | Protect the dashboard with any FastAPI Depends() |
| 🌍 Env-configurable | All settings available as FLARE_* environment variables |
Installation
pip install fastapi-flare
All features (SQLite backend, Zitadel JWT auth) are included in the base install.
Requirements: Python 3.11+, FastAPI. Redis is only required when using the default
redisstorage backend.
Quick Start
Redis (default — production-ready, durable):
from fastapi import FastAPI
from fastapi_flare import setup
app = FastAPI()
setup(app, redis_url="redis://localhost:6379")
SQLite (zero-infra — no Redis required):
from fastapi import FastAPI
from fastapi_flare import FlareConfig, setup
app = FastAPI()
setup(app, config=FlareConfig(storage_backend="sqlite", sqlite_path="flare.db"))
Visit http://localhost:8000/flare to open the error dashboard.
Storage Backends
Redis (default)
Uses a Redis List as buffer queue and a Redis Stream as durable storage. Best for production deployments where Redis is already available.
setup(app, config=FlareConfig(
storage_backend="redis", # default
redis_url="redis://localhost:6379",
redis_password=None,
stream_key="flare:logs",
queue_key="flare:queue",
))
Docker:
docker run -d -p 6379:6379 redis:7
SQLite
Stores everything in a local .db file. No external services, no Docker, no configuration — ideal for local development, small deployments, or air-gapped environments.
setup(app, config=FlareConfig(
storage_backend="sqlite",
sqlite_path="flare.db", # path to the .db file
))
The SQLite backend uses WAL mode and indexed queries for efficient reads and writes.
Full Configuration
from fastapi_flare import setup, FlareConfig
setup(app, config=FlareConfig(
# --- Backend (choose one) ---
storage_backend="redis", # "redis" | "sqlite"
# Redis options (storage_backend="redis")
redis_url="redis://localhost:6379",
redis_password=None,
stream_key="flare:logs",
queue_key="flare:queue",
# SQLite options (storage_backend="sqlite")
# sqlite_path="flare.db",
# --- Shared ---
max_entries=10_000, # Count-based cap
retention_hours=168, # Time-based retention (7 days)
# Dashboard
dashboard_path="/flare",
dashboard_title="My App — Errors",
dashboard_auth_dependency=None, # e.g. Depends(verify_token)
# Worker
worker_interval_seconds=5,
worker_batch_size=100,
))
Environment Variables
All options can be set via FLARE_* env vars — no code changes needed:
FLARE_REDIS_URL=redis://myhost:6379
FLARE_RETENTION_HOURS=72
FLARE_DASHBOARD_PATH=/errors
FLARE_DASHBOARD_TITLE="Production Errors"
Dashboard
The built-in dashboard gives you full visibility into your application errors without leaving your infrastructure.
| Feature | Detail |
|---|---|
| URL | {dashboard_path} (default /flare) |
| Stats cards | Errors/Warnings in last 24h, total entries, latest error time |
| Filters | Level (ERROR / WARNING), event name, full-text search |
| Table | Timestamp, level badge, event, message, endpoint, HTTP status |
| Detail modal | Full message, error, stack trace, request metadata, context JSON |
| Auto-refresh | 30s polling toggle |
Redis Data Model
fastapi-flare uses two Redis structures:
| Key | Type | Purpose |
|---|---|---|
flare:queue |
List | Incoming buffer — LPUSH by handlers, RPOP by worker |
flare:logs |
Stream | Durable time-ordered storage — XADD / XREVRANGE |
Stream entries are automatically trimmed by two policies applied on every worker cycle:
- Count-based —
MAXLEN ~keeps at mostmax_entriesitems - Time-based —
XTRIM MINIDremoves entries older thanretention_hours
Log Entry Schema
Every captured error is stored as a structured FlareLogEntry:
class FlareLogEntry(BaseModel):
id: str # Redis Stream entry ID (millisecond-precise)
timestamp: datetime
level: Literal["ERROR", "WARNING"]
event: str # e.g. "http_exception", "unhandled_exception"
message: str
request_id: str | None # UUID from X-Request-ID header
endpoint: str | None
http_method: str | None
http_status: int | None
ip_address: str | None
duration_ms: int | None
error: str | None
stack_trace: str | None
context: dict | None # Additional structured data
Manual Logging
You can push custom log entries from anywhere in your application:
from fastapi_flare.queue import push_log
await push_log(
config,
level="ERROR",
event="payment_failed",
message="Stripe charge declined",
context={"order_id": "ord_123", "amount": 2500},
)
Protecting the Dashboard
Secure the dashboard using any FastAPI dependency:
from fastapi import HTTPException, Security
from fastapi.security import HTTPBearer
bearer = HTTPBearer()
def verify_token(token=Security(bearer)):
if token.credentials != "my-secret":
raise HTTPException(status_code=401, detail="Unauthorized")
setup(app, config=FlareConfig(
redis_url="redis://localhost:6379",
dashboard_auth_dependency=verify_token,
))
Zitadel Authentication
fastapi-flare has built-in support for protecting the /flare dashboard via Zitadel OIDC.
Two integration modes are available:
| Mode | When to use |
|---|---|
| Browser (PKCE) | Users access /flare from a browser — automatically redirected to the Zitadel login page |
| Bearer Token | API clients send Authorization: Bearer <token> — no redirect |
Requires the
[auth]extra:pip install 'fastapi-flare[auth]'
Prerequisites
In the Zitadel console:
- Create a Web Application inside a project (type: PKCE / User Agent)
- Note the Domain — e.g.
auth.mycompany.com - Note the Client ID of the application
- Note the Project ID (visible in the project's general settings)
- For browser mode: register the callback URL — e.g.
https://myapp.com/flare/callback
Browser Mode (PKCE) — access from a browser
When zitadel_redirect_uri is configured, opening /flare in a browser automatically redirects to Zitadel. After login, Zitadel calls the callback, the session is created in a signed cookie, and the user is redirected back to the dashboard.
setup(app, config=FlareConfig(
redis_url="redis://localhost:6379",
zitadel_domain="auth.mycompany.com",
zitadel_client_id="000000000000000001",
zitadel_project_id="000000000000000002",
zitadel_redirect_uri="https://myapp.com/flare/callback",
))
Via environment variables:
FLARE_ZITADEL_DOMAIN=auth.mycompany.com
FLARE_ZITADEL_CLIENT_ID=000000000000000001
FLARE_ZITADEL_PROJECT_ID=000000000000000002
FLARE_ZITADEL_REDIRECT_URI=https://myapp.com/flare/callback
FLARE_ZITADEL_SESSION_SECRET=<32-byte-hex> # python -c "import secrets; print(secrets.token_hex(32))"
Flow:
- User opens
https://myapp.com/flarein a browser fastapi-flaredetects no session → redirects to/flare/auth/login/flare/auth/logingenerates a PKCE challenge, storescode_verifier+statein the session, redirects to Zitadel- User logs in on the Zitadel page
- Zitadel redirects to the configured callback URL with
?code=...&state=... fastapi-flarevalidates the state, exchanges the code for anaccess_token, calls/oidc/v1/userinfo- User data and expiry timestamp are saved in the session (signed
flare_sessioncookie) - User is redirected to
/flare— access granted ✅
Important: register exactly
https://yourapp.com/<callback-path>as the Redirect URI in the Zitadel app. The callback path is extracted automatically fromzitadel_redirect_uri— it does not need to be under/flare.
Routes created automatically:
| Route | Purpose |
|---|---|
GET /flare/auth/login |
Starts the PKCE flow → redirects to Zitadel |
GET <callback-path> |
Receives the code, exchanges it for a token, creates the session |
GET /flare/auth/logout |
Clears the session → redirects to login |
API Mode (Bearer Token) — no zitadel_redirect_uri
When zitadel_redirect_uri is not set, the dashboard validates the Authorization: Bearer <token> header. Ideal when the frontend already manages the PKCE flow and injects the token into requests.
setup(app, config=FlareConfig(
redis_url="redis://localhost:6379",
zitadel_domain="auth.mycompany.com",
zitadel_client_id="000000000000000001",
zitadel_project_id="000000000000000002",
# no zitadel_redirect_uri → Bearer mode
))
Manual Mode — custom dependency (advanced)
from fastapi_flare import setup, FlareConfig
from fastapi_flare.zitadel import make_zitadel_dependency
dep = make_zitadel_dependency(
domain="auth.mycompany.com",
client_id="000000000000000001",
project_id="000000000000000002",
)
setup(app, config=FlareConfig(
redis_url="redis://localhost:6379",
dashboard_auth_dependency=dep,
))
Project migration — accepting tokens from the old project
FLARE_ZITADEL_OLD_CLIENT_ID=old-client-id
FLARE_ZITADEL_OLD_PROJECT_ID=old-project-id
Tokens from both projects are accepted until you remove the _old_* fields.
How it works internally
- The session is managed by Starlette's
SessionMiddleware— aflare_sessioncookie signed with HMAC usingzitadel_session_secret. All content (user, token, expiry) lives inside the cookie — no database or Redis required for session management. - The PKCE
code_verifierandstateare stored in the session (not in separate cookies), following the PKCE spec recommendations. - After the callback,
fastapi-flarecalls/oidc/v1/userinfoto fetch the real user data (email, name, etc.) and saves it in the session. - Sessions expire automatically after 1 hour — the user is transparently redirected to login.
- Use
clear_jwks_cache()to reset the JWKS cache in tests (Bearer mode).
Running the Example
SQLite mode (no dependencies):
poetry run uvicorn examples.example:app --reload --port 8000
Redis mode:
# 1. Copy and configure environment
cp .env.example .env
# 2. Start Redis (Docker)
docker run -d -p 6379:6379 redis:7
# 3. Switch the example to Redis and run
# In examples/example.py, change:
# FlareConfig(storage_backend="redis") # and set FLARE_REDIS_URL in .env
poetry run uvicorn examples.example:app --reload --port 8000
Test routes:
| Route | Behavior |
|---|---|
GET / |
Returns 200 OK |
GET /boom |
Triggers RuntimeError → captured as ERROR |
GET /items/999 |
Triggers HTTPException 404 → captured as WARNING |
GET /flare |
Opens the error dashboard |
Comparison
| Project | What it does |
|---|---|
sentry-sdk |
Full error tracking SaaS — more features, external dependency |
fastapi-analytics |
Endpoint analytics / performance — not error-focused |
fastapi-middleware-logger |
HTTP logging only, no storage or dashboard |
api-watch |
Real-time monitoring, Flask/FastAPI |
fastapi-flare |
Self-hosted, zero-config error visibility — no external services |
fastapi-flare is for teams that want local, observable, production-ready error tracking without the overhead of a full observability platform.
Why not Sentry?
Sentry is a great product — but it comes with trade-offs that not every team wants to accept.
| fastapi-flare | Sentry | |
|---|---|---|
| Hosting | Self-hosted, your infra | External SaaS |
| Account required | No | Yes |
| Infrastructure | Redis only | Kafka, ClickHouse, Postgres, … |
| Cost | Zero | Free tier → paid plans |
| Privacy | Data never leaves your server | Data sent to third-party |
| Setup | One setup(app) call |
SDK + DSN + account config |
| Customization | Full source access | Configuration only |
fastapi-flare is the right choice when you need fast, private, zero-dependency error visibility — especially in self-hosted, air-gapped, or cost-sensitive environments.
For large-scale teams who need release tracking, performance monitoring, and team workflows, Sentry remains the better fit.
License
MIT © Gabriel
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_flare-0.1.4.tar.gz.
File metadata
- Download URL: fastapi_flare-0.1.4.tar.gz
- Upload date:
- Size: 55.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.11.9 Windows/10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f72394b326e785a615b1d40744f82b620ec9f23527a6aa06b56aaaadd93d8489
|
|
| MD5 |
ececa1290ce8306c4f65adea9f946373
|
|
| BLAKE2b-256 |
cdfb36efbdf17a031ac9de8a9070e8bd6b29360172c2b2978e4251b9b199c876
|
File details
Details for the file fastapi_flare-0.1.4-py3-none-any.whl.
File metadata
- Download URL: fastapi_flare-0.1.4-py3-none-any.whl
- Upload date:
- Size: 66.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.11.9 Windows/10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84920ddfcbd279ed56c97b7dd262390611788b540989a66d76a4471afd8a626d
|
|
| MD5 |
d911cb32bc8a25d108b35836b6075e0e
|
|
| BLAKE2b-256 |
822563790eca8da69f1f092a2ea4340e6983bff7f4034dfc772bf9b789256d5b
|