Framework-agnostic metrics and structured log collection with hexagonal architecture
Project description
observabilipy
Start with observability built in.
Build microservices that come with metrics, logs, and a dashboard from day one. No Prometheus or Grafana required — but ready to integrate when your infrastructure catches up.
Why observabilipy?
The problem: You're building a service but your org doesn't have centralized observability yet. Or you're deploying to client environments where you don't control the infrastructure. You still need to understand what your service is doing — and you don't want to wait for the platform team.
The solution: Develop your observability features decoupled from the observability stack:
- Your code defines metrics, logs, and dashboards
- The infrastructure (Prometheus, Grafana, Loki) is optional and can come later
- Standard endpoints (
/metrics,/logs) work standalone and integrate seamlessly when infra arrives
This means you can ship observable services today. When central infrastructure catches up, just point scrapers at your existing endpoints — no code changes needed.
Quick Start
from contextlib import asynccontextmanager
from fastapi import FastAPI
from observabilipy import (
SQLiteLogStorage, SQLiteMetricsStorage,
EmbeddedRuntime, RetentionPolicy, info, counter
)
from observabilipy.adapters.frameworks.fastapi import create_observability_router
# Storage with automatic retention
log_storage = SQLiteLogStorage("app.db")
metrics_storage = SQLiteMetricsStorage("app.db")
runtime = EmbeddedRuntime(
log_storage=log_storage,
metrics_storage=metrics_storage,
log_retention=RetentionPolicy(max_age_seconds=86400), # 24 hours
)
@asynccontextmanager
async def lifespan(app):
await runtime.start()
yield
await runtime.stop()
app = FastAPI(lifespan=lifespan)
app.include_router(create_observability_router(log_storage, metrics_storage))
@app.get("/users/{user_id}")
async def get_user(user_id: int):
await log_storage.write(info("User requested", user_id=user_id))
await metrics_storage.write(counter("api_requests_total", endpoint="get_user"))
return {"user_id": user_id}
Run it and you get:
GET /logs— structured logs in NDJSONGET /metrics/prometheus— Prometheus text formatGET /metrics— metrics in NDJSON (for custom dashboards)
Use Cases
| Scenario | How observabilipy helps |
|---|---|
| Decoupled development | Build observability features without waiting for platform team to set up Prometheus/Grafana |
| Internal tools & microservices | Ship with observability included, no external dependencies required |
| Client/on-prem deployments | Works standalone in unknown environments, integrates when infrastructure exists |
| Prototypes & MVPs | Production-ready observability patterns from the start — no rework later |
Features
- Prometheus-compatible —
/metrics/prometheusendpoint, ready for scraping - Grafana Alloy/Loki compatible —
/logsendpoint in NDJSON format - Embedded dashboard — Live charts and log viewer (see example)
- Persistent storage — SQLite with WAL mode, logs survive restarts
- Automatic retention — Background cleanup with configurable policies
- Framework support — FastAPI, Django, ASGI, WSGI
Installation
pip install observabilipy[fastapi] # or [django]
Recording Metrics and Logs
from observabilipy import info, error, warn, counter, gauge, histogram
# Structured logs
await log_storage.write(info("User logged in", user_id=123, ip="10.0.0.1"))
await log_storage.write(error("Payment failed", order_id=456, reason="timeout"))
# Metrics
await metrics_storage.write(counter("requests_total", method="GET", status=200))
await metrics_storage.write(gauge("active_connections", value=42))
await metrics_storage.write(histogram("request_duration_seconds", value=0.125))
Context Managers
from observabilipy import timer, timed_log
# Auto-record timing to histogram
async with timer(metrics_storage, "request_duration_seconds", method="GET"):
response = await handle_request()
# Log entry/exit with elapsed time
async with timed_log(log_storage, "Processing order", order_id=123):
await process_order()
Python Logging Integration
import logging
from observabilipy import ObservabilipyHandler
logging.getLogger().addHandler(ObservabilipyHandler(log_storage))
# All logging calls now captured
logging.info("Starting up", extra={"version": "1.0.0"})
Storage Options
| Backend | Use Case |
|---|---|
SQLiteLogStorage / SQLiteMetricsStorage |
Recommended — persistent, survives restarts |
InMemoryLogStorage / InMemoryMetricsStorage |
Development and testing |
RingBufferLogStorage / RingBufferMetricsStorage |
Memory-constrained environments |
Embedded Dashboard
Build admin visibility into your service:
# Run the dashboard example
uvicorn examples.dashboard_example:app --reload
# Visit http://localhost:8000/
See dashboard_example.py for a complete implementation with live CPU/memory charts and log viewer.
Integration with Central Infrastructure
When your org sets up Prometheus/Grafana, no code changes needed:
# prometheus.yml
scrape_configs:
- job_name: 'my-service'
static_configs:
- targets: ['my-service:8000']
metrics_path: '/metrics/prometheus'
# grafana-alloy config
loki.source.api "my_service" {
http { listen_address = "0.0.0.0:3100" }
forward_to = [loki.write.default.receiver]
}
Examples
| Example | Description |
|---|---|
| dashboard_example.py | Embedded admin dashboard with live charts |
| fastapi_example.py | Basic FastAPI setup |
| sqlite_example.py | Persistent storage |
| logging_handler_example.py | Python logging integration |
| embedded_runtime_example.py | Background retention cleanup |
When to use observabilipy
| observabilipy | OpenTelemetry |
|---|---|
| No central infrastructure yet | Prometheus/Grafana/Jaeger already set up |
| Self-contained microservices | Distributed tracing across services |
| Simple, lightweight | Full CNCF observability stack |
| Works offline | Cloud-native environments |
They can coexist — use observabilipy for services not yet connected to central infrastructure.
License
MIT
Project details
Release history Release notifications | RSS feed
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 observabilipy-1.3.0.tar.gz.
File metadata
- Download URL: observabilipy-1.3.0.tar.gz
- Upload date:
- Size: 141.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36f2e3b6b6166c70550cba2cd4bc4a42d6875b28cfa86d1928fbe6bdfe785958
|
|
| MD5 |
2d4fc7e60d9305ae3847e8d2885f1ff5
|
|
| BLAKE2b-256 |
47116eacd92fcf1b60ff42ef6f111a3baf284525d4d8114a1fd48d32a4e34294
|
Provenance
The following attestation bundles were made for observabilipy-1.3.0.tar.gz:
Publisher:
release.yml on PhilHem/observabilipy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
observabilipy-1.3.0.tar.gz -
Subject digest:
36f2e3b6b6166c70550cba2cd4bc4a42d6875b28cfa86d1928fbe6bdfe785958 - Sigstore transparency entry: 813975400
- Sigstore integration time:
-
Permalink:
PhilHem/observabilipy@bdb0f1ea05e802f6401eafb55965afcd85573778 -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/PhilHem
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bdb0f1ea05e802f6401eafb55965afcd85573778 -
Trigger Event:
push
-
Statement type:
File details
Details for the file observabilipy-1.3.0-py3-none-any.whl.
File metadata
- Download URL: observabilipy-1.3.0-py3-none-any.whl
- Upload date:
- Size: 42.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1acc38af330d7083cbdf6b47cfabd656bb70667bdfd34bd665de098fa2c3aa2b
|
|
| MD5 |
cf2d68f473e2daae6e3c91964fa15dcd
|
|
| BLAKE2b-256 |
c8aaa92b0bd8abb2d48bad6445366231b5e96908f6ed2c1a0dc8d2006ecc54ae
|
Provenance
The following attestation bundles were made for observabilipy-1.3.0-py3-none-any.whl:
Publisher:
release.yml on PhilHem/observabilipy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
observabilipy-1.3.0-py3-none-any.whl -
Subject digest:
1acc38af330d7083cbdf6b47cfabd656bb70667bdfd34bd665de098fa2c3aa2b - Sigstore transparency entry: 813975402
- Sigstore integration time:
-
Permalink:
PhilHem/observabilipy@bdb0f1ea05e802f6401eafb55965afcd85573778 -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/PhilHem
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bdb0f1ea05e802f6401eafb55965afcd85573778 -
Trigger Event:
push
-
Statement type: