Backend / Observability / 01_sentry.md

Sentry

Updated 5 interview angles 4 min read source
On this page8
  1. Grouping, and how to break it
  2. Releases are what make it actionable
  3. Context beats verbosity
  4. PII: on by default in the ways that matter
  5. Sampling and cost
  6. What it is not
  7. Related
  8. Interview angle

Sentry

Error tracking. It captures an exception with its stack trace, request context and release, then groups repeated occurrences into one issue you can prioritise by frequency and users affected.

That grouping is the product. Without it you have a log of a million tracebacks; with it you have twelve issues ranked by impact.

python
import sentry_sdk

sentry_sdk.init(
    dsn=os.environ["SENTRY_DSN"],
    environment=os.environ["ENV"],
    release=os.environ["GIT_SHA"],
    traces_sample_rate=0.1,
    send_default_pii=False,
)

The integrations do the wiring — Django, FastAPI, Celery and friends hook themselves in, so unhandled exceptions arrive with the request, the user and the transaction already attached.

Grouping, and how to break it

Sentry groups an event into an issue by its stack trace when it has one (the in-app frames, normalised), falling back to exception type and value only when there is no stack trace, and to the message, with parameters stripped where it can, for message events. So raise ValueError(f"No account for {user_id}") from one place is still one issue: the stack is the same.

Over-splitting comes from things that change the stack: frames marked in-app that should not be (vendored code, generated wrappers), minified JavaScript without source maps, or messages captured with capture_message that embed ids. Keep ids out of messages anyway, as context, so they are searchable:

python
sentry_sdk.set_context("account", {"user_id": user_id})
sentry_sdk.capture_message("Account lookup failed")       # one message issue

When the default grouping is wrong, set the fingerprint on a scope (sentry-sdk 2.x; push_scope and configure_scope are deprecated):

python
with sentry_sdk.new_scope() as scope:
    scope.set_tag("provider", name)
    scope.fingerprint = ["{{ default }}", name]   # split one issue per provider
    sentry_sdk.capture_exception(exc)

Server-side fingerprint and stack-trace rules in the project settings do the same without a deploy.

Grouping too coarsely is the opposite failure — three unrelated bugs under one issue, where fixing one closes it and hides the others.

Releases are what make it actionable

python
release=os.environ["GIT_SHA"]

With a release set, Sentry tells you which deploy introduced an error and marks it regressed if it comes back. Without one, every error is undated and “is this new?” becomes a manual investigation.

Upload source maps for JavaScript and debug symbols for compiled languages in CI, tagged with the same release, or the stack traces are minified noise.

Context beats verbosity

Mechanism Use for Searchable
Tags low-cardinality facets yes, indexed
Context structured detail no
Breadcrumbs what happened before no
User who hit it yes

Tags are for things you filter by — provider, plan, region. The same cardinality discipline as metrics applies: a tag per user id is not a facet, it is a leak.

Breadcrumbs are the underused one. They are the trail of events leading to the error — queries, HTTP calls, log lines — and they usually answer “what was this request doing” faster than the stack trace does.

PII: on by default in the ways that matter

Error trackers accumulate personal data fast. Request bodies, headers, querystrings and local variables all routinely contain it.

  • send_default_pii=False — do not attach user IP and cookies automatically.
  • before_send — scrub or drop events in code, which is the only reliable place to strip a bespoke field.
  • Server-side scrubbing as a second layer, because a client can be misconfigured.
python
def before_send(event, hint):
    req = event.get("request", {})
    req.pop("cookies", None)
    if "data" in req:
        req["data"] = "[redacted]"
    return event

Under GDPR the retention window is also a decision, not a default.

Sampling and cost

Errors are captured independently of tracing, governed by sample_rate (default 1.0): you want all of them. Traces are not: traces_sample_rate=1.0 in production is a large bill and a large amount of noise. Start at 0.1, and use traces_sampler to sample by route (more for checkout, none for health checks).

What a traces_sampler cannot do is keep “every errored or slow trace”: it decides when the transaction starts, before the outcome is known. Keeping those needs tail sampling, for example the OpenTelemetry Collector’s tail_sampling processor (Sampling Strategies for Tracing).

What it is not

Not a metrics platform and not a log store, despite overlapping with both. Ask it “how many 500s per second” and you are using the wrong tool — that is Prometheus. Sentry answers “which exception, in which release, hitting how many users, with what stack”.

Interview angle 5

  • “What is Sentry for, and what isn’t it?” - error tracking: aggregating exceptions with stack traces, request context and release information so you can prioritise by impact. It’s not a metrics or logging platform, though it now overlaps with tracing.
  • “Why does grouping matter?” - it turns thousands of events into a handful of issues ranked by frequency and users affected. Bad grouping - usually from dynamic values in the message - produces noise nobody triages.
  • “How do you avoid leaking PII?” - scrub sensitive fields before send, disable default PII capture, and be deliberate about request bodies and headers. Error trackers accumulate personal data quickly if left on defaults.
  • “Why set a release?” - it ties an error to the deploy that introduced it and lets Sentry mark regressions. Without it every error is undated, and source maps have nothing to attach to, so JavaScript traces stay minified.
  • “Would you sample?” - errors no, traces yes. Full trace sampling in production is expensive and noisy; sample traces by route with traces_sampler, and if you need every errored or slow trace, use tail sampling in a collector, since the SDK decides before the outcome is known.