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.2.0.tar.gz (22.3 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.2.0-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: zerotel-0.2.0.tar.gz
  • Upload date:
  • Size: 22.3 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.2.0.tar.gz
Algorithm Hash digest
SHA256 2acb2dbadc5c177be6fe41c17260a79c662a61dc54eccbdbb96ea023d719ce7c
MD5 9ac0481a4d8243460d12f0444d95f6b8
BLAKE2b-256 89deca0ecbe13227b40f34be39f8b09b7eef8056e8cd558a6c9d07007850cb20

See more details on using hashes here.

File details

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

File metadata

  • Download URL: zerotel-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 29.2 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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 010e1ab1ef71f5ed421400c57a898c5c53c4ec5f989932773dd364c42d0b5885
MD5 f105379f371488f63994ada9688c06e4
BLAKE2b-256 c51f59d404e2b15d134807fefcc0bfd058f3e1dcf728399960fd8de4132b3855

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