Sentry
What it is
Sentry collects errors from the API, the websites and the mobile app, groups them into issues, and tells you which release (build) each first appeared in. Apps send errors to a DSN, an address specific to one Sentry project.
How to triage issues once it is running is in Sentry (DevOps). This page is the setup.
Why we do it
Without it, the first sign of a bug is a user complaint, and you cannot tell which build caused it. With release names that match the Docker image tag, an issue leads straight to a commit.
Before you start
- A Sentry organisation for the project, created with the team mailbox, owner per
OWNERSHIP.md. - The list of apps: for example
acme-api,acme-web,acme-admin,acme-mobile. - The team chat channel for alerts (Slack or Microsoft Teams).
How it fits together
One project per app, all environments inside it. Each event carries an environment
(development, staging, production), and you filter by it.
Steps
1. Organisation settings
In Sentry, Settings, Organization:
| Setting | Where | Value |
|---|---|---|
| Require 2FA | Security & Privacy | On |
| Data scrubber | Security & Privacy | On, with default scrubbers |
| Prevent storing IP addresses | Security & Privacy | On |
| Members | Members | Invite through the team mailbox domain; roles Member, Admin for leads |
2. Create one project per app
Projects, Create Project, choose the platform (Node.js, Next.js, Flutter), name it
after the repo (acme-api). Copy the DSN from Project Settings, Client Keys
(DSN).
3. Put the DSN where the app reads it
| App | Where the DSN goes |
|---|---|
| API | SSM /acme/<env>/api/SENTRY_DSN, injected by the task definition |
| Web | Build-time environment variable, for example NEXT_PUBLIC_SENTRY_DSN. A DSN is not secret; it only allows sending events |
| Mobile | A build-time define per flavor, for example --dart-define=SENTRY_DSN=... |
4. Initialise the SDK
API (Node). Load this first, before any other import:
// src/instrument.ts
import * as Sentry from '@sentry/node';
if (process.env.SENTRY_DSN && process.env.NODE_ENV !== 'development-local') {
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.SENTRY_ENVIRONMENT, // development | staging | production
release: process.env.SENTRY_RELEASE, // acme-api@<image tag>
sendDefaultPii: false,
tracesSampleRate: 0.1,
});
}
Web (Next.js): run npx @sentry/wizard@latest -i nextjs and then set the same three
values. Mobile (Flutter): sentry_flutter, initialised in main_<flavor>.dart with the
flavor's environment.
Laptops send nothing: the init is skipped locally, so a stray DSN cannot fill the quota.
5. Releases and readable stack traces from CI
- Settings, Developer Settings, Organization Tokens, create a token for CI.
Store it as a CI secret (
SENTRY_AUTH_TOKEN), never in the repo. - In the build, set the release to the image tag and upload maps or symbols:
| App | In CI |
|---|---|
| API | Release acme-api@<first 7 chars of commit>, written into the task definition |
| Web | Source maps uploaded by the Sentry build plugin; not served publicly |
| Mobile | dart run sentry_dart_plugin after the release build uploads debug symbols |
6. Alerts
Alerts, Create Alert, Issues:
| Alert | Condition | Goes to |
|---|---|---|
| New issue in production | A new issue is created, environment production | Team channel |
| Issue spike | Number of events above a threshold in 1 hour, production | Team channel and on-call |
| Regression | A resolved issue happens again | Team channel |
Install the Slack or Microsoft Teams integration from Settings, Integrations first, so alerts can post there. Keep spike protection on (it is by default).
Check it worked
From dev, trigger a test error:
curl -X POST https://api.dev.example.com/debug/sentry-test # a route that throws, dev only
Within a minute an issue appears in acme-api, environment development, with the
release name and a readable stack trace pointing at your source file.
If it goes wrong
| You see | It means |
|---|---|
| No events at all | DSN not set in that environment, or init skipped by the local check |
| Every deploy shows the same release | The release is a fixed value instead of the image tag |
| Stack traces point at minified code | Source maps or symbols were not uploaded for that release |
| Quota used up in a day | An error loop, or rejected requests (4xx) reported as events. Filter them in beforeSend |
Rules
Do not report every 4xx as an event. One scanner sending bad requests once created about 600 events. Report 5xx and unexpected errors; count 4xx as metrics.
sendDefaultPii stays false. Never attach request bodies, tokens or personal data to
events.
Done when
- Organisation with 2FA, data scrubbing, no IP storage.
- One project per app; DSNs stored per environment.
- Releases match image tags; source maps and symbols uploaded by CI.
- Alerts post to the team channel.
- A test error from dev arrived with a readable stack trace.