Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

Python - Sentry SDK

Real-time error tracking and performance monitoring for Python applications.

Python - Sentry SDK

Real-time error tracking and performance monitoring for Python applications.

Overview

Sentry is an error tracking and performance monitoring platform that helps developers identify, triage, and resolve issues in production. The Sentry Python SDK automatically captures exceptions, logs, and performance metrics, providing detailed context about errors and their impact.

CapturesSends EventsStoresNotifiesDisplaysAnalysisPython AppSentry SDKSentry ServerEvent DataAlertsDashboardDevelopersCapturesSends EventsStoresNotifiesDisplaysAnalysisPython AppSentry SDKSentry ServerEvent DataAlertsDashboardDevelopers

Installation and Setup

Installation

# Basic installation
pip install sentry-sdk

# With web framework integrations
pip install sentry-sdk[flask]
pip install sentry-sdk[django]
pip install sentry-sdk[fastapi]

# With additional features
pip install sentry-sdk[sqlalchemy]
pip install sentry-sdk[redis]

Basic Initialisation

import sentry_sdk

sentry_sdk.init(
    dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
    # Set traces_sample_rate to 1.0 to capture 100%
    # of transactions for performance monitoring.
    traces_sample_rate=1.0,
    # Set profiles_sample_rate to 1.0 to profile 100%
    # of sampled transactions.
    profiles_sample_rate=1.0,
)

Environment Configuration

import sentry_sdk

sentry_sdk.init(
    dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
    environment="production",  # e.g., "staging", "development"
    release="my-app@1.0.0",    # Version tracking
    traces_sample_rate=0.1,     # 10% of transactions
    profiles_sample_rate=0.1,   # 10% of profiles

    # Additional options
    debug=False,                # Enable for debugging SDK issues
    attach_stacktrace=True,     # Attach stacktrace to messages
    send_default_pii=False,     # Don't send personally identifiable info
    max_breadcrumbs=50,         # Maximum breadcrumbs to store
)

Framework Integration

# Flask
from flask import Flask
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
    integrations=[FlaskIntegration()],
)

app = Flask(__name__)

# Django (in settings.py)
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
    integrations=[DjangoIntegration()],
)

# FastAPI
from fastapi import FastAPI
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration

sentry_sdk.init(
    dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
    integrations=[
        StarletteIntegration(),
        FastApiIntegration(),
    ],
)

app = FastAPI()

Capturing Exceptions and Messages

Automatic Exception Capture

import sentry_sdk

sentry_sdk.init(dsn="...")

# Uncaught exceptions are automatically captured
def process_data():
    result = 1 / 0  # ZeroDivisionError captured automatically

try:
    process_data()
except Exception as e:
    # Exception is already captured, but you can add context
    sentry_sdk.capture_exception(e)

Manual Exception Capture

import sentry_sdk

try:
    risky_operation()
except Exception as e:
    # Capture with additional context
    sentry_sdk.capture_exception(e)

# Capture current exception in except block
try:
    something()
except:
    sentry_sdk.capture_exception()

Logging Messages

import sentry_sdk

# Capture informational messages
sentry_sdk.capture_message("User login successful", level="info")

# Different severity levels
sentry_sdk.capture_message("Database connection slow", level="warning")
sentry_sdk.capture_message("Critical system failure", level="error")
sentry_sdk.capture_message("Security breach detected", level="fatal")

# Available levels: "debug", "info", "warning", "error", "fatal"

Breadcrumbs

import sentry_sdk

# Add breadcrumbs for context
sentry_sdk.add_breadcrumb(
    category="auth",
    message="User logged in",
    level="info",
)

sentry_sdk.add_breadcrumb(
    category="database",
    message="Query executed",
    level="info",
    data={
        "query": "SELECT * FROM users",
        "duration_ms": 145,
    }
)

# Breadcrumbs are automatically included with errors
try:
    process_order()
except Exception:
    sentry_sdk.capture_exception()  # Includes all breadcrumbs

Context and User Information

Setting User Context

import sentry_sdk

# Set user information
sentry_sdk.set_user({
    "id": "12345",
    "email": "john.doe@example.com",
    "username": "johndoe",
    "ip_address": "127.0.0.1",
})

# Clear user context
sentry_sdk.set_user(None)

Adding Tags

import sentry_sdk

# Add tags for filtering and searching
sentry_sdk.set_tag("environment", "production")
sentry_sdk.set_tag("server", "web-01")
sentry_sdk.set_tag("feature_flag", "new_checkout")

# Tags help organise and filter issues in Sentry UI

Adding Extra Context

import sentry_sdk

