Skip to main content

Structured healthcheck endpoints with shallow and deep response tiers, concurrent check execution, and built-in support for PostgreSQL, Redis, SQLAlchemy, and FastAPI.

Project description

Vitals (Python)

Structured deep healthcheck endpoints for Python services. Register health checks, run them concurrently with timeouts, and expose them via FastAPI.

Install

pip install vitals

Install with optional dependencies for the checks and integrations you need:

pip install vitals[asyncpg]      # PostgreSQL via asyncpg
pip install vitals[redis]        # Redis via redis-py
pip install vitals[sqlalchemy]   # PostgreSQL via SQLAlchemy
pip install vitals[fastapi]      # FastAPI integration
pip install vitals[all]          # Everything

Quick Start

from vitals import HealthcheckRegistry, Status, CheckResult
from vitals.checks.postgres import AsyncpgPoolCheck
from vitals.checks.redis import RedisPoolCheck
from vitals.integrations.fastapi import create_healthcheck_route
from fastapi import FastAPI

registry = HealthcheckRegistry(default_timeout=5.0)

# Add checks using existing connection pools
registry.add("postgres", AsyncpgPoolCheck(pool))
registry.add("redis", RedisPoolCheck(redis_pool))

# Or add a custom check
@registry.check("api")
async def check_api() -> CheckResult:
    start = time.monotonic()
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://api.example.com/ping")
    latency = (time.monotonic() - start) * 1000
    return CheckResult(
        status=Status.HEALTHY if resp.is_success else Status.OUTAGE,
        latency_ms=latency,
        message="" if resp.is_success else f"HTTP {resp.status_code}",
    )

# Mount as a FastAPI route
app = FastAPI()
app.include_router(create_healthcheck_route(
    registry,
    token=os.environ.get("HEALTHCHECK_TOKEN"),
))

API

HealthcheckRegistry

registry = HealthcheckRegistry(default_timeout=5.0)

registry.add(name, check, *, timeout=None)

Register a named async check function.

async def my_check() -> CheckResult:
    return CheckResult(status=Status.HEALTHY, latency_ms=0.0, message="")

registry.add("service", my_check, timeout=3.0)

registry.check(name, *, timeout=None)

Decorator form of add.

@registry.check("service", timeout=3.0)
async def check_service() -> CheckResult:
    ...

await registry.run()

Execute all registered checks concurrently using asyncio.TaskGroup. Returns a HealthcheckResponse with the worst overall status.

response = await registry.run()
response.status       # Status.HEALTHY
response.to_dict()    # {'status': 'healthy', 'timestamp': '...', 'checks': {...}}
response.http_status_code  # 200

Status

from vitals import Status

Status.HEALTHY   # 2
Status.DEGRADED  # 1
Status.OUTAGE    # 0

Status.from_string("degraded")  # Status.DEGRADED
Status.HEALTHY.json_value       # "healthy"

Built-in Checks

PostgreSQL

from vitals.checks.postgres import AsyncpgCheck, AsyncpgPoolCheck, SQLAlchemyCheck

# Fresh asyncpg connection each time
registry.add("pg", AsyncpgCheck("postgresql://localhost:5432/mydb"))

# Existing asyncpg pool
registry.add("pg", AsyncpgPoolCheck(pool))

# SQLAlchemy AsyncEngine
registry.add("pg", SQLAlchemyCheck(engine))

Redis

from vitals.checks.redis import RedisCheck, RedisPoolCheck

# Fresh connection each time
registry.add("redis", RedisCheck("redis://localhost:6379"))

# Existing redis.asyncio connection pool
registry.add("redis", RedisPoolCheck(pool))

Sync Checks

Wrap synchronous functions using asyncio.to_thread:

from vitals import sync_check, Status, CheckResult

def check_disk() -> CheckResult:
    free = shutil.disk_usage("/").free
    return CheckResult(
        status=Status.HEALTHY if free > 1_000_000_000 else Status.DEGRADED,
        latency_ms=0.0,
        message=f"{free} bytes free",
    )

