Error tracking
Catch every exception with its stack trace and context, and fold thousands of copies into one issue you can fix.
3 technologies on this page ↓How it works
An SDK inside the app hooks the language’s handler for uncaught exceptions, and lets you report caught ones too. For each error it sends an event: the exception type and message, the stack trace, and context such as the release, environment, user ID and request URL, plus breadcrumbs, the last few log lines, HTTP calls and clicks before the crash. The server computes a fingerprint for each event, typically from the app’s own stack frames (library frames are ignored), else from the exception type and message, else from the message alone with its parameters stripped. Events with the same fingerprint become one issue with a count, first and last seen times and the number of users hit, so a crash that happens 10,000 times is one line. Alerts go out when a new issue appears, when an issue marked fixed in one release comes back in a later one (a regression), or when a count spikes, not for every event. Minified JavaScript and stripped native binaries produce unreadable traces, so the build uploads source maps or debug symbols and the server turns frames back into original file names and line numbers. Unlike a log store, which keeps every line, an error tracker deduplicates and gives each issue a state (new, resolved, regressed, archived), much like a bug tracker.
A repair shop’s complaints desk. Five hundred customers report the same rattle; the desk files one ticket marked “500 reports, started after the March model”, with the notes from the first few customers attached, and reopens it if the rattle comes back after the fix.
Reach for it when
- Any app with users you can’t watch: web front ends, mobile apps, APIs, background jobs
- Finding which release introduced a crash, and how many users it hits
- Turning minified JavaScript or native crash dumps back into readable stack traces
Think twice when
- As your only telemetry: it sees exceptions, not slow requests, wrong answers or a service that never got the request
- Sending expected conditions, such as validation failures, as errors; they bury real issues and cost event quota
A real example
Release 4.2.0 of a web shop goes out at 14:00. Within 20 minutes TypeError: Cannot read properties of undefined (reading 'price') arrives 3,100 times from 870 users, but it is one new issue and one chat alert. The uploaded source map points at cart/total.ts:57, and the breadcrumbs show the cart API returned an empty item list just before. The fix ships in 4.2.1 and the issue is marked resolved in that release; if the same fingerprint shows up again in 4.2.3, it reopens as a regression.
Technologies
Specific products, and what each does differently.
| Commercial product | Functional Software, Inc. (Sentry) | FSL-1.1-Apache-2.0: source-available, each release becomes Apache 2.0 after two years (BSL 1.1 from 2019, BSD 3-Clause before); SDKs are MIT | 2026-10-05 |
Also used for error tracking
Products of other kinds that are often used this way, grouped by what they are.
| Observability | Commercial product | Better Stack, Inc. | Proprietary (the Better Stack collector is Apache 2.0) | 2026-10-05 | |
| Observability | Commercial product | Datadog, Inc. | Proprietary (the Datadog Agent is Apache 2.0) | 2026-10-05 |
What each provider calls it
- AWS
- No dedicated error tracker; CloudWatch RUM records client-side errors, crashes and app hangs from real user sessions
- Google Cloud
- Error Reporting, which groups errors found in Cloud Logging
- Azure
- Application Insights exception telemetry
- Open source & others
- GlitchTip (MIT, accepts Sentry SDKs); Sentry self-hosted (source-available FSL); hosted: Sentry, BugSnag (SmartBear), Rollbar, Honeybadger, Datadog Error Tracking, Better Stack Errors (accepts Sentry SDKs)
Same idea, different names#
Every tool groups occurrences into something you triage, but the words and the default rules differ. All of them let you override grouping with your own fingerprint.
| Tool | One occurrence | The group | Default grouping |
|---|---|---|---|
| Sentry | Event | Issue | The app’s own stack frames; else exception type and value; else the message |
| BugSnag | Event | Error | Error class plus file and line of the top in-project stack frame |
| Rollbar | Occurrence | Item | Its own grouping engine, with custom fingerprint rules |
| Google Cloud Error Reporting | Error event | Error group | Exception type and the five topmost frames; else the message’s first three tokens |
| Datadog Error Tracking | Error | Issue | Similar errors grouped into one issue |
- Grouping by message alone is fragile: messages often contain IDs, amounts or timestamps, so one bug splits into thousands of groups. Sentry’s docs call exception-based grouping “a lot less reliable because of changing error messages”.
In code · python
import sentry_sdk
sentry_sdk.init(dsn=SENTRY_DSN, release="shop@4.2.0", environment="production")
sentry_sdk.set_user({"id": "42"})
sentry_sdk.add_breadcrumb(category="cart", message="cart API returned 0 items", level="info")
def charge(order):
try:
return payments.charge(order)
except PaymentTimeout as err:
with sentry_sdk.new_scope() as scope:
scope.fingerprint = ["payments", "timeout"] # every timeout: one issue
sentry_sdk.capture_exception(err)
raise
# uncaught exceptions are captured automaticallyOften confused with
Where it came from1999–2024
Before error trackers, a crash was a line in a log or an email. Microsoft hit the problem first at scale. Windows Error Reporting, in use since 1999 and built into every Windows system since XP, collected crash reports from about a billion computers. Its 2009 paper explains that, collected naively, the reports “would absolutely overwhelm programmers”, so WER sorted them into buckets, aiming for “one bug per bucket and one bucket per bug”, and used the bucket counts to decide what to fix first.
Web frameworks had the same flood on a smaller scale. Rails’ Exception Notifier plugin reported errors by email, and Django sent an email for every error, so a busy site filled inboxes with copies of one crash. In May 2008 David Cramer made the first commit of django-db-log, which stored exceptions in a database and showed them in aggregate; it grew into Sentry. In July 2008 thoughtbot launched Hoptoad, a hosted replacement for Exception Notifier where you could sort errors by project and mark them resolved; a trademark complaint made it Airbrake in 2011.
As front ends moved to minified JavaScript, stack traces pointed at line 1 of a one-line file. Source maps, first created by Joseph Schorr for Google’s Closure Inspector, map compiled code back to the original, and one of the format’s stated goals is server-side stack trace deobfuscation. After years as an informal Google Doc, the format became the Ecma standard ECMA-426 in December 2024.
Why it’s built this way
- Group by stack trace, not by message
- Messages carry IDs and values that differ on every occurrence. The code path that failed is a far more stable identity for “the same bug”, which is why WER, Sentry, BugSnag and Google all key on stack frames first.
- Only the app’s own frames count
- Two crashes that differ only inside a framework or library are usually one bug in your code. Sentry groups only by frames the SDK marks as in-app, and BugSnag uses the top in-project frame.
- Capture context at the moment of the crash
- By the time someone looks, the request is gone. Breadcrumbs, release, user and environment travel with the event, so the issue page answers “what was happening?” without reproducing it.
- Resolve against a release
- Marking an issue fixed in version 4.2.1 lets the tracker ignore stragglers still running 4.2.0 and flag a real regression when the same fingerprint appears in a newer version.
Timeline
- 1999
Windows Error Reporting goes into use, bucketing crash reports
- 2008
First commit of
django-db-log, later Sentry (May) - 2008
thoughtbot launches Hoptoad, a hosted error catcher for Rails (July)
- 2009
Microsoft publishes ten years of WER experience at SOSP
- 2011
Hoptoad is renamed Airbrake
- 2024
Source maps become the Ecma standard ECMA-426
- Glerum et al.: Debugging in the (Very) Large: Ten Years of Implementation and Experience (SOSP 2009)
- Microsoft Research: Debugging in the (Very) Large, abstract
- Sentry blog: Sentry turns 9, by David Cramer (May 2017)
- GitHub: first commit, djangodblog (12 May 2008)
- thoughtbot: Ride the Toad, Hoptoad launch (29 July 2008)
- thoughtbot: Hoptoad is now Airbrake (15 July 2011)
- ECMA-426: Source map format specification (draft, with history)
- Ecma International: ECMA-426, 1st edition (December 2024)
Sources19
- Sentry docs: Issue grouping (fingerprints, stack trace, exception, message)
- Sentry docs: Issue status (new, regressed, resolve in release)
- Sentry docs: Python SDK fingerprinting (new_scope, scope.fingerprint)
- Sentry docs: Python breadcrumbs
- Sentry docs: Python, identify users
- Sentry docs: Python configuration options (release, environment)
- Sentry docs: JavaScript source maps
- BugSnag docs: Error grouping
- Rollbar docs: Grouping (occurrences and items)
- Honeybadger docs: Error tracking
- Datadog docs: Error Tracking
- Google Cloud docs: How Error Reporting groups errors
- Microsoft Learn: Application Insights for .NET and Node.js, exception reporting
- AWS docs: CloudWatch RUM (client-side errors, crashes, app hangs)
- Better Stack docs: Errors (Sentry SDK compatible)
- GlitchTip docs: error tracking and uptime monitoring, open source
- GlitchTip docs: Sentry-compatible SDKs
- GitLab: glitchtip-backend (MIT License)
- GitHub: Sentry LICENSE.md (FSL-1.1-Apache-2.0)