# Add arbitrary extra data
sentry_sdk.set_context("order", {
    "order_id": "ORD-123",
    "total": 99.99,
    "items": 3,
    "payment_method": "credit_card",
})

sentry_sdk.set_context("server_info", {
    "hostname": "web-01",
    "region": "eu-west-1",
    "instance_type": "t3.medium",
})

Scoped Context

import sentry_sdk

# Context for a specific operation (SDK v2+)
def process_user_request(user_id):
    with sentry_sdk.new_scope() as scope:
        scope.set_user({"id": user_id})
        scope.set_tag("operation", "user_request")
        scope.set_context("request_info", {
            "user_id": user_id,
            "timestamp": "2025-11-24T10:30:00Z",
        })

        # Any errors here include this context
        risky_operation()

    # Context is cleared after the scope

Transaction Context

import sentry_sdk

# Group related operations (SDK v2+)
scope = sentry_sdk.get_current_scope()
scope.set_transaction_name("process_payment")

# All events use this transaction name
process_payment()
validate_card()
charge_customer()

Performance Monitoring

Transaction Tracing

import sentry_sdk

# Manual transaction
with sentry_sdk.start_transaction(name="process_order", op="task") as transaction:
    # Transaction automatically tracked
    validate_order()
    charge_payment()
    send_confirmation()

Spans for Detailed Timing

import sentry_sdk

with sentry_sdk.start_transaction(name="checkout", op="http.server"):
    # Add child spans for granular timing
    with sentry_sdk.start_span(op="db.query", description="Load user"):
        user = db.query(User).get(user_id)

    with sentry_sdk.start_span(op="http.request", description="Payment API"):
        response = payment_gateway.charge(user, amount)

    with sentry_sdk.start_span(op="db.query", description="Save order"):
        order = Order.create(user=user, total=amount)
        db.session.add(order)
        db.session.commit()

Automatic Performance Tracking

import sentry_sdk
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.integrations.redis import RedisIntegration

sentry_sdk.init(
    dsn="...",
    traces_sample_rate=0.1,
    integrations=[
        SqlalchemyIntegration(),  # Track database queries
        RedisIntegration(),        # Track Redis operations
    ],
)

# Database queries and Redis ops are automatically tracked

Custom Performance Metrics

import sentry_sdk
import time

def process_batch():
    start_time = time.time()

    with sentry_sdk.start_transaction(name="batch_process", op="task"):
        # Your processing logic
        for item in items:
            with sentry_sdk.start_span(op="process", description="item"):
                process_item(item)

        duration = time.time() - start_time
        sentry_sdk.set_measurement("batch_duration", duration, "second")
        sentry_sdk.set_measurement("items_processed", len(items), "none")

Common Use Cases

Web Application Error Tracking

# Flask application
from flask import Flask, request
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn="...",
    integrations=[FlaskIntegration()],
    traces_sample_rate=0.1,
    before_send=lambda event, hint: filter_sensitive_data(event),
)

app = Flask(__name__)

@app.before_request
def add_request_context():
    sentry_sdk.set_context("request", {
        "url": request.url,
        "method": request.method,
        "user_agent": request.user_agent.string,
    })

@app.route("/api/users/<user_id>")
def get_user(user_id):
    with sentry_sdk.start_transaction(name="get_user", op="http.server"):
        user = User.query.get(user_id)
        if not user:
            sentry_sdk.capture_message(f"User {user_id} not found", level="warning")
            return {"error": "Not found"}, 404
        return user.to_dict()

Background Job Monitoring

import sentry_sdk
from celery import Celery
from sentry_sdk.integrations.celery import CeleryIntegration

sentry_sdk.init(
    dsn="...",
    integrations=[CeleryIntegration()],
    traces_sample_rate=1.0,
)

app = Celery("tasks")

@app.task
def process_email_queue():
    with sentry_sdk.start_transaction(name="process_email_queue", op="task"):
        emails = get_pending_emails()

        for email in emails:
            with sentry_sdk.start_span(op="email.send", description=email.subject):
                try:
                    send_email(email)
                except Exception as e:
                    sentry_sdk.capture_exception(e)
                    sentry_sdk.set_tag("email_id", email.id)

Microservices Distributed Tracing

import sentry_sdk
import requests

sentry_sdk.init(
    dsn="...",
    traces_sample_rate=1.0,
)

def call_downstream_service():
    with sentry_sdk.start_transaction(name="call_service", op="http.client"):
        # Sentry automatically propagates trace context (SDK v2+)
        headers = {
            "sentry-trace": sentry_sdk.get_traceparent(),
            "baggage": sentry_sdk.get_baggage(),
        }

        response = requests.get(
            "https://api.example.com/data",
            headers=headers
        )

        return response.json()

