Error Tracking with Sentry

The essential playbook for implementing error tracking with sentry in your SaaS.

Use Sentry to capture unhandled exceptions, trace failures by release, and keep enough context to fix production issues quickly. For a small SaaS, the baseline setup is: initialize Sentry early, set environment and release consistently, instrument backend and worker entrypoints first, then verify alerts before launch.

This page focuses on practical setup and troubleshooting for missing, duplicated, or noisy Sentry events across app, worker, and frontend services.

Quick Fix / Quick Setup

Start with backend error capture first. Add release and environment tags immediately. Keep tracing disabled until basic error reporting is stable.

Python backend

bash
# Flask/FastAPI
pip install sentry-sdk[flask,fastapi]

Flask example

python
import os
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn=os.getenv("SENTRY_DSN"),
    integrations=[FlaskIntegration()],
    environment=os.getenv("APP_ENV", "production"),
    release=os.getenv("APP_RELEASE", "unknown"),
    traces_sample_rate=0.0,
    send_default_pii=False,
)

FastAPI example

python
import os
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration

sentry_sdk.init(
    dsn=os.getenv("SENTRY_DSN"),
    integrations=[StarletteIntegration(), FastApiIntegration()],
    environment=os.getenv("APP_ENV", "production"),
    release=os.getenv("APP_RELEASE", "unknown"),
    traces_sample_rate=0.0,
    send_default_pii=False,
)

Trigger a test event

python
from sentry_sdk import capture_exception

try:
    1 / 0
except Exception as e:
    capture_exception(e)

Frontend optional setup

bash
npm install @sentry/browser @sentry/tracing
ts
import * as Sentry from "@sentry/browser";

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  environment: import.meta.env.VITE_APP_ENV,
  release: import.meta.env.VITE_APP_RELEASE,
  tracesSampleRate: 0.0,
});

Required environment variables

bash
SENTRY_DSN=https://<key>@o<org>.ingest.sentry.io/<project>
APP_ENV=production
APP_RELEASE=$(git rev-parse --short HEAD)
app/worker/frontend
Sentry SDK
Sentry project
alert channel

Process Flow

What’s happening

  • Sentry receives exceptions, stack traces, request metadata, and release/environment tags from your app.
  • If Sentry is installed but no events appear, the common causes are bad DSN, wrong environment variables, blocked outbound HTTPS, filtered events, or initialization happening too late.
  • If too many events appear, duplicate exception capture, logging integration noise, health checks, and expected application errors are usually the cause.
  • For a small SaaS, instrument these three surfaces first:
    • API app
    • background worker
    • frontend

Step-by-step implementation

1. Create projects and copy DSNs

Use separate Sentry projects when possible:

  • backend-api
  • worker
  • frontend-web

This reduces noise and makes ownership clearer.

2. Store DSNs in environment variables

Do not hardcode DSNs.

bash
# backend
SENTRY_DSN=https://<backend-key>@o<org>.ingest.sentry.io/<project>

# frontend
VITE_SENTRY_DSN=https://<frontend-key>@o<org>.ingest.sentry.io/<project>

# shared
APP_ENV=production
APP_RELEASE=git-sha-or-image-tag

3. Initialize Sentry as early as possible

Initialize before routes, middleware, or worker job registration.

FastAPI app entrypoint

python
import os
import sentry_sdk
from fastapi import FastAPI
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration

sentry_sdk.init(
    dsn=os.getenv("SENTRY_DSN"),
    integrations=[StarletteIntegration(), FastApiIntegration()],
    environment=os.getenv("APP_ENV", "production"),
    release=os.getenv("APP_RELEASE", "unknown"),
    traces_sample_rate=0.0,
    send_default_pii=False,
)

app = FastAPI()

@app.get("/error-test")
def error_test():
    raise RuntimeError("Sentry API test")

Flask app entrypoint

python
import os
import sentry_sdk
from flask import Flask
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn=os.getenv("SENTRY_DSN"),
    integrations=[FlaskIntegration()],
    environment=os.getenv("APP_ENV", "production"),
    release=os.getenv("APP_RELEASE", "unknown"),
    traces_sample_rate=0.0,
    send_default_pii=False,
)

