Skip to main content

Error Reporting and Exception Tracking in Python

Error reporting systems (Sentry, Rollbar, Airbrake, Honeycomb) aggregate exceptions from your application and alert you to new errors, error spikes, and regressions. Instead of waiting for customers to report bugs, error tracking systems detect issues in real time and provide stack traces, affected user counts, and reproduction steps. This is essential for production applications where bugs are discovered in microseconds via automated monitoring, not days later via support emails.

Setting up error reporting is straightforward: initialize a client at startup, and it automatically captures unhandled exceptions. You can also manually report issues and attach custom context (user ID, session, feature flags). The client batches errors and sends them asynchronously, so exception reporting never blocks your application. For most teams, error reporting is the fastest ROI on observability investment because it surfaces issues that would otherwise go undetected.

How Do You Set Up Sentry for Python?

Sentry is the most popular open-source error tracking system. Initialize it at application startup:

import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
dsn="https://[email protected]/0", # Get from Sentry project settings
integrations=[FlaskIntegration()],
traces_sample_rate=1.0, # Sample 100% of transactions for tracing
environment="production",
release="1.2.3"
)

from flask import Flask
app = Flask(__name__)

# Unhandled exceptions are now captured automatically
@app.route('/api/users/<user_id>')
def get_user(user_id):
user = db.query(f"SELECT * FROM users WHERE id = {user_id}") # SQL injection!
if not user:
raise ValueError(f"User {user_id} not found") # Sentry captures this
return user

if __name__ == '__main__':
app.run()

The DSN (Data Source Name) is a URL that tells the Sentry client where to send errors. You get this from your Sentry project dashboard. The integrations list tells Sentry which libraries to instrument (Flask, Django, FastAPI, SQLAlchemy, requests, etc.). Setting traces_sample_rate=1.0 enables distributed tracing; set to 0.1 to sample 10% of requests for performance.

How Do You Manually Report Exceptions to Sentry?

Beyond automatic exception capture, you can manually report issues:

import sentry_sdk

# Report a message
sentry_sdk.capture_message("Payment service is degraded", level="warning")

# Report an exception you caught
try:
risky_operation()
except TimeoutError as e:
sentry_sdk.capture_exception(e)
# Continue with fallback logic

# Report a custom exception
class PaymentError(Exception):
pass

try:
charge_card(user_id, amount)
except PaymentError as e:
sentry_sdk.capture_exception(e)
# Notify user
return {'error': 'Payment failed, please retry'}, 500

For critical issues (e.g., database connection failed), use level="critical" to trigger immediate alerts. For degradation that is recoverable (e.g., a retry succeeded after a timeout), use level="warning".

How Do You Attach Context to Exception Reports?

Error reports are most useful when they include context: which user was affected, what operation were they performing, what feature flag was enabled. Attach context via set_user(), set_tag(), and set_context():

import sentry_sdk
from flask import request, g

@app.before_request
def set_request_context():
"""Called before every request."""
# Set user context
if hasattr(g, 'user'):
sentry_sdk.set_user({'id': g.user.id, 'email': g.user.email})

# Set tags for easy filtering
sentry_sdk.set_tag("api_version", request.headers.get('X-API-Version', 'v1'))
sentry_sdk.set_tag("environment", "production")
sentry_sdk.set_tag("region", os.environ.get('AWS_REGION', 'us-east-1'))

# Set custom context (key-value data)
sentry_sdk.set_context('request_info', {
'path': request.path,
'method': request.method,
'ip': request.remote_addr,
'user_agent': request.headers.get('User-Agent')
})

@app.route('/orders', methods=['POST'])
def create_order():
order_data = request.json

# Set breadcrumbs (events that led to an error)
sentry_sdk.add_breadcrumb(
category='order',
message=f'Order created for user {order_data["user_id"]}',
level='info'
)

try:
result = process_order(order_data)
except Exception as e:
sentry_sdk.capture_exception(e)
raise

sentry_sdk.add_breadcrumb(
category='order',
message=f'Order {result["id"]} saved to database',
level='info'
)
return result

When an error occurs, Sentry captures the user, tags, context, and breadcrumbs alongside the stack trace. This context helps you understand the error's impact and reproduce it.