API Rate Limiting and Monitoring

from fastapi import FastAPI, Request
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration

sentry_sdk.init(
    dsn="...",
    integrations=[FastApiIntegration()],
    traces_sample_rate=0.1,
)

app = FastAPI()

@app.middleware("http")
async def track_api_usage(request: Request, call_next):
    with sentry_sdk.start_transaction(
        name=f"{request.method} {request.url.path}",
        op="http.server"
    ) as transaction:
        # Add request metadata
        sentry_sdk.set_tag("endpoint", request.url.path)
        sentry_sdk.set_tag("method", request.method)

        response = await call_next(request)

        # Track response status
        transaction.set_status(response.status_code)
        sentry_sdk.set_tag("status_code", response.status_code)

        return response

Database Query Performance

import sentry_sdk
from sqlalchemy import create_engine
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration

sentry_sdk.init(
    dsn="...",
    integrations=[SqlalchemyIntegration()],
    traces_sample_rate=1.0,
)

engine = create_engine("postgresql://...")

def get_user_orders(user_id):
    with sentry_sdk.start_transaction(name="get_user_orders", op="db"):
        # SQLAlchemy queries are automatically tracked
        with sentry_sdk.start_span(op="db.query", description="fetch user"):
            user = session.query(User).get(user_id)

        with sentry_sdk.start_span(op="db.query", description="fetch orders"):
            orders = session.query(Order).filter_by(user_id=user_id).all()

        # Track query performance
        sentry_sdk.set_measurement("order_count", len(orders), "none")

        return orders

Filtering and Sampling

Event Filtering

import sentry_sdk

def before_send(event, hint):
    # Don't send events for specific exceptions
    if "exc_info" in hint:
        exc_type, exc_value, tb = hint["exc_info"]
        if isinstance(exc_value, KeyboardInterrupt):
            return None  # Don't send

    # Filter out sensitive data
    if "request" in event:
        event["request"].pop("cookies", None)

    return event

sentry_sdk.init(
    dsn="...",
    before_send=before_send,
)

Sampling Configuration

import sentry_sdk
import random

def traces_sampler(sampling_context):
    # Sampling based on transaction name
    transaction_name = sampling_context.get("transaction_context", {}).get("name")

    if transaction_name == "health_check":
        return 0.0  # Never sample health checks
    elif transaction_name == "critical_api":
        return 1.0  # Always sample critical endpoints
    else:
        return 0.1  # 10% for everything else

sentry_sdk.init(
    dsn="...",
    traces_sampler=traces_sampler,
)

Ignoring Errors

import sentry_sdk
from sentry_sdk.integrations.logging import ignore_logger

sentry_sdk.init(
    dsn="...",
    ignore_errors=[
        KeyboardInterrupt,
        BrokenPipeError,
        ConnectionResetError,
    ],
)

# Ignore specific loggers
ignore_logger("django.security.DisallowedHost")
ignore_logger("urllib3.connectionpool")

Quick Reference

Initialisation Options

Option Description Default
dsn Data Source Name (project key) Required
environment Environment name (prod, staging) None
release Version identifier None
traces_sample_rate Percentage of transactions to track 0.0
profiles_sample_rate Percentage of profiles to capture 0.0
debug Enable SDK debug logging False
attach_stacktrace Add stacktraces to messages False
send_default_pii Send personally identifiable info False
max_breadcrumbs Maximum breadcrumbs to store 100

Common SDK Methods

# Capture events
sentry_sdk.capture_exception(exception)
sentry_sdk.capture_message(message, level="info")

# Context
sentry_sdk.set_user({"id": "123", "email": "user@example.com"})
sentry_sdk.set_tag("key", "value")
sentry_sdk.set_context("name", {"key": "value"})
sentry_sdk.add_breadcrumb(category="auth", message="Login", level="info")

# Performance
sentry_sdk.start_transaction(name="operation", op="task")
sentry_sdk.start_span(op="db.query", description="SELECT users")

# Scope management (SDK v2+)
sentry_sdk.new_scope()           # fork current scope for a localised block
sentry_sdk.isolation_scope()     # fork isolation scope for a full transaction
sentry_sdk.get_current_scope()   # mutate scope without a context manager

Integration Packages

# Web frameworks
sentry-sdk[flask]
sentry-sdk[django]
sentry-sdk[fastapi]
sentry-sdk[starlette]

# Async/Task queues
sentry-sdk[celery]
sentry-sdk[rq]
sentry-sdk[aiohttp]

# Databases
sentry-sdk[sqlalchemy]
sentry-sdk[redis]

# Other
sentry-sdk[beam]
sentry-sdk[boto3]
sentry-sdk[pure_eval]

