How do I monitor errors and slow requests in a Python app (Django or FastAPI) in production?
Install sentry-sdk and call sentry_sdk.init as early as possible with a DSN, a release, an environment and a traces_sample_rate below 1.0 for busy services. The Django and FastAPI integrations enable automatically, capture unhandled exceptions, and create one transaction per request, so slow endpoints appear when you sort transactions by duration.
Why it happens
Python web apps fail in two different ways, and they need different signals. Exceptions are discrete events: a view raises, a task crashes, a database call times out. Slowness is continuous: nothing throws, but an endpoint takes four seconds because of an N+1 query or a blocked event loop. A traceback in a log file captures neither well. It gives you one line per failure with no grouping, and no timing at all for the requests that succeed slowly.
Error monitoring and performance monitoring therefore collect different data. Errors are captured when an exception reaches the SDK. Performance data comes from tracing: each request becomes a transaction made of spans such as the view, database queries and outgoing calls, with durations. Because recording every request is expensive at volume, tracing is sampled, which is why a sample rate is one of the first decisions you make.
Both depend on the SDK being initialised early. Frameworks such as Django and FastAPI are instrumented when the SDK starts, and asyncio programs need the event loop to be running when the integration is set up. Most missing-data problems in production come from initialising too late, in the wrong process, or with a sample rate of zero.
How to monitor errors and slow requests in Django or FastAPI
Install the SDK and initialise it as early as possible
Run pip install sentry-sdk. Sentry's Django and FastAPI guides both say configuration should happen as early as possible in the application's lifecycle: in Django that is settings.py, in FastAPI the entry point before the app is created (Sentry docs, read 2026-11-13). Error monitoring starts working as soon as sentry_sdk.init is called. Read the DSN from an environment variable so it is not committed.
import os import sentry_sdk sentry_sdk.init( dsn=os.environ["SENTRY_DSN"], release=os.environ.get("GIT_SHA"), environment=os.environ.get("APP_ENV", "production"), traces_sample_rate=0.1, )Rely on the framework integration, and know what it reports
If django or fastapi is installed, the matching integration is enabled automatically when you initialise the SDK. The Django guide says it monitors the middleware stack, signals, database queries, Redis commands and cache access; the FastAPI guide lists middleware, database queries and Redis commands. FastAPI reports exceptions that lead to a 500 by default, and the failed_request_status_codes option changes which status codes count. Request data such as method, URL, headers and form or JSON payloads is attached, with raw bodies and multipart uploads excluded.
Choose a tracing sample rate for production
traces_sample_rate is the fraction of requests recorded as transactions. Sentry's own example uses 1.0 and notes that the value should be reduced in production. Start at 0.1 for a busy service or 1.0 for a low-traffic one, and lower it if volume or overhead grows. Slow-endpoint conclusions come from the sampled transactions, so keep enough traffic in the sample to compare endpoints with confidence.
Handle async programs and custom spans correctly
For plain asyncio programs, add AsyncioIntegration to the integrations list and call sentry_sdk.init at the beginning of the first async function you call, because the integration instruments the running event loop (Sentry asyncio docs, read 2026-11-13). Outside a supported framework, spans are only created inside an existing transaction, so a standalone script or worker needs a transaction started around the unit of work before custom spans show anything.
import asyncio, sentry_sdk from sentry_sdk.integrations.asyncio import AsyncioIntegration async def main(): sentry_sdk.init(dsn=..., traces_sample_rate=0.1, integrations=[AsyncioIntegration()]) with sentry_sdk.start_transaction(op="task", name="nightly-report"): with sentry_sdk.start_span(op="db", name="load orders"): ... asyncio.run(main())Connect your logging
Per the Django guide, logs emitted by any logger are recorded as breadcrumbs by the logging integration, which is enabled by default, so the lines leading up to a crash appear on the error. Keep secrets out of log messages, because breadcrumbs travel with the event. To send log records as structured logs, the docs say to set capture_sentry_logs on the logging integration.
Tag the release and environment
release and environment are options on init; the SDK also reads SENTRY_RELEASE and SENTRY_ENVIRONMENT from the environment, and environment defaults to production (Sentry Python options, read 2026-11-13). Set the release to your commit SHA so a regression can be tied to a deploy, and set a different environment for staging so test noise stays out of production views.
Find slow endpoints, and add profiling only if you need it
In your tracker's transactions or performance view, sort transactions by duration (or p95 if offered) and open the slowest. The span waterfall shows whether time went to the database, an outgoing call or your own code. Transactions are named by URL pattern by default in both frameworks, so /catalog/product/{product_id} groups all products. Profiling is optional and goes deeper into which functions burn time; enable it after tracing shows a slow endpoint, not before.
| Symptom | Likely cause | Check in the transaction |
|---|---|---|
| One endpoint is slow, others are fine | N+1 queries or an unindexed query | Many repeated database spans under one transaction |
| All endpoints slow at the same time | Saturated workers or a slow dependency | Long gaps with no child spans, or one shared outgoing call |
| Slow only on first request after deploy | Cold start or cache warm-up | Compare the first transactions of a release with later ones |
| Async endpoint stalls other requests | Blocking call inside an async function | A long span with no I/O wait and other requests delayed |
| No transactions appear at all | traces_sample_rate is 0 or unset, or init ran too late | Print the option, and confirm init runs before the app is created |
Compiled from the Sentry Django, FastAPI, asyncio and options documentation (docs.sentry.io, read 2026-11-13) and common failure modes; it makes no vendor-specific claims.
When to use Bugsly
Bugsly accepts events from the standard sentry-sdk, so the code above works unchanged; only the DSN changes, to the one from your Bugsly project. It ingests errors, transactions and spans, and can also receive logs and profiles. The get_project_stats tool returns transaction performance statistics for a project over a number of hours, so an assistant can list your slowest endpoints without opening the dashboard. Errors are grouped into issues and a recurrence after resolving is flagged as a regression.
Bugsly is an error tracker with transaction data, not a replacement for infrastructure monitoring of hosts and databases. Performance data comes from your tracing sample, so a low sample rate means coarser numbers. See the Python, Django and FastAPI integration pages for setup details.
When Bugsly is not the right answer
If you only need request timing and already run Prometheus or a cloud provider's monitoring on the load balancer, you may not need a tracer at all; structured request logs with duration and a log-based percentile query will answer which endpoint is slow. If you need host metrics, database internals or distributed tracing across many services you already instrument with OpenTelemetry, a dedicated observability platform fits better than an error tracker. If your data must stay on your own servers, Bugsly is not an option because it cannot be self-hosted.
Ask your AI assistant to do it
With the Bugsly MCP server connected (https://bugsly.dev/api/mcp; setup for ChatGPT, Claude, Cursor and Gemini CLI is on /mcp), paste this. The assistant reads first and reports back; you decide what happens next.
Assign to Bugsly: use list_projects to find my Python project, then get_project_stats for the last 24 hours and tell me which three transactions have the highest p95 duration. Then use list_issues to show unresolved errors on the same endpoints, and get_issue on the most frequent one. Tell me whether the slow endpoints and the errors overlap. Do not resolve or ignore anything; show me what you found so I can approve the next step.
Frequently asked questions
Do I need to add Django or FastAPI integrations manually?
Not normally. Sentry's guides say the integration enables automatically when the framework package is installed and the SDK is initialised. You add it explicitly only to change its options, such as transaction_style.
What traces_sample_rate should I use in production?
Sentry's examples use 1.0 and advise reducing it in production. A starting point is 0.1 for a busy service and 1.0 for a quiet one, then adjust to your traffic and the precision you need.
Why do my custom spans never show up?
Outside a supported framework, spans are only created inside an existing transaction. Wrap the unit of work in a transaction first, and make sure tracing is enabled with a non-zero sample rate.
Do I need profiling to find slow endpoints?
No. Transactions sorted by duration, plus the span waterfall, usually point at the slow query or call. Profiling is an optional second step for slow code inside a function.
Sources
- Sentry docs: Django (read 2026-11-13)
- Sentry docs: FastAPI (read 2026-11-13)
- Sentry docs: asyncio (read 2026-11-13)
- Sentry docs: Python options (read 2026-11-13)
- Sentry docs: Python (read 2026-11-13)
Related
- Bugsly Python integration
- Bugsly Django integration
- Bugsly FastAPI integration
- Best performance monitoring for Python in 2026
- All problems and solutions
Sentry is a trademark of its owner; Bugsly is not affiliated with or endorsed by Sentry. “Sentry-compatible” only means Bugsly accepts events sent by Sentry's open SDKs.