How Do You Track Errors Across Deployments?

Sentry's release tracking feature lets you correlate errors to specific code versions. This is essential for identifying regressions: "Did this error start in v1.2.3?"

import sentry_sdk
import os

# Get the current release from environment variable or version file
release = os.environ.get('APP_VERSION', 'dev')

sentry_sdk.init(
dsn="https://...",
release=release,
environment=os.environ.get('ENVIRONMENT', 'development'),
integrations=[...],
traces_sample_rate=0.1
)

@app.route('/health')
def health():
return {
'status': 'ok',
'release': release,
'environment': os.environ.get('ENVIRONMENT', 'development')
}

In your CI/CD pipeline, set the APP_VERSION environment variable before deploying:

export APP_VERSION=$(git describe --tags --abbrev=0)
python app.py

Or set it in your Docker image:

FROM python:3.11
ENV APP_VERSION=1.2.3
COPY . /app
WORKDIR /app
CMD ["python", "app.py"]

In the Sentry dashboard, you can filter errors by release and see which versions have the highest error rates.

How Do You Use Performance Monitoring to Detect Slow Requests?

Sentry integrates with its distributed tracing to track slow requests. Set traces_sample_rate to a non-zero value:

import sentry_sdk

sentry_sdk.init(
dsn="https://...",
traces_sample_rate=0.1, # Trace 10% of requests
integrations=[FlaskIntegration()],
)

# Sentry now captures transactions (equivalent to OpenTelemetry traces)
# and alerts you to slow endpoints

In the Sentry dashboard, you see:

  • Performance metrics per endpoint: p50, p95, p99 latencies
  • Slow transactions ranked by duration
  • Error rate per endpoint
  • Throughput (requests per second)

You can set alerts like "notify if the 95th percentile latency of POST /orders exceeds 2 seconds" or "notify if error rate of GET /api/users exceeds 1%".

How Do You Test Error Reporting Locally?

Raise an exception and verify Sentry captures it:

import sentry_sdk

sentry_sdk.init(
dsn="https://[email protected]/0",
traces_sample_rate=1.0,
environment="development"
)

# Test 1: Unhandled exception
try:
raise ValueError("Test error")
except:
pass # Sentry captures this

# Test 2: Manual capture
sentry_sdk.capture_message("Test message", level="info")

# Test 3: Exception with context
sentry_sdk.set_user({'id': 42, 'email': '[email protected]'})
sentry_sdk.capture_exception(RuntimeError("Test with context"))

# Flush and check Sentry dashboard
sentry_sdk.flush()
print("Check Sentry dashboard at https://sentry.io")

Key Takeaways

  • Error tracking systems (Sentry, Rollbar) aggregate exceptions and alert you to issues.
  • Initialize the client at startup; it auto-captures unhandled exceptions.
  • Attach context (user, tags, breadcrumbs) to error reports for debugging.
  • Track releases to correlate errors to code versions and detect regressions.
  • Enable performance monitoring to detect slow requests and degradation.

Frequently Asked Questions

Should I send PII (user emails, phone numbers) to Sentry?

Be cautious. Sentry has data processing options to redact sensitive fields, but GDPR and privacy laws may require explicit user consent. Strip or hash PII before sending, or use Sentry's Data Scrubbing rules.

How do I avoid alerting fatigue from too many errors?

Set thresholds: alert on error rate > 1% or error count > 100/hour, not on every individual error. Use issue grouping (Sentry auto-groups similar errors) and silence known issues until they are fixed.

Can I send custom metrics alongside errors?

Yes. Use breadcrumbs to record state changes: sentry_sdk.add_breadcrumb(category='db', message=f'Query took {duration_ms}ms'). In Sentry dashboards, you can analyze breadcrumbs to correlate with errors.

How do I handle errors in background tasks (async, Celery)?

Sentry's Flask/Django integrations auto-capture exceptions in Celery tasks. If using raw asyncio, wrap tasks with try/except and call sentry_sdk.capture_exception().

What is the performance impact of error reporting?

Minimal. Sentry's client runs asynchronously and batches events. In production deployments, overhead is typically under 1% CPU and 10 MB memory. Disable tracing (traces_sample_rate=0) if you need to reduce overhead.

Further Reading