docs
Dashboard

Python SDK

The official Python SDK for booboo.dev error tracking.

Installation

pip install booboo-sdk

Quick start

import booboo
booboo.init("https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project")

That's all you need. The SDK hooks into sys.excepthook to capture unhandled exceptions automatically.

API Reference

booboo.init(dsn, app=None, environment="", ignore_errors=None, before_send=None)

Initialize booboo error tracking. Call this once at application startup.

ParameterTypeDescription
dsnstrRequired. Your project's DSN key from the dashboard.
appFlask/FastAPIPass your WSGI/ASGI app instance for framework-specific error handling.
environmentstrFree-form environment name (e.g. "development", "acceptance", "production"). Attached to every event for filtering in the dashboard.
ignore_errorslistException classes to suppress. Uses isinstance() so subclasses are matched automatically (e.g. [OSError] also catches ConnectionError).
before_sendcallableHook called with the event dict before sending. Return the (possibly modified) event, or None to drop it. Requires SDK 0.14.0+.

Full example with all options:

from flask import Flask
import booboo

app = Flask(__name__)

def scrub_event(event):
    # Modify the event dict here, or return None to drop it
    return event

booboo.init(
    "https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project",
    app=app,                                    # Flask or FastAPI instance
    environment="production",                   # Filter events by environment
    ignore_errors=[KeyboardInterrupt, OSError], # Suppress noisy exceptions
    before_send=scrub_event,                    # Hook to modify or drop events
)

# Set user context — attached to every subsequent event
booboo.set_user({
    "id": "user-123",
    "email": "alice@example.com",
    "username": "alice",
})

The init() function:

  • Hooks sys.excepthook for unhandled exceptions
  • Auto-detects Django via django.core.signals.got_request_exception
  • Registers Flask/FastAPI error handlers when app is provided

booboo.capture_exception(exc=None)

Manually capture an exception. Useful for errors you catch and handle.

ParameterTypeDescription
excExceptionThe exception to capture. If None, uses sys.exc_info().
try:
    do_something_risky()
except Exception:
    booboo.capture_exception()
    # handle the error gracefully

booboo.capture_message(message, level="info")

Send a plain message event (not tied to an exception). Useful for tracking deployments, warnings, or custom events.

ParameterTypeDescription
messagestrRequired. The message to send.
levelstrEvent level: "error", "warning", or "info". Defaults to "info".
booboo.capture_message("Deployment started", "info")
booboo.capture_message("Disk usage above 90%", "warning")

booboo.set_user(user)

Set user context that is attached to every subsequent event. Useful for identifying which user experienced an error.

ParameterTypeDescription
userdictUser data with keys like id, email, username, ip_address.

What gets captured

Each error event sent to booboo.dev includes:

  • Exception type — e.g. ValueError, ConnectionError
  • Message — the string representation of the exception
  • Stack trace — every frame with filename, function name, and line number
  • Source context — 5 lines of code above and below the error line
  • Local variablesrepr() of locals in each frame (truncated to 200 chars)
  • Request data — HTTP method and path (when available from framework integrations)
  • Environment — the environment tag set during init() (e.g. "production")

Variable scrubbing

The SDK automatically scrubs sensitive local variables. Any variable whose name contains password, secret, token, api_key, auth, credential, or private is replaced with [filtered]. Dunder variables (__name__, etc.) are excluded entirely.

before_send hook

Use before_send (SDK 0.14.0+) to inspect or modify every event before it leaves your server. Return the event to send it, or None to drop it:

def drop_health_checks(event):
    if "/healthz" in event.get("message", ""):
        return None  # drop
    return event

booboo.init(
    "https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project",
    before_send=drop_health_checks,
)

The hook runs for every event — exceptions and capture_message() calls alike — on the SDK's background sender thread, so it adds no latency to your request path. If the hook raises, the event is dropped, never sent unmodified.

Value-level PII scrubbing

The built-in scrubbing above redacts variables by name — but only your code knows which values are sensitive (IBANs, payment references, email addresses inside variable values — the things a GDPR processing register cares about). A thorough scrubber must visit every place values appear: the message, the top-level stacktrace's variables, and each chained exception's message and frames:

