A Real Scenario: 2 AM, Production Server Down
2:17 AM. Phone buzzing. A customer in Japan is reporting errors — the checkout page won’t load.
I SSH into the server, run journalctl -u myapp -n 200, and find a wall of tracebacks. But the real question is: when did this error start? How many users are affected? Which request triggered it?
The log file has thousands of lines. Manual grepping takes 20 minutes just to pinpoint the problem. That’s when it hits you — you’re completely blind to your production state. Nothing but raw logs and hoping users will report issues.
That was the moment I started taking Sentry seriously.
The Problem Isn’t Missing Logs — It’s Missing Context
I had plenty of logs. So many I couldn’t even read them all. The problem was missing context — and a way to aggregate them into actionable information. During a production incident, here’s what you actually need:
- Full stack traces, not just the last 5 lines
- Request URL, HTTP method, request body
- Which user was affected (user_id, email)
- Environment details: Python version, server name, which deploy version
- When this error first occurred, and how often
- Whether it correlates with the most recent deploy
logging.error(str(e)) gives you none of that. It gives you one line of text.
There’s also performance monitoring. Before Sentry, I had no idea which endpoints were slow or which queries were eating resources — until a user complained or the server ran out of memory.
Two Approaches to Solving This
Option 1: Build Your Own Alert System with Logging + Slack
Many teams write custom middleware to push errors to Slack:
import traceback
import requests
def notify_slack(error_msg):
webhook = "https://hooks.slack.com/services/..."
requests.post(webhook, json={"text": f"🚨 Error: {error_msg}"})
try:
process_order(order_id)
except Exception as e:
notify_slack(traceback.format_exc())
raise
This works. But it has problems: no deduplication — the same error can fire 1,000 Slack messages in minutes. No grouping by type. No performance profiling. And you own the entire pipeline yourself.
Option 2: Use Sentry SDK
Sentry is built to do one thing well: error monitoring. It’s available as both a free self-hosted option and a cloud service. Integrating it into Python takes just a pip install and a few lines of code:
pip install sentry-sdk
import sentry_sdk
sentry_sdk.init(
dsn="https://[email protected]/project-id",
traces_sample_rate=1.0,
environment="production",
release="[email protected]",
)
That’s all it takes to get started. From this point on, every unhandled exception is automatically captured by Sentry.
Integrating Sentry: From Basic Setup to Production-Ready
1. Full Configuration with Logging Integration
import sentry_sdk
from sentry_sdk.integrations.logging import LoggingIntegration
import logging
logging_integration = LoggingIntegration(
level=logging.INFO, # Capture breadcrumbs from INFO level
event_level=logging.ERROR # Only create Sentry events from ERROR level
)
sentry_sdk.init(
dsn="https://[email protected]/project-id",
integrations=[logging_integration],
traces_sample_rate=0.1, # Sample 10% of requests for APM
profiles_sample_rate=0.1,
environment="production",
release="[email protected]",
send_default_pii=False, # Do not send PII
)
2. Attach User Context to Each Request
When debugging an error, knowing who encountered it is just as important as knowing what the error was:
from sentry_sdk import set_user, set_tag, set_context
# Call this after authentication completes
def set_sentry_user(user):
set_user({
"id": user.id,
"email": user.email,
"username": user.username,
})
set_tag("plan", user.plan)
set_tag("region", user.region)
# Custom context with no limits
set_context("order", {
"order_id": order.id,
"total": order.total,
"items_count": len(order.items),
})
3. Manual Exception Capture with Breadcrumbs
Breadcrumbs record each step a user took before an error occurred. With a full timeline in the incident view, you can reproduce bugs precisely instead of guessing:
from sentry_sdk import add_breadcrumb, capture_exception
def process_payment(order_id: str, amount: float):
add_breadcrumb(
category="payment",
message=f"Starting payment for order {order_id}",
level="info",
data={"amount": amount}
)
try:
result = payment_gateway.charge(amount)
add_breadcrumb(
category="payment",
message="Gateway responded",
data={"status": result.status}
)
return result
except PaymentGatewayError as e:
capture_exception(e) # Capture but do not re-raise
return {"success": False, "error": "Payment failed"}
except Exception as e:
capture_exception(e)
raise # Unexpected error — re-raise to trigger an alert
4. APM for FastAPI and SQLAlchemy
Simply add integrations to the list and Sentry automatically instruments the entire framework — no need to modify individual endpoints:
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
sentry_sdk.init(
dsn="...",
integrations=[
StarletteIntegration(transaction_style="endpoint"),
FastApiIntegration(transaction_style="endpoint"),
SqlalchemyIntegration(), # Automatically trace DB queries
],
traces_sample_rate=0.05, # 5% for high-traffic apps
)
Once set up, Sentry displays a waterfall chart for each request: processing time, N+1 queries, slow endpoints. The first time I looked at the dashboard, I discovered an endpoint making 47 separate queries instead of a single JOIN — something logs would never show you.
5. Filter Events to Reduce Alert Noise
Not every exception warrants a 2 AM wake-up call. 404s from bot scanners, routine timeouts, and user validation errors don’t need to page you:
import re
def before_send(event, hint):
# Ignore health check endpoints
if "request" in event:
url = event["request"].get("url", "")
if re.search(r"/(health|ping|metrics)", url):
return None # None = do not send this event to Sentry
# Ignore specific exception types
if "exc_info" in hint:
exc_type, exc_value, _ = hint["exc_info"]
if isinstance(exc_value, (KeyboardInterrupt, BrokenPipeError)):
return None
return event
sentry_sdk.init(
dsn="...",
before_send=before_send,
)
When I need to write more complex URL filter patterns inside before_send, I often reach for regex. To quickly test a pattern before adding it to the code, I use a regex tester at toolcraft.app/en/tools/developer/regex-tester — it runs right in the browser, nothing to install, and saves a lot of debugging time.
6. Release Tracking with CI/CD
With release tracking, Sentry knows which commit introduced an error and who changed that line of code:
# In the CI pipeline, after deploying
export SENTRY_AUTH_TOKEN=your-auth-token
export SENTRY_ORG=your-org
export SENTRY_PROJECT=your-project
pip install sentry-cli
sentry-cli releases new "myapp@${GIT_SHA}"
sentry-cli releases set-commits "myapp@${GIT_SHA}" --auto
sentry-cli releases finalize "myapp@${GIT_SHA}"
7. Alert Rules on the Dashboard
In the Sentry dashboard, go to Alerts → Create Alert Rule:
- Issue Alert: Triggers when a new error appears for the first time, or when it regresses after being resolved
- Metric Alert: Triggers when error rate exceeds a threshold (e.g., >5% of requests failing within 5 minutes)
- Performance Alert: Triggers when P95 latency for an endpoint spikes suddenly
The hardest part isn’t the setup — it’s picking the right thresholds: too sensitive and you get alert fatigue; too loose and you miss real incidents.
8. Self-Hosted for Teams That Need Data Control
# Requires ~4GB RAM
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted
./install.sh
docker-compose up -d
Self-hosting is the right call if you have compliance requirements. The free cloud plan allows 5,000 errors per month — enough for small to medium projects.
After that 2 AM incident, I set up Sentry across all production apps. The next time an error hit, Sentry pushed a Slack notification with full context within seconds — I knew exactly which user was affected, which endpoint, and which deploy caused it. MTTR dropped from 45 minutes to 8 minutes. That’s the difference between reactive (waiting for users to report) and proactive (knowing before users even notice).