Common Issues and Solutions

Events Not Appearing in Sentry

Problem: Initialised Sentry but events aren't showing up in the dashboard.

# Solution 1: Check DSN is correct
import sentry_sdk

sentry_sdk.init(
    dsn="https://key@o0.ingest.sentry.io/0",  # Verify this is correct
    debug=True,  # Enable to see SDK logs
)

# Solution 2: Ensure event is being sent before app exits
import sentry_sdk

sentry_sdk.capture_message("test")
sentry_sdk.flush(timeout=2.0)  # Wait for events to be sent

# Solution 3: Check network connectivity
# Sentry requires outbound HTTPS to *.ingest.sentry.io

Too Many Events / Rate Limiting

Problem: Hitting rate limits or generating too many events.

# Solution: Implement sampling and filtering
import sentry_sdk

def before_send(event, hint):
    # Filter noisy errors
    if "exc_info" in hint:
        exc_type, exc_value, tb = hint["exc_info"]
        if isinstance(exc_value, (KeyError, ValueError)):
            return None
    return event

sentry_sdk.init(
    dsn="...",
    traces_sample_rate=0.01,  # Sample 1% of transactions
    before_send=before_send,
    ignore_errors=[KeyboardInterrupt, ConnectionError],
)

Performance Impact

Problem: Sentry SDK affecting application performance.

# Solution: Optimise sampling and async sending
import sentry_sdk

sentry_sdk.init(
    dsn="...",
    traces_sample_rate=0.05,      # Lower sample rate
    profiles_sample_rate=0.01,    # Minimal profiling
    max_breadcrumbs=30,           # Reduce breadcrumb storage
    attach_stacktrace=False,      # Disable for messages
    # Events are sent asynchronously by default via the built-in HttpTransport.
    # Use traces_sampler / before_send for fine-grained control rather than
    # a custom transport. If you do need a custom transport, subclass
    # sentry_sdk.transport.HttpTransport rather than passing a lambda —
    # function transports are deprecated in sentry-sdk v2 and will be removed.
)

Sensitive Data in Events

Problem: PII or secrets appearing in error reports.

# Solution: Filter data before sending
import sentry_sdk
from sentry_sdk.scrubber import EventScrubber

def before_send(event, hint):
    # Remove sensitive fields
    if "request" in event:
        event["request"].pop("cookies", None)
        event["request"].pop("headers", None)

    # Scrub environment variables
    if "contexts" in event and "runtime" in event["contexts"]:
        event["contexts"]["runtime"].pop("environment", None)

    return event

sentry_sdk.init(
    dsn="...",
    send_default_pii=False,  # Disable PII by default
    before_send=before_send,
)

Integration Not Working

Problem: Framework integration not capturing errors automatically.

# Solution: Verify integration is loaded correctly
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

# Check integration is in list
sentry_sdk.init(
    dsn="...",
    integrations=[
        FlaskIntegration(),  # Must be initialised
    ],
    debug=True,  # Check logs for integration messages
)

# For Django, ensure in settings.py, not views.py
# For FastAPI, ensure both Starlette and FastAPI integrations

Grouping Issues

Problem: Similar errors creating separate issues in Sentry.

# Solution: Customise fingerprinting
import sentry_sdk

def before_send(event, hint):
    # Custom grouping logic
    if "exception" in event:
        exc = event["exception"]["values"][0]
        # Group by exception type and first line of stacktrace
        if exc.get("stacktrace", {}).get("frames"):
            frame = exc["stacktrace"]["frames"][-1]
            event["fingerprint"] = [
                exc["type"],
                frame.get("function", "unknown"),
            ]

    return event

sentry_sdk.init(
    dsn="...",
    before_send=before_send,
)

Source Maps for Minified Code

Problem: Stacktraces showing minified/compiled code locations.

# Solution: Upload source maps (for Python, ensure frames have correct paths)
# Ensure release is set and matches deployment
import sentry_sdk

sentry_sdk.init(
    dsn="...",
    release="my-app@1.2.3",  # Must match source map upload
    environment="production",
)

Transaction Name Explosion

Problem: Too many unique transaction names (e.g., with IDs in path).

# Solution: Parameterise transaction names
from flask import Flask, request
import sentry_sdk

app = Flask(__name__)

@app.before_request
def set_transaction_name():
    # Use route pattern, not actual URL with IDs
    if request.url_rule:
        sentry_sdk.set_tag("transaction", request.url_rule.rule)

# Or for manual transactions
with sentry_sdk.start_transaction(
    name="/api/users/{user_id}",  # Parameterised
    op="http.server"
):
    process_user(user_id)