import re
import booboo

IBAN_RE = re.compile(r"\b[A-Z]{2}\d{2}[A-Z0-9]{10,30}\b")

def scrub_ibans(event):
    all_frames = list(event.get("stacktrace", []))
    for exc in event.get("exceptions", []):
        all_frames.extend(exc.get("stacktrace", []))
        exc["value"] = IBAN_RE.sub("[iban]", exc.get("value", ""))
    for frame in all_frames:
        for name, value in frame.get("vars", {}).items():
            frame["vars"][name] = IBAN_RE.sub("[iban]", value)
    event["message"] = IBAN_RE.sub("[iban]", event.get("message", ""))
    return event

booboo.init(
    "https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project",
    before_send=scrub_ibans,
)

Event shape

The event dict your hook receives looks like this (annotated, abridged):

{
    "message": "division by zero",          # str(exception), or capture_message() text
    "exception_type": "ZeroDivisionError",  # "" for capture_message() events
    "level": "error",                       # "error", "warning", or "info"
    "environment": "production",
    "tags": {"runtime": "python"},
    "context": {
        "sdk": {"name": "booboo-sdk", "version": "0.14.0"},
        "runtime": {"name": "Python", "version": "3.12.4"},
        "user": {"id": "42"},               # present when set_user() was called
    },
    "request": {"method": "POST", "url": "..."},  # present on framework-captured errors
    "stacktrace": [                         # frames of the outermost exception
        {
            "filename": "app/billing.py",
            "function": "charge",
            "lineno": 42,
            "context_line": "    total = amount / count",
            "pre_context": ["..."],         # up to 5 source lines before
            "post_context": ["..."],        # up to 5 source lines after
            "vars": {"amount": "100", "count": "0"},  # repr() strings, scrubbed by name
            "in_app": True,
        },
    ],
    "exceptions": [                         # full exception chain, outermost first
        {
            "type": "ZeroDivisionError",
            "value": "division by zero",
            "stacktrace": ["..."],          # same frame shape as above
            "chain_type": None,             # None, "cause" (raise from), or "context"
        },
    ],
}

How events are sent

Events are sent asynchronously in background threads so they never block your application. Pending events are flushed on process exit via atexit.

The SDK sends events as HTTP POST requests to the ingestion endpoint. The DSN you pass to init() can be a URL (e.g. https://TOKEN@ingest.booboo.dev/your-org/your-project) — the SDK parses the URL to derive the ingest endpoint and sends only the token in the X-Booboo-DSN header.

Django

Django is auto-detected — no app argument needed. The SDK connects to Django's got_request_exception signal to capture errors from views automatically.

# settings.py (or manage.py, wsgi.py — anywhere before requests are served)
import booboo
booboo.init("https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project")
Tip: Add the import to your settings.py for the simplest setup. The SDK will begin capturing errors immediately — no middleware or INSTALLED_APPS entry required.

Django errors automatically include the HTTP method and request path in the event data. 404 errors are filtered by default.

Flask

Pass your Flask app instance to booboo.init(). The SDK registers an error handler that captures exceptions before they reach your error page.

from flask import Flask
import booboo

app = Flask(__name__)
booboo.init("https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project", app=app)

@app.route("/")
def hello():
    return "Hello, world!"

Flask errors include the HTTP method and request path in the event data. 404 errors are filtered by default.

FastAPI

Pass your FastAPI app instance to booboo.init(). The SDK registers an async exception handler for all unhandled errors.

from fastapi import FastAPI
import booboo

app = FastAPI()
booboo.init("https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project", app=app)

@app.get("/")
def hello():
    return {"message": "Hello, world!"}

FastAPI errors include the HTTP method and request URL path in the event data.

Other Python frameworks

Even without explicit framework support, booboo.init() hooks sys.excepthook to capture any unhandled exception in your Python process. You can also use booboo.capture_exception() in try/except blocks for manual capture.

import booboo
booboo.init("https://YOUR_TOKEN@ingest.booboo.dev/your-org/your-project")

# In your error handling code:
try:
    process_request()
except Exception:
    booboo.capture_exception()
    return {"error": "Something went wrong"}, 500