registry.add("disk", sync_check(check_disk))

FastAPI Integration

from vitals.integrations.fastapi import create_healthcheck_route

router = create_healthcheck_route(
    registry,
    token="my-secret-token",       # optional — omit to disable auth
    path="/healthcheck/deep",      # default
    query_param_name="token",      # default
    include_in_schema=False,       # default — hides from OpenAPI docs
)
app.include_router(router)

When a token is configured, requests must provide it via:

  • Query parameter: ?token=my-secret-token
  • Bearer header: Authorization: Bearer my-secret-token

Liveness Probes (/ping)

Never point a liveness probe at the healthcheck endpoint. The registry runs on every request, so a transient DB / Redis / downstream blip fails the probe and, after the orchestrator's failure threshold, restarts the container — turning a brief dependency hiccup into cascading restarts.

Use a separate registry-free liveness endpoint that only confirms the process is up. It never touches the registry, always returns HTTP 200, and responds with {"status": "ok", "timestamp": ..., **metadata}.

  • liveness probe → /ping (registry-free, always 200)
  • readiness / dependency checks → /healthcheck/deep (runs the registry)
from vitals.integrations.fastapi import create_healthcheck_route, create_liveness_route

app.include_router(create_liveness_route(path="/ping"))          # registry-free, no auth
app.include_router(create_healthcheck_route(registry))           # runs the registry

create_liveness_route takes path="/ping", optional metadata, and include_in_schema=False (defaults). It is public (no token).

Framework-agnostic handler:

from vitals import create_liveness_handler

ping = create_liveness_handler(metadata={"build": "stg-45d76e5"})
result = ping({})
# HandlerResult(status=200, body={"status": "ok", "timestamp": "...", "build": "stg-45d76e5"})

metadata follows the same reserved-key rules as the healthcheck handler (status, timestamp, checks, cachedAt are rejected).

Authentication

from vitals import verify_token, extract_token

# Timing-safe token comparison
verify_token("provided-token", "expected-token")  # bool

# Extract token from request data
token = extract_token(
    query_params={"token": "abc"},
    authorization_header="Bearer abc",
    query_param_name="token",
)

Response Format

{
  "status": "healthy",
  "timestamp": "2025-02-26T12:00:00.000Z",
  "checks": {
    "postgres": {
      "status": "healthy",
      "latencyMs": 4.2,
      "message": ""
    },
    "redis": {
      "status": "healthy",
      "latencyMs": 1.1,
      "message": ""
    }
  }
}
Overall Status HTTP Code
healthy 200
degraded 503
outage 503
Auth failure 403

Requirements

  • Python >= 3.11

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

firebreak_vitals-1.1.1.tar.gz (19.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

firebreak_vitals-1.1.1-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

Details for the file firebreak_vitals-1.1.1.tar.gz.

File metadata

  • Download URL: firebreak_vitals-1.1.1.tar.gz
  • Upload date:
  • Size: 19.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.0.1 CPython/3.12.8

File hashes

Hashes for firebreak_vitals-1.1.1.tar.gz
Algorithm Hash digest
SHA256 345c261c470242722da27e23f7b77c12d09dca41e739965b68b8538db4e8b905
MD5 8f339f11e2876693f2b3bc4f263adb77
BLAKE2b-256 c5fc345896dcaaf392022dbbf16a631066677cb4f54c848181379d097298f5ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for firebreak_vitals-1.1.1.tar.gz:

Publisher: publish-python.yml on firebreak-io/vitals

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file firebreak_vitals-1.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for firebreak_vitals-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 390ab57f80bb6f7cbefdd03ef99be4b5e89cdce3d7f11d297307fcb1f9aa5a54
MD5 43a714b99171391eb1e2a02277aa6692
BLAKE2b-256 789c845dac4c277fabbace7eeba67bcf2d52428a552d739edf83e654b96c7d8d

See more details on using hashes here.

Provenance

The following attestation bundles were made for firebreak_vitals-1.1.1-py3-none-any.whl:

Publisher: publish-python.yml on firebreak-io/vitals

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page