Home

BugTape blog

Python + Streamlit: redacted ERROR logs to BugTape in 5 minutes

pip install bugtape, init once, and Streamlit page errors plus ERROR logs ship redacted to BugTape. A checklist from the Python docs.

Streamlit apps fail in the page, not always in a tidy ops pipeline. The error shows in the app. The same failure can also hit the logging path. You still need something an agent can pick up later over MCP, with a human reviewing any pull request.

This cookbook follows the public Python docs: install the package, call init, let ERROR logs and Streamlit page errors report with messages redacted by pattern, then confirm an issue in the console.

BugTape is for capture, then agent-native closure via MCP, then pull-request review, then a check that the fix holds in the next release. It is not a pitch to replace your metrics or a full observability stack.

Package: bugtape on PyPI (the wheel named in the Python docs is 0.1.1).

Checklist (5 minutes)

  1. Create a free project and copy a Python capture key from Setup → Python (bt_live_… or bt_test_…). Register.
  2. pip install bugtape (Python 3.9 or later; no package dependencies, per the docs and PyPI).
  3. Export BUGTAPE_KEY.
  4. Call bugtape.init(service=..., release=...) at the top of the Streamlit page.
  5. Trigger a test ERROR or a page exception.
  6. Open Issues in the BugTape console and confirm the report.
  7. Optional: connect MCP so an agent can read the issue (MCP install). A human still reviews the pull request.

Install

From PyPI and the Python docs:

pip install bugtape

If your network cannot reach PyPI, the docs also document a reviewed wheel hosted by BugTape:

pip install https://app.bugtape.ai/sdk/python/bugtape-0.1.1-py3-none-any.whl

The wheel in this post is bugtape-0.1.1-py3-none-any.whl, the file named in the Python docs. If that doc bumps the version, update this post in the same change.

Start (any Python process)

import bugtape

bugtape.init(service="reports", release="2026.09.28")

The capture key is read from the BUGTAPE_KEY environment variable. Without a key, init logs one warning and reporting stays off.

From then on BugTape reports (per the docs):

  • uncaught exceptions in the main thread and in threads
  • log records at ERROR and above, from any logger
  • anything you pass to bugtape.capture_exception() or bugtape.capture_message()
  • uncaught exceptions in Streamlit pages, when Streamlit is imported
try:
    run_export()
except Exception:
    bugtape.capture_exception()  # reports the exception being handled
    raise

Streamlit

import streamlit as st
import bugtape

bugtape.init(service="analytics")

st.title("Revenue")

Notes from the docs that matter in production:

  • Streamlit reruns the page on interaction. Calling init at the top with the same settings keeps the running client. That is fine.
  • Streamlit catches page errors and shows them in the app, so they never reach Python’s normal error hook. BugTape wraps the function Streamlit uses for those errors. The app still shows the error. BugTape files one report with the page name and the Streamlit session.
  • Streamlit also logs the same error. BugTape recognises it and does not report it twice.
  • If a future Streamlit release changes that hook, init(streamlit=True) logs a warning. You can wrap page code yourself:
from bugtape.integrations.streamlit import capture

with capture():
    render_page()

What leaves your server (redaction)

Per the Python docs.

Sent: exception type and a redacted message; file, function and line per frame; service, release and environment; log templates such as export failed for %s; Datadog trace and span ids when ddtrace is running.

Never sent: frame locals; source code lines; arguments passed to a log call; request bodies; other environment variables beyond the documented settings, Datadog keys, and the commit keys used for release.

Redaction replaces quoted values, numbers and amounts, SQL, emails, credentialed URLs, cloud paths, ids, and common token= / password= style pairs. Pattern-based redaction has limits (for example a bare name in an f-string). Mitigations in the docs: log with %s arguments, extra_redactions, before_send, or send_messages=False.

bugtape.init(
    service="reports",
    extra_redactions=[(r"\bACME-\d+\b", "[account]")],
    before_send=lambda report: None if "healthcheck" in report["title"] else report,
)

Release (so fix verification works)

Every report should carry a release so BugTape can tell a fixed issue from a regression. Pass release=, or set BUGTAPE_RELEASE or DD_VERSION, or rely on the CI commit environment variables listed in the docs (GITHUB_SHA and the rest of COMMIT_ENV_KEYS). Without a release the SDK records release_missing.

Check it

BUGTAPE_KEY=bt_test_… python -c "import bugtape; bugtape.init(service='check'); bugtape.capture_message('BugTape Python check'); bugtape.flush()"

Then open Issues in the console.

For short scripts, call bugtape.flush() before exit so queued reports send.

Optional: agent loop after the error lands

Once issues appear:

  1. Connect MCP (install guide, or the MCP install post).
  2. The agent uses list_recommended_critical, then get_repro_context, then ack_bug with prUrl.
  3. You review the pull request.
  4. Mark the issue fixed and ship a release. BugTape alerts you if the same failure returns on a newer release.

Summary

StepAction
Installpip install bugtape
ConfigureBUGTAPE_KEY plus bugtape.init(service=..., release=...)
Streamlitinit at the top of the page; page errors and ERROR logs
PrivacyMessages redacted by pattern; locals never read
NextMCP, then a human-reviewed pull request

Full reference: Python docs. Where data lives and what is masked: Trust.