Integrating Sentry SDK with Python: Automatic Production Error Capture, APM Monitoring, and Real-Time Alerts

Python tutorial - IT technology blog
Python tutorial - IT technology blog

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).

Share: