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.
The short answer#
Use the official
sentry-sdkwith your Belvador DSN.A
scrub_eventhook 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-sdkitself.
Install#
pip install --upgrade "sentry-sdk"Initialize at start-up#
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#
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 jobCelery 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-sdkrelease.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.