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 tem suporte nativo para proteger o dashboard /flare via Zitadel OIDC.
Existem dois modos de integração:
| Modo | Quando usar |
|---|---|
| Browser (PKCE) | Usuários acessam /flare pelo navegador — redirecionados para o login do Zitadel automaticamente |
| Bearer Token | Clientes de API enviam Authorization: Bearer <token> — sem redirecionamento |
Requer o extra
[auth]:pip install 'fastapi-flare[auth]'
Pré-requisitos
No console do Zitadel:
- Crie uma Web Application dentro de um projeto (tipo: PKCE)
- Anote o Domain — ex:
auth.mycompany.com - Anote o Client ID da aplicação
- Anote o Project ID (visível nas configurações gerais do projeto)
- Para modo browser: registre a URL de callback — ex:
https://myapp.com/flare/callback
Modo Browser (PKCE) — acesso pelo navegador
Quando zitadel_redirect_uri está configurado, abrir /flare no browser redireciona automaticamente para o Zitadel. Após o login, o Zitadel chama o callback, o token é salvo em cookie e o usuário é redirecionado de volta ao 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 variáveis de ambiente:
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=<hex-de-32-bytes> # python -c "import secrets; print(secrets.token_hex(32))"
O que acontece:
- Usuário abre
https://myapp.com/flareno browser fastapi-flaredetecta ausência de sessão → redireciona para/flare/auth/login/flare/auth/logingera PKCE challenge, salvacode_verifier+statena sessão, redireciona para o Zitadel- Usuário faz login na tela do Zitadel
- Zitadel redireciona para
/flare/callback?code=...&state=... fastapi-flarevalida o state, troca o code peloaccess_token, chama/oidc/v1/userinfo- Dados do usuário e timestamp de expiração salvos na sessão (cookie assinado
flare_session) - Usuário redirecionado para
/flare— acesso liberado ✅
Importante: registre exatamente
https://yourapp.com/flare/callbackcomo Redirect URI no app do Zitadel.
Rotas criadas automaticamente:
| Rota | O que faz |
|---|---|
GET /flare/auth/login |
Inicia o fluxo PKCE → redireciona ao Zitadel |
GET /flare/callback |
Recebe o code, troca por token, cria sessão |
GET /flare/auth/logout |
Limpa a sessão → redireciona ao login |
Modo API (Bearer Token) — sem zitadel_redirect_uri
Quando zitadel_redirect_uri não está definido, a proteção valida o header Authorization: Bearer <token>. Ideal para quando o frontend já gerencia o fluxo PKCE e injeta o token nas requisições.
setup(app, config=FlareConfig(
redis_url="redis://localhost:6379",
zitadel_domain="auth.mycompany.com",
zitadel_client_id="000000000000000001",
zitadel_project_id="000000000000000002",
# sem zitadel_redirect_uri → modo Bearer
))
Modo Manual — dependency customizada (avançado)
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,
))
Migração de projeto — aceitar tokens do projeto antigo
FLARE_ZITADEL_OLD_CLIENT_ID=old-client-id
FLARE_ZITADEL_OLD_PROJECT_ID=old-project-id
Tokens dos dois projetos são aceitos até você remover os campos _old_*.
Como funciona internamente
- A sessão é gerenciada pelo
SessionMiddlewaredo Starlette — cookieflare_sessionassinado com HMAC usandozitadel_session_secret. Todo o conteúdo (user, tokens, expiração) fica dentro do cookie — sem banco, sem Redis para gerenciar sessões. - O
code_verifierestatePKCE são armazenados na sessão (não em cookies separados), exatamente como recomendam as specs do PKCE. - Após o callback,
fastapi-flarechama/oidc/v1/userinfopara buscar os dados reais do usuário (email, nome, etc.) e os salva na sessão. - Sessões expiram automaticamente após 1 hora — o usuário é redirecionado para login sem interrupção.
- Use
clear_jwks_cache()para resetar o cache de JWKS em testes (modo Bearer).
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.2.tar.gz.
File metadata
- Download URL: fastapi_flare-0.1.2.tar.gz
- Upload date:
- Size: 55.5 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 |
38f3d5ac60cd64e7b09c7b48ae09f6460ae0b0758a645ed7147a8b95b9c4a563
|
|
| MD5 |
77ec54cae1bff9d858694431dcb034e2
|
|
| BLAKE2b-256 |
2d3617cde43d8278fc3d349ea2bb30cf05d8b6e47d40b3c0d62b5c528104bc0f
|
File details
Details for the file fastapi_flare-0.1.2-py3-none-any.whl.
File metadata
- Download URL: fastapi_flare-0.1.2-py3-none-any.whl
- Upload date:
- Size: 65.7 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 |
c9801d052fc2c5816fa7115417020fcaf5a61e5345779c9ae299a15d94c39ada
|
|
| MD5 |
690d44763169990b7c9629a636fd4883
|
|
| BLAKE2b-256 |
9419b98b9a8f90d6ae2c6da1a0e55f706a7e74fedbc6f1b85002cc71ae0bcc3d
|