5.4 KiB
0076 - Reduce Sentry Event Volume
Date: 2026-04-02
Status
Proposed — needs ADR-0078 to be implemented first
Context
Sentry is receiving too many low-value events, making it hard to triage real errors and draining quota. Four root causes were identified:
-
logTracesends Sentry events._shouldReportToSentryincludesLevel.trace, which is called on every scroll, cache hit, and token validation — high-frequency diagnostics with no actionable value. -
RemoteExceptionThrowercallslogErrorbefore re-throwing. EveryDioException— including expected ones like no internet, connection timeout, and HTTP 401 — is sent to Sentry before being re-thrown as a typed exception. -
SendEmailExceptionThrowercallslogErrorfor network loss. Loss of connectivity is a normal user-facing condition, not an application bug. -
tracesSampleRateandprofilesSampleRateare set to 1.0 (100%). Given JMAP's high request frequency (push, sync, email fetch), 100% sampling rapidly consumes quota.
Decision
1. Remove Level.trace from Sentry reporting
logTrace calls will no longer create Sentry events. Trace context is preserved as Sentry Breadcrumbs — stored locally and attached automatically to the next error event (see ADR-0078 for the implementation mechanism).
2. Log expected network errors as warnings, not errors
In RemoteExceptionThrower, replace the blanket logError at the top of throwException with per-branch logging:
NoNetworkError,ConnectionTimeout,ConnectionError→logWarning- HTTP 401 (
BadCredentialsException) → no log (handled by auth retry flow) - HTTP 403, 404, 429 and other unlisted 4xx →
logWarning(client errors, not application bugs) - HTTP 500, 502 and other 5xx →
logWarning - Unknown HTTP status or unrecognised error →
logError(genuinely unexpected; should appear in Sentry)
In SendEmailExceptionThrower, replace logError for no realtime network with logWarning.
3. Add ignoredExceptions as a safety net
Configure the Sentry SDK to drop expected exception types at the SDK level. This acts as a second line of defence if a call site accidentally logs them as errors:
options.addIgnoredExceptionType(NoNetworkError);
options.addIgnoredExceptionType(ConnectionTimeout);
options.addIgnoredExceptionType(ConnectionError);
options.addIgnoredExceptionType(SocketError);
4. Drop HTTP 4xx and 5xx events in beforeSend
Extend _beforeSendHandler to return null for HTTP 4xx and 5xx events by default.
This is a second line of defence: Decision 2 already routes these to logWarning (which never creates Sentry events), but beforeSend filtering guards against future regressions where a call site accidentally uses logError for an expected HTTP error.
Exception: authentication-critical 4xx events must be retained. Specifically, HTTP 400 responses from the refresh-token and retry-flow paths in AuthorizationInterceptors are genuine failure signals that require developer visibility. These call sites must tag the event so beforeSend allows them through:
logError(
'Refresh token request failed',
exception: exception,
extras: {'auth_critical': true},
);
The beforeSend handler checks for extras['auth_critical'] == true and skips the filter for those events. This allowlist must be documented in code alongside the beforeSend implementation so future maintainers do not inadvertently suppress or re-add these events.
Note: the AuthorizationInterceptors call sites described in the Unchanged section below require a one-line update to add this tag — they are not truly unchanged, just unchanged in log level.
5. Reduce performance sampling rates
Lower tracesSampleRate and profilesSampleRate from 1.0 to 0.1 in SentryConfig. 10% sampling is sufficient to detect performance regressions without exhausting quota.
Consequences
Benefits:
- Sentry events reflect genuine, actionable bugs — not connectivity noise or routine diagnostics.
- Quota savings from both event filtering and reduced sampling.
- Trace context (from
logTrace) remains available in Sentry, attached to actual error events.
Trade-offs:
- Routine network errors (
NoNetworkError,ConnectionTimeout) are no longer visible in Sentry. UselogWarning+ LogTracking export for analysis if needed. - New
logErrorcall sites must be intentional — only use when the error requires developer action.
Unchanged:
FlutterError.onError,PlatformDispatcher.instance.onError, andrunZonedGuardedcontinue to uselogError— unhandled errors must always be monitored.AuthorizationInterceptorslogErrorfor refresh token failure (HTTP 400) and retry failure remains.
Logging Guidelines
| Situation | Log function | Sentry result |
|---|---|---|
| App crash / unhandled exception | logError / logCritical |
✅ Event |
| Refresh token failure, retry failure | logError + auth_critical: true tag |
✅ Event (allowlisted in beforeSend) |
| Unknown HTTP status or unrecognised error | logError |
✅ Event |
| HTTP 4xx (excl. auth-critical), network loss, timeout | logWarning |
❌ Not sent |
| HTTP 5xx (known: 500, 502; unknown: other 5xx) | logWarning |
❌ Not sent |
| Diagnostic info (scroll, cache, counts) | logTrace |
🔶 Breadcrumb only |
| Normal flow information | logInfo |
❌ Not sent |