Skip to main content

Zero-config observability SDK for Python services — auto-instruments FastAPI with traces, metrics, and logs via OpenTelemetry

Project description

zerotel

PyPI Python License: MIT CI

Zero-config observability SDK for Python services.

Add one line. Get distributed traces, Prometheus metrics, and structured JSON logs — all correlated by trace_id and ready to query in Grafana.

from fastapi import FastAPI
from zerotel import Zerotel

app = FastAPI()
Zerotel(app, service_name="my-api")   # done

What you get

Signal What's captured
Traces Root span per request — method, route, status, latency, error
Traces @trace child spans, nested correctly under the request
Traces SQLAlchemy async queries as child spans (with sanitised SQL)
Metrics zerotel_requests_total — counter by method/route/status
Metrics zerotel_request_duration_seconds — histogram
Metrics zerotel_requests_in_flight — gauge
Logs Structured JSON with trace_id, span_id, service auto-injected

Installation

pip install zerotel

Optional extras:

pip install "zerotel[sqlalchemy]"   # async SQLAlchemy query tracing
pip install "zerotel[flask]"        # Flask WSGI adapter
pip install "zerotel[all]"          # everything

Quickstart

Minimal

from fastapi import FastAPI
from zerotel import Zerotel

app = FastAPI()
Zerotel(app, service_name="payments-api")

Full configuration

from fastapi import FastAPI
from zerotel import Zerotel, ZerotelConfig

app = FastAPI()

Zerotel(app, config=ZerotelConfig(
    service_name="payments-api",
    service_version="2.1.0",
    otlp_endpoint="http://otel-collector:4317",
    enable_traces=True,
    enable_metrics=True,
    enable_logging=True,
    exclude_paths=["/health", "/metrics"],  # skip these from tracing
    log_request_body=False,                 # keep off in prod (PII risk)
    trace_sample_rate=1.0,                  # lower in high-volume prod
))

@trace decorator

Add a named child span to any function — works on both async def and def:

from zerotel import trace

@trace(name="send-email")
async def send_email(user_id: int) -> None:
    ...  # this entire function becomes a child span under the request span

@trace
def compute_score(data: list[float]) -> float:
    ...  # span name defaults to "module.compute_score"

Reading the current trace ID

from zerotel import get_trace_id, get_span_id

def my_helper() -> None:
    tid = get_trace_id()   # 32-char hex or "0" * 32 if outside a request
    sid = get_span_id()    # 16-char hex

FastAPI dependency injection

from fastapi import FastAPI
from zerotel.integrations.fastapi import TraceIdDep, RequestContextDep

app = FastAPI()

@app.get("/profile")
async def get_profile(trace_id: TraceIdDep) -> dict:
    return {"trace_id": trace_id}

SQLAlchemy async query tracing

from sqlalchemy.ext.asyncio import create_async_engine
from zerotel.integrations.sqlalchemy import instrument_sqlalchemy

engine = create_async_engine("postgresql+asyncpg://user:pw@localhost/mydb")
instrument_sqlalchemy(engine)
# Every query is now a child span with sanitised SQL as an attribute

Local observability stack

The docker/ folder contains a ready-to-use Docker Compose stack:

Service URL Purpose
OTel Collector Receives OTLP, fans out
Grafana Tempo http://localhost:3200 Trace storage
Prometheus http://localhost:9090 Metrics storage
Grafana Loki http://localhost:3100 Log aggregation
Grafana http://localhost:3000 Unified UI
cd docker/
docker compose up -d

# Run your service
OTLP_ENDPOINT=http://localhost:4317 uvicorn myapp:app --reload

# Open Grafana (admin / admin)
open http://localhost:3000

Configuration reference

Field Type Default Description
service_name str "unknown-service" Appears on every trace, metric, and log
service_version str "0.0.0" Attached to trace resources
otlp_endpoint str "http://localhost:4317" OTLP gRPC collector address
enable_traces bool True Export OpenTelemetry spans
enable_metrics bool True Expose Prometheus /metrics
enable_logging bool True Configure structlog JSON
exclude_paths list[str] ["/health", "/metrics"] Paths to skip from instrumentation
log_request_body bool False Capture request body on span (PII risk)
trace_sample_rate float 1.0 Fraction of requests to sample (0.0–1.0)
metrics_endpoint str "/metrics" Path for Prometheus scrape endpoint

Development

git clone https://github.com/Kamalesh-Kavin/zerotel
cd zerotel
uv sync --extra dev

# Run tests
uv run pytest

# Lint
uv run ruff check src/
uv run ruff format src/

See CONTRIBUTING.md for full guidelines.


Philosophy

"If you cannot explain the code without the AI, you haven't learned it yet."

Every file in this project is heavily commented. The goal is for you to be able to read any file cold and understand exactly what it does and why.


License

MIT — see LICENSE.

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

zerotel-0.1.0.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

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

zerotel-0.1.0-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

File details

Details for the file zerotel-0.1.0.tar.gz.

File metadata

  • Download URL: zerotel-0.1.0.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for zerotel-0.1.0.tar.gz
Algorithm Hash digest
SHA256 aa51f913bca56dab52aa1e4d722c956e06175cec24af23d8a4d80c6537ebaea3
MD5 0aef990d14f3ea0930ae9f2322aba60d
BLAKE2b-256 c872345d19b293fcbe68290780c3e30a9cf7e18ac2f592d55ab5e8b37c800ce2

See more details on using hashes here.

File details

Details for the file zerotel-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: zerotel-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 26.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for zerotel-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 55d1389bb80a45ce92a68736d86ff8b19d681deb288e161878004e68c1a138de
MD5 aefd58aa9f4634152970403ce4a03562
BLAKE2b-256 fbcfd4348c07fd25cc83ca49d5a27f8e6f77cd887c159a0ad78ee326862b038b

See more details on using hashes here.

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