app = Flask(__name__)

@app.route("/error-test")
def error_test():
    raise RuntimeError("Sentry Flask test")

4. Set environment values explicitly

Use one format everywhere:

bash
APP_ENV=development
APP_ENV=staging
APP_ENV=production

Do not mix values like prod, Prod, live.

5. Set release from CI/CD

Use deploy identifiers such as:

  • Git SHA
  • Docker image tag
  • CI build number

Example:

bash
APP_RELEASE=${GITHUB_SHA}

Docker example:

dockerfile
ARG APP_RELEASE=unknown
ENV APP_RELEASE=$APP_RELEASE

Runtime example:

bash
docker run -e APP_ENV=production -e APP_RELEASE=$(git rev-parse --short HEAD) ...

6. Initialize workers separately

Background workers need their own initialization.

Celery example

python
import os
import sentry_sdk
from celery import Celery

sentry_sdk.init(
    dsn=os.getenv("SENTRY_DSN"),
    environment=os.getenv("APP_ENV", "production"),
    release=os.getenv("APP_RELEASE", "unknown"),
    traces_sample_rate=0.0,
    send_default_pii=False,
)

celery_app = Celery("worker")

@celery_app.task
def failing_task():
    raise RuntimeError("Sentry worker test")

If jobs are short-lived, flush before exit:

python
import sentry_sdk

try:
    raise RuntimeError("job failed")
except Exception as e:
    sentry_sdk.capture_exception(e)
    sentry_sdk.flush(timeout=5)

7. Add user context carefully

Do not send PII by default.

python
sentry_sdk.set_user({"id": str(user.id)})

Only add email or IP after privacy review and operational need.

8. Filter expected exceptions

Use before_send to drop noise.

python
import os
import sentry_sdk
from werkzeug.exceptions import NotFound, Unauthorized

def before_send(event, hint):
    exc_info = hint.get("exc_info")
    if exc_info:
        exc_type, exc_value, tb = exc_info
        if isinstance(exc_value, (NotFound, Unauthorized)):
            return None
    return event

sentry_sdk.init(
    dsn=os.getenv("SENTRY_DSN"),
    environment=os.getenv("APP_ENV", "production"),
    release=os.getenv("APP_RELEASE", "unknown"),
    before_send=before_send,
    traces_sample_rate=0.0,
    send_default_pii=False,
)

9. Add correlation IDs as tags

This makes Sentry events easier to match with logs.

python
import sentry_sdk

request_id = "abc-123"
sentry_sdk.set_tag("request_id", request_id)

Pair this with structured logs. See Logging Setup.

10. Verify end-to-end from every surface

Test all of these:

  • API exception
  • worker failure
  • frontend exception

Frontend test:

ts
throw new Error("Sentry frontend test");

Use this configuration matrix to validate each reporting surface before launch:

SurfaceDSN variableAPP_ENV / environment tagAPP_RELEASE sourceSampling baselineFlush needed before exit
Backend API (Flask/FastAPI)SENTRY_DSNproduction / staging / development (consistent naming)CI SHA or image tag injected at deploytraces_sample_rate=0.0 initiallyUsually no (long-running process)
Worker (Celery/RQ/cron)SENTRY_DSN (worker project preferred)Same value scheme as APISame deploy identifier as API for correlationtraces_sample_rate=0.0 initiallyYes for short-lived jobs: sentry_sdk.flush(timeout=5)
Frontend web appVITE_SENTRY_DSN (or framework-specific public DSN var)Public env value mapped to same env namingBuild-time release matching backend deploytracesSampleRate=0.0 initiallyNot typically required; SDK lifecycle handles browser flush

Common causes

  • SENTRY_DSN is unset or points to the wrong project.
  • Sentry SDK initializes too late in the app lifecycle.
  • Worker process does not initialize Sentry separately.
  • Frontend DSN or build env variable is missing at compile time.
  • Outbound network or firewall blocks HTTPS requests to Sentry.
  • before_send or logger filters drop events unexpectedly.
  • Events are duplicated by both logging integration and explicit capture.
  • Release/environment tags are missing or inconsistent.
  • Short-lived jobs exit before the SDK flushes events.
  • CSP or ad blockers prevent browser events from being sent.

