Zum Hauptinhalt springen

Sentry

Error Tracking and Performance Monitoring

Sentry provides real-time error tracking and performance monitoring for all Amply applications.

Overview

ComponentSentry Project
Backend APIamply-backend
Dashboardamply-dashboard
Public Websiteamply-public
Widgetsamply-widgets

Backend Integration

Installation

pip install sentry-sdk[fastapi,celery,sqlalchemy]

Configuration

# src/amply/core/sentry.py
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.integrations.redis import RedisIntegration

def init_sentry(dsn: str, environment: str):
"""Initialise Sentry SDK."""
sentry_sdk.init(
dsn=dsn,
environment=environment,
traces_sample_rate=0.1, # 10% of transactions
profiles_sample_rate=0.1, # 10% of profiled transactions

integrations=[
FastApiIntegration(transaction_style="endpoint"),
CeleryIntegration(),
SqlalchemyIntegration(),
RedisIntegration(),
],

# Don't send PII
send_default_pii=False,

# Filter sensitive data
before_send=filter_sensitive_data,

# Release tracking
release=f"amply-backend@{settings.version}",
)

def filter_sensitive_data(event, hint):
"""Remove sensitive data before sending to Sentry."""
# Remove API keys from headers
if 'request' in event and 'headers' in event['request']:
headers = event['request']['headers']
if 'X-Api-Key' in headers:
headers['X-Api-Key'] = '[REDACTED]'
if 'Authorization' in headers:
headers['Authorization'] = '[REDACTED]'

# Remove sensitive body data
if 'request' in event and 'data' in event['request']:
data = event['request'].get('data', {})
if isinstance(data, dict):
for key in ['password', 'card_number', 'cvv', 'secret']:
if key in data:
data[key] = '[REDACTED]'

return event

FastAPI Integration

# src/amply/main.py
from fastapi import FastAPI
from amply.core.sentry import init_sentry

app = FastAPI()

@app.on_event("startup")
async def startup():
init_sentry(
dsn=settings.sentry_dsn,
environment=settings.environment,
)

Custom Context

import sentry_sdk

def process_donation(donation: Donation, user: User):
"""Process donation with Sentry context."""
with sentry_sdk.push_scope() as scope:
# Add context for debugging
scope.set_tag("organisation_id", donation.organisation_id)
scope.set_tag("donation_type", donation.type)
scope.set_user({"id": user.id, "email": user.email})
scope.set_context("donation", {
"id": donation.id,
"amount": str(donation.amount),
"currency": donation.currency,
})

try:
# Process donation
result = _process_donation(donation)
return result
except Exception as e:
sentry_sdk.capture_exception(e)
raise

Manual Error Capture

import sentry_sdk

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

# Capture message
sentry_sdk.capture_message("Unusual activity detected", level="warning")

# Add breadcrumb
sentry_sdk.add_breadcrumb(
category="payment",
message="Initiated Stripe charge",
level="info",
data={"amount": amount, "currency": currency},
)

Celery Integration

# src/amply/jobs/config.py
from celery import Celery
import sentry_sdk
from sentry_sdk.integrations.celery import CeleryIntegration

# Sentry captures task failures automatically
app = Celery('amply')

@app.task(bind=True, max_retries=3)
def process_webhook(self, webhook_id: str):
"""Process webhook with automatic Sentry tracking."""
try:
# Task logic
pass
except Exception as e:
# Sentry captures this automatically
raise self.retry(exc=e, countdown=60)

Frontend Integration

Dashboard (React)

// src/sentry.ts
import * as Sentry from "@sentry/react";

Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,

integrations: [
new Sentry.BrowserTracing({
tracePropagationTargets: [
"localhost",
"api.amply-impact.org",
],
}),
new Sentry.Replay({
maskAllText: true,
blockAllMedia: true,
}),
],

tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,

beforeSend(event) {
// Scrub sensitive data
if (event.request?.data) {
delete event.request.data.password;
delete event.request.data.cardNumber;
}
return event;
},

release: `amply-dashboard@${__APP_VERSION__}`,
});

Error Boundary

