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)
- Create a free project and copy a Python capture key from Setup → Python (
bt_live_…orbt_test_…). Register. pip install bugtape(Python 3.9 or later; no package dependencies, per the docs and PyPI).- Export
BUGTAPE_KEY. - Call
bugtape.init(service=..., release=...)at the top of the Streamlit page. - Trigger a test ERROR or a page exception.
- Open Issues in the BugTape console and confirm the report.
- 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 bugtapeIf 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.whlThe 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
ERRORand above, from any logger - anything you pass to
bugtape.capture_exception()orbugtape.capture_message() - uncaught exceptions in Streamlit pages, when Streamlit is imported
try:
run_export()
except Exception:
bugtape.capture_exception() # reports the exception being handled
raiseStreamlit
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
initat 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:
- Connect MCP (install guide, or the MCP install post).
- The agent uses
list_recommended_critical, thenget_repro_context, thenack_bugwithprUrl. - You review the pull request.
- Mark the issue fixed and ship a release. BugTape alerts you if the same failure returns on a newer release.
Summary
| Step | Action |
|---|---|
| Install | pip install bugtape |
| Configure | BUGTAPE_KEY plus bugtape.init(service=..., release=...) |
| Streamlit | init at the top of the page; page errors and ERROR logs |
| Privacy | Messages redacted by pattern; locals never read |
| Next | MCP, then a human-reviewed pull request |
Full reference: Python docs. Where data lives and what is masked: Trust.