Skip to content

Framework guide

Error tracking for Python

For Python, Belvador uses the official sentry-sdk with your Belvador DSN, plus init options and a scrub_event hook that removes request bodies, cookies and credentials before an event leaves your server.

Start freePython

Updated Oct 25, 2026

The short answer#

  • Use the official sentry-sdk with your Belvador DSN.

  • A scrub_event hook removes request bodies, cookies and credentials before an event leaves your server.

  • Scheduled jobs report check-ins, and OpenTelemetry traces and logs are stored on the same node.

  • This is a compatibility setup: Belvador tests its hook, not sentry-sdk itself.

Install#

Terminal
pip install --upgrade "sentry-sdk"

Initialize at start-up#

app.py
import osimport sentry_sdkimport reSENSITIVE_KEY = re.compile(r"password|passwd|pwd|secret|api_key|apikey|api-key|access_token|auth|credentials|credit_card|card_number|cvv|private_key|privatekey|session|cookie|token|bearer|ssn|dob", re.I)DROP_HEADER = re.compile(r"^(authorization|proxy-authorization|cookie|set-cookie|x-forwarded-for|x-real-ip|cf-connecting-ip|true-client-ip)$", re.I)IN_TEXT = [    (re.compile(r"\bBearer\s+[A-Za-z0-9._~+/-]+=*", re.I), "Bearer [Filtered]"),    (re.compile(r"\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}"), "[Filtered]"),    (re.compile(r"\b[sr]k_(live|test)_[A-Za-z0-9]{8,}\b"), "[Filtered]"),    (re.compile(r"([?&#;\s\"']?[^=&#\s\"':]*(?:token|key|secret|password|session|auth)[^=&#\s\"':]*[\"']?\s*[=:]\s*[\"']?)[^&#\s\"',}]+", re.I), r"\1[Filtered]"),]def scrub_text(text):    for pattern, repl in IN_TEXT:        text = pattern.sub(repl, text)    return textdef scrub_value(value, depth=0):    if isinstance(value, str):        return scrub_text(value)    if depth > 8:        return value    if isinstance(value, (list, tuple)):        return [scrub_value(v, depth + 1) for v in value]    if isinstance(value, dict):        return {k: "[Filtered]" if SENSITIVE_KEY.search(str(k)) else scrub_value(v, depth + 1) for k, v in value.items()}    return valuedef scrub_event(event, hint):    request = event.get("request")    if request:        for field in ("data", "cookies", "env"):            request.pop(field, None)        headers = request.get("headers") or {}        for name in list(headers):            if DROP_HEADER.match(name):                del headers[name]            elif SENSITIVE_KEY.search(name):                headers[name] = "[Filtered]"        if isinstance(request.get("url"), str):            request["url"] = scrub_text(request["url"])        if request.get("query_string"):            request["query_string"] = scrub_value(request["query_string"])    user = event.get("user")    if user is not None:        event["user"] = {"id": user["id"]} if "id" in user else None    if event.get("message"):        event["message"] = scrub_text(event["message"])    if event.get("logentry"):        event["logentry"] = scrub_value(event["logentry"])    for ex in (event.get("exception") or {}).get("values") or []:        if ex.get("value"):            ex["value"] = scrub_text(ex["value"])    for field in ("extra", "contexts", "tags"):        if event.get(field):            event[field] = scrub_value(event[field])    crumbs = event.get("breadcrumbs")    if isinstance(crumbs, dict) and crumbs.get("values"):        crumbs["values"] = [scrub_value(b) for b in crumbs["values"]]    return eventsentry_sdk.init(    dsn=os.environ.get("BELVADOR_DSN", "https://<public-key>@ingest.belvador.dev/<project-id>"),    release=os.environ.get("APP_VERSION"),    environment=os.environ.get("APP_ENV", "production"),    send_default_pii=False,    traces_sample_rate=0,    max_breadcrumbs=50,    max_value_length=8192,    before_send=scrub_event,)

Initialize before your web framework is imported so its integration is set up too. The Python guide explains each option.

What this setup is#

A compatibility setup, not a Belvador package. Belvador's tests run scrub_event on hostile data, but Belvador does not certify sentry-sdk or its framework integrations. Your node scrubs again before storing anything (data protection).

Scheduled jobs#

Python
from sentry_sdk.crons import monitor@monitor(monitor_slug="nightly-invoices", monitor_config={"schedule": {"type": "crontab", "value": "0 2 * * *"}, "timezone": "UTC"})def nightly_invoices():    ...  # your job

Celery beats, cron scripts and management commands can report the same way (cron monitoring).

Traces and logs#

Use the OpenTelemetry Python SDK or zero-code instrumentation with the standard OTLP exporter (OpenTelemetry for Python).

Tested on 2026-10-25 with python 3.12.15, opentelemetry-distro 0.66b1, opentelemetry-sdk 1.45.1, flask 3.1.3: traces passed, logs passed, logs carry the request's trace and span.

Limits#

  • Supported Python versions follow the current sentry-sdk release.

  • No profiling; traces and logs are kept for debugging for a limited time.

Questions#

Do Django, Flask and FastAPI integrations work?#

They are part of sentry-sdk and send through the same DSN. Belvador does not certify each integration; the scrubbing hook runs on every event they produce.

Try it on your own Hetzner project

Free during the beta. Hetzner bills the server; Belvador never bills per event.