// src/components/ErrorBoundary.tsx
import * as Sentry from "@sentry/react";

export const SentryErrorBoundary = Sentry.withErrorBoundary(
({ children }) => children,
{
fallback: ({ error, resetError }) => (
<ErrorFallback error={error} onReset={resetError} />
),
showDialog: true,
}
);

// Usage in App.tsx
function App() {
return (
<SentryErrorBoundary>
<Router>
<Routes />
</Router>
</SentryErrorBoundary>
);
}

User Context

// After successful login
import * as Sentry from "@sentry/react";

function onLoginSuccess(user: User) {
Sentry.setUser({
id: user.id,
email: user.email,
// Don't include PII like name
});
}

function onLogout() {
Sentry.setUser(null);
}

Widget Integration

// widgets/src/sentry.ts
import * as Sentry from "@sentry/browser";

Sentry.init({
dsn: WIDGET_SENTRY_DSN,
environment: process.env.NODE_ENV,

// Minimal sampling for widgets (high volume)
tracesSampleRate: 0.01, // 1%

// Only capture errors
integrations: [
new Sentry.BrowserTracing({
tracePropagationTargets: ["api.amply-impact.org"],
}),
],

// Tag with host site
beforeSend(event) {
event.tags = {
...event.tags,
host_origin: window.location.origin,
};
return event;
},
});

Performance Monitoring

Custom Transactions

import sentry_sdk

def generate_checkpoint():
"""Generate checkpoint with performance tracking."""
with sentry_sdk.start_transaction(
op="task",
name="generate_checkpoint",
) as transaction:

with transaction.start_child(op="db", description="fetch entries"):
entries = fetch_entries_since_last_checkpoint()

with transaction.start_child(op="compute", description="compute merkle root"):
merkle_root = compute_merkle_root(entries)

with transaction.start_child(op="sign", description="sign checkpoint"):
signature = sign_checkpoint(merkle_root)

with transaction.start_child(op="upload", description="upload to S3"):
upload_checkpoint(checkpoint)

Custom Spans

// Frontend performance tracking
import * as Sentry from "@sentry/react";

async function loadDashboard() {
const transaction = Sentry.startTransaction({
name: "Load Dashboard",
op: "navigation",
});

const fetchSpan = transaction.startChild({
op: "http",
description: "Fetch organisation data",
});

const data = await fetchOrganisationData();
fetchSpan.finish();

const renderSpan = transaction.startChild({
op: "render",
description: "Render dashboard",
});

renderDashboard(data);
renderSpan.finish();

transaction.finish();
}

Alerts and Notifications

Alert Rules

AlertConditionAction
New errorFirst occurrenceSlack
High volume> 100 errors/hourSlack + Email
Critical errorProduction + paymentPagerDuty
Performance regressionP95 > 2sSlack

Slack Integration

Sentry → Project Settings → Integrations → Slack
Channel: #amply-alerts

Issue Assignment

Auto-assign based on file path:
- src/amply/payments/* → payments-team
- src/amply/ledger/* → core-team
- widgets/* → frontend-team

Environments

EnvironmentDSNSample Rate
Productionamply-backend (prod)10%
Stagingamply-backend (staging)100%
DevelopmentLocal onlyN/A

Release Tracking

Backend Releases

# In CI/CD
export SENTRY_AUTH_TOKEN=xxx
export SENTRY_ORG=amply
export SENTRY_PROJECT=amply-backend

# Create release
sentry-cli releases new "amply-backend@${VERSION}"

# Associate commits
sentry-cli releases set-commits "amply-backend@${VERSION}" --auto

# Finalise
sentry-cli releases finalize "amply-backend@${VERSION}"

Source Maps

# Upload frontend source maps
sentry-cli releases files "amply-dashboard@${VERSION}" \
upload-sourcemaps ./dist \
--url-prefix '~/'

Data Retention

Data TypeRetention
Error events90 days
Transactions90 days
Attachments30 days
Session replays30 days

Cost

TierPriceErrors/month
Developer (Free)$05,000
Team$26/month50,000
Business$80/month100,000

Related: