Tune Sentry Sampling for Django Without Losing Important Data
Control Sentry volume and cost for Django: keep error sample_rate at 1.0, set traces_sample_rate or a traces_sampler that keeps slow and critical transactions, drop health checks and noisy errors with before_send, and use rate limits and spike protection.
Problem statement
Sentry's quota runs out mid-month, or the bill jumps after enabling performance monitoring. Health check transactions, bot traffic, and one noisy error dominate the volume, while the transactions you care about (checkout, signup, slow admin pages) are sampled away. Sentry lets you choose per event what to keep: errors, traces, and profiles each have their own sampling controls, and filters can drop noise before it is sent.
- Keep
sample_rate=1.0for errors; reduce error noise withignore_errorsandbefore_sendinstead. - Replace a flat
traces_sample_ratewith atraces_samplerthat drops health checks and keeps important or slow transactions at higher rates. - Drop static file and bot transactions, and group or ignore expected exceptions (
Http404, client disconnects). - Set project rate limits and spike protection in Sentry so a runaway loop cannot consume the quota.
- Sample profiles relative to traces with
profiles_sample_rateorprofile_session_sample_rate.
Step-by-step solution
1. Keep errors, filter noise
import sentry_sdk
from django.core.exceptions import DisallowedHost
def before_send(event, hint):
exc = hint.get("exc_info", (None, None, None))[1]
if isinstance(exc, (BrokenPipeError, ConnectionResetError)):
return None # client went away
return event
sentry_sdk.init(
dsn=os.environ["SENTRY_DSN"],
sample_rate=1.0,
ignore_errors=[DisallowedHost],
before_send=before_send,
)
Dropping error events by sampling hides real bugs; filtering by type keeps everything that matters. Use Sentry's inbound filters (browser extensions, legacy browsers, localhost) for frontend projects.
2. Sample traces by importance
def traces_sampler(ctx):
if ctx.get("parent_sampled") is not None:
return ctx["parent_sampled"] # follow upstream decisions
path = (ctx.get("wsgi_environ") or {}).get("PATH_INFO", "")
if path.startswith(("/healthz", "/static/", "/favicon.ico")):
return 0.0
if path.startswith(("/checkout/", "/accounts/signup/")):
return 1.0
if path.startswith("/api/"):
return 0.1
return 0.05
sentry_sdk.init(dsn=..., traces_sampler=traces_sampler)
Celery task transactions arrive with celery_job in the context; sample them by task name the same way.
3. Reduce span noise
Each trace can contain hundreds of spans (one per SQL query, cache call, or template). That is useful, but large transactions with N+1 patterns consume quota quickly. Fix the N+1 queries themselves, and consider disabling integrations you do not need, such as Redis spans if they add little.
4. Protect the quota
In Sentry's project settings, set rate limits per DSN key (events per minute) and enable spike protection. A deploy that introduces an exception in every request then cannot use the month's quota in an hour. Alerts on event volume let you react quickly.
5. Profiles and releases
Profiling multiplies data volume. Sample profiles as a fraction of sampled traces:
sentry_sdk.init(dsn=..., traces_sampler=traces_sampler, profiles_sample_rate=0.2)
Set release and environment so you can compare error rates per release and filter staging out of production views.
Explanation
The Sentry SDK makes sampling decisions at the start of each transaction (for traces) and for each error event. sample_rate applies to errors, traces_sample_rate or traces_sampler to transactions, and profile sampling to profiled transactions. A sampler function sees context such as the WSGI environment or Celery task, so it can assign rates by route, user type, or importance. Filters drop events after creation but before sending, which saves quota and network.
Edge cases / notes
- Dynamic sampling. Sentry's server-side dynamic sampling adjusts what is retained on some plans; SDK sampling still controls what is sent.
- Distributed tracing. Frontend and backend should agree; honoring
parent_sampledkeeps traces complete across services. - Self-hosted. Self-hosted Sentry has no quota, but storage and processing still scale with volume.
Next steps
- Start from a solid setup with Set Up Sentry for Django in Production.
- If nothing arrives after tuning, see Fix Sentry Not Receiving Errors From Django.
- Apply the same thinking to logs with Reduce Django Log Volume and Costs.
FAQ
Should I sample Sentry errors?
Usually not. Keep sample_rate=1.0 and filter noisy error types instead.
What is traces_sampler?
A function that returns a sample rate per transaction, letting you keep important routes and drop noise.
How do I stop health checks from using quota?
Return 0 for health check paths in traces_sampler.
How do I avoid a runaway error using all my quota?
Set rate limits per DSN key and enable spike protection in the Sentry project.