Debugging tips

Check environment variables inside the running process

bash
printenv | grep SENTRY
printenv | grep APP_

If using Docker:

bash
docker exec -it <container_name> printenv | grep SENTRY
docker logs <container_name> --tail 200

If using systemd:

bash
journalctl -u <service_name> -n 200 --no-pager

Send a manual test event from the same environment

bash
python -c "import os, sentry_sdk; sentry_sdk.init(dsn=os.getenv('SENTRY_DSN'), environment=os.getenv('APP_ENV','production'), release=os.getenv('APP_RELEASE','debug'), debug=True); 1/0"

Message test:

bash
python - <<'PY'
import os
import sentry_sdk
from sentry_sdk import capture_message

sentry_sdk.init(
    dsn=os.getenv('SENTRY_DSN'),
    environment=os.getenv('APP_ENV','production'),
    release=os.getenv('APP_RELEASE','debug'),
    debug=True
)
capture_message('sentry test message')
sentry_sdk.flush()
print('sent')
PY

Verify outbound connectivity

bash
curl -I https://sentry.io

For self-hosted Sentry, test your own domain instead.

Check worker health

Celery:

bash
celery -A <app_path> inspect ping

RQ:

bash
rq info

Look for duplicate capture paths

If duplicate issues appear, check whether both of these are active:

  • automatic framework/logging integration
  • manual capture_exception(...)

Check for buffering or process exit issues

For jobs, scripts, and short-lived worker processes, call:

python
sentry_sdk.flush(timeout=5)

Compare logs against Sentry timestamps

Correlate timestamps and request IDs to determine whether the event was:

  • never emitted
  • blocked by filters
  • delayed by buffering
  • sent from the wrong project or environment

troubleshooting decision tree for no events, duplicate events, and too many events.

What kind of webhook event problem are you seeing?
No events
Check webhook endpoint registration, Stripe dashboard event logs, and firewall rules
Duplicate events
Implement idempotency keys — check if the handler is processing the same event_id twice
And too many events
Diagnose: and too many events

Checklist

  • SENTRY_DSN is present in web, worker, and frontend environments where needed.
  • Sentry initializes before app routes and worker jobs start.
  • APP_ENV uses consistent values across all services.
  • APP_RELEASE is set from CI/CD or git SHA.
  • A manual test exception appears in the correct Sentry project.
  • No sensitive data is sent unintentionally.
  • Expected errors are filtered out.
  • Alerts are configured for production only.
  • Server logs include correlation IDs or request IDs.
  • Worker processes flush events before exit if short-lived.
  • Frontend build exposes the DSN correctly if browser reporting is enabled.
  • Browser CSP does not block Sentry ingestion endpoints.

Related guides

FAQ

Do I need separate Sentry projects for API, frontend, and workers?

Usually yes. Separate projects reduce noise, simplify ownership, and make alerts more useful.

Why are no events showing up even though the SDK is installed?

Most often the DSN is wrong, the SDK initializes too late, or the event is filtered before send. Verify from inside the running process with a manual test exception.

Should I send user emails and IPs to Sentry?

Not by default. Start with send_default_pii=False and only add user context after privacy review and a clear operational need.

How do I connect Sentry issues to a deployment?

Set a consistent release value from CI/CD, such as a git SHA or image tag, on every service that reports to Sentry.

When should I enable performance monitoring?

After error tracking is working cleanly. Start with a low traces sample rate to control volume and cost.

Final takeaway

A working Sentry setup is not just installing the SDK. The minimum production-ready setup is:

  • early initialization
  • correct DSN, environment, and release
  • low-noise reporting
  • verification from every process that can fail

If you only do three things, do this:

  1. initialize early
  2. tag releases
  3. send a deliberate test exception from web and worker after every deployment

For broader production readiness, pair this with: