feat(sentry): error tracking, performance monitoring, and session replay#280
Open
Just-Insane wants to merge 27 commits intodevfrom
Open
feat(sentry): error tracking, performance monitoring, and session replay#280Just-Insane wants to merge 27 commits intodevfrom
Just-Insane wants to merge 27 commits intodevfrom
Conversation
Contributor
|
OpenTofu plan for production Plan: 2 to add, 0 to change, 2 to destroy.OpenTofu used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
OpenTofu will perform the following actions:
# cloudflare_worker_version.site must be replaced
-/+ resource "cloudflare_worker_version" "site" {
!~ annotations = {
+ workers_message = (known after apply)
+ workers_tag = (known after apply)
!~ workers_triggered_by = "create_version_api" -> (known after apply)
} -> (known after apply)
!~ assets = { # forces replacement
!~ asset_manifest_sha256 = "b44bbf6a507cd60278d078f529f5e4b4bfe36580139ecb1ff4fc842de520526f" -> "dabe9263bcfbd3acf488419df0f30df7f446fd96b3307ddca33789bb563681ce" # forces replacement
!~ directory = "/home/runner/work/Sable/Sable/dist" -> "/github/workspace/dist"
# (1 unchanged attribute hidden)
}
+ compatibility_flags = (known after apply)
!~ created_on = "2026-03-14T23:24:54Z" -> (known after apply)
!~ id = "************************************" -> (known after apply)
+ limits = (known after apply)
+ main_script_base64 = (known after apply)
!~ number = 112 -> (known after apply)
!~ source = "terraform" -> (known after apply)
+ startup_time_ms = (known after apply)
# (4 unchanged attributes hidden)
}
# cloudflare_workers_deployment.site must be replaced
-/+ resource "cloudflare_workers_deployment" "site" {
!~ annotations = { # forces replacement
!~ workers_message = "b90555a27c7dac104a96bf0e2e5b66c2c815da2f" -> "feat(sentry): error tracking, performance monitoring, and session replay"
!~ workers_triggered_by = "deployment" -> (known after apply)
}
!~ author_email = "cloudflare@sl.sable.moe" -> (known after apply)
!~ created_on = "2026-03-14T23:24:55Z" -> (known after apply)
!~ id = "************************************" -> (known after apply)
!~ source = "terraform" -> (known after apply)
!~ versions = [ # forces replacement
!~ {
!~ version_id = "************************************" -> (known after apply)
# (1 unchanged attribute hidden)
},
]
# (3 unchanged attributes hidden)
}
Plan: 2 to add, 0 to change, 2 to destroy.
Warning: Attribute Deprecated
with cloudflare_workers_custom_domain.site,
on main.tf line 41, in resource "cloudflare_workers_custom_domain" "site":
41: environment = "production"
This attribute is deprecated.
(and one more similar warning elsewhere)📝 Plan generated in Cloudflare Infra #53 |
47151fc to
39a4ca1
Compare
Add @sentry/react and wire instrument.ts into the app entry point. - Environment detection: production (10%), preview (100%), development (100%); driven by VITE_SENTRY_ENVIRONMENT env var - SessionReplay with full privacy masking (text, media, inputs) - Performance tracing and profiling enabled - Data scrubbing for Matrix tokens and user IDs - Exposes Sentry on window.Sentry for console-level debugging
- SentrySettings panel: capture events manually, feedback widget, session replay controls, and diagnostic test buttons - Wire SentrySettings into DevelopTools tab - debugLogger: attach buffered logs to Sentry events via breadcrumbs, extra data, and file attachment for full context on error reports
- DefaultErrorPage: show Sentry event ID, offer one-click feedback submission for in-field crash reports - BugReportModal: prefer Sentry feedback dialog over GitHub issues when Sentry is configured; include all bug report fields - App.tsx: wrap router in Sentry ErrorBoundary for automatic capture of unhandled React rendering errors
Add Sentry spans and custom metrics throughout the critical paths: - ClientRoot: set user context (hashed MXID) and identify session - ClientNonUIFeatures: instrument push notification handling - DirectDMsList: DM sync performance metrics - RoomTimeline: spans for jump-load and pagination with direction and encryption attributes; sable.timeline.jump_load_ms metric - RoomInput: message send span with threading context - EncryptedContent: decryption latency span per event - matrix.ts / room.ts: Matrix API call instrumentation - slidingSync.ts: sliding sync cycle performance spans
39a4ca1 to
660f8a4
Compare
- docs/SENTRY_INTEGRATION.md: comprehensive guide covering setup, environment variables, sampling rates, privacy configuration, and self-hosting considerations - cloudflare-web-deploy.yml: pass VITE_SENTRY_DSN and VITE_SENTRY_ENVIRONMENT=production to deploy workflow - prepare-tofu/action.yml: forward Sentry env vars through OpenTofu infrastructure actions - changesets for Sentry integration and improved crash page
660f8a4 to
d9bffa6
Compare
…red log attrs
- vite.config.ts: enable reactComponentAnnotation in sentryVitePlugin so React
component names appear in breadcrumbs, spans, and replay search instead of
raw minified CSS selectors
- debugLogger.ts: include flattened primitive fields from entry.data as
searchable attributes in Sentry.logger.* structured log calls; previously
only category/namespace were passed, dropping all contextual data
- instrument.ts: add beforeSendLog to scrub Matrix IDs/tokens from structured
log messages in production; drop debug-level logs in production; set global
scope attributes (app.name, app.version) on all events/logs
- loginUtil.ts: wrap login() in Sentry.startSpan({op: auth}) to track login
latency and attach auth.method/auth.error/auth.success span attributes;
covers both password and SSO token login paths
- Router.tsx: add scoped Sentry.ErrorBoundary with beforeCapture section tags
around auth routes (section=auth) and client routes (section=client) for
better error grouping and filtering in Sentry
slidingSync.ts: - Add `sync.initial` span from attach() to first SlidingSyncState.Complete, with attributes sync.cycles_to_ready and sync.rooms_at_ready. Replaces the misleading sable.sync.initial_ms metric which previously measured only the processing time within the lifecycle callback (effectively ~0ms) rather than the actual wall-clock wait from attach() to first data. - Fix sable.sync.initial_ms to use attachTime (wall-clock from attach()) so the metric reflects real user-perceived latency. - Add sable.sync.lists_loaded_ms distribution (attach() to all lists fully loaded) and sable.sync.total_rooms gauge emitted when listsFullyLoaded first becomes true. - Add sable.sync.active_subscriptions gauge in subscribeToRoom / unsubscribeFromRoom to track how many rooms have active subscriptions. - Wrap probe() static method in Sentry.startSpan (sync.probe, op=matrix.sync) with probe.supported span attribute. - Wrap startSpidering() loop in Sentry.startSpan (sync.spidering, op=matrix.sync) with spidering.batches and spidering.total_rooms attributes. initMatrix.ts: - Add sable.sync.transport count metric in startClassicSync and sliding sync active path, with transport, reason, and fallback attributes. Allows filtering Sentry dashboards by sync transport in use.
Add Sentry metrics to surface orphaned resources, growing caches, and
unexpectedly long waits before they become user-visible problems.
src/app/hooks/useBlobCache.ts
- Export getBlobCacheStats() returning { cacheSize, inflightCount } from
the module-level Maps so callsites can observe cache growth over time.
src/app/state/callEmbed.ts
- Import Sentry and track embedCreatedAt when a CallEmbed is promoted.
- On disposal or replacement emit sable.call.embed_lifetime_ms (tagged
disposed/replaced) to detect embeds whose dispose path was skipped.
src/client/initMatrix.ts
- Instrument waitForClientReady: emit sable.sync.client_ready_ms
distribution with timed_out attribute on every call path.
- When the timeout fires add a warning-level breadcrumb so a stuck-sync
state is visible in the context of related error events.
src/app/pages/client/ClientNonUIFeatures.tsx
- Add HealthMonitor component rendered inside ClientNonUIFeatures.
60s setInterval emits:
sable.media.blob_cache_size (gauge) - cached object-URL count
sable.media.inflight_requests (gauge) - inflight fetches when > 0
Adds a warning breadcrumb when inflightCount >= 10 (stuck fetches).
src/app/pages/client/BackgroundNotifications.tsx
- Add Sentry import.
- Emit sable.background.client_count gauge after each add and remove of
a background Matrix client for multi-account visibility.
- beforeBreadcrumb: replace overly-broad single-char patterns ('@','!','$')
with precise Matrix ID regexes that always apply to every breadcrumb message,
ensuring @user:server, !room:server and $eventId are consistently redacted
rather than only when the broad check happened to fire
- beforeSend: fingerprint MatrixError instances by their errcode so that
M_FORBIDDEN, M_NOT_FOUND, M_LIMIT_EXCEEDED etc. land in distinct Sentry
issues instead of all being merged under the same stack-trace group
- SentryRoomContextFeature: new component that tracks lastVisitedRoomIdAtom
and writes a 'room' context (type, encrypted, member_count_range) plus
room_type/room_encrypted tags to the scope whenever the active room changes;
these appear on every subsequent error captured while the room is open
- setUser: add serverDomain as 'username' alongside the hashed user ID so
issues can be segmented by homeserver without exposing personal identifiers
…lures - SyncStatus: add Sentry breadcrumb + sable.sync.degraded metric count when SyncState transitions to Reconnecting (warning) or Error (error); includes previous state in breadcrumb data for context - ClientRoot useLogoutListener: capture breadcrumb before clearing stores on HttpApiEvent.SessionLoggedOut (server-side forced session expiry/revocation) so the event appears in Sentry before the page reloads and session is lost - useKeyBackup useKeyBackupSync: add error breadcrumb + sable.crypto.key_backup_failures metric (keyed by errcode) when CryptoEvent.KeyBackupFailed fires, giving visibility into E2E key backup reliability
…ilures - EncryptedContent: include event.decryptionFailureReason as an attribute on sable.decryption.failure so UTD spikes can be triaged by cause (MISSING_ENCRYPTION_KEY, UNKNOWN_MESSAGE_INDEX, SENDER_IDENTITY_VERIFICATION_VIOLATION, etc.) rather than a single bucket - initMatrix wipeAllStores: add warning breadcrumb + sable.crypto.store_wipe metric when a store mismatch forces a local db wipe-and-retry; fires for both buildClient and initRustCrypto mismatch paths - RoomTimeline handleLocalEchoUpdated: emit sable.message.send_failed count metric when a local echo transitions to EventStatus.NOT_SENT, giving visibility into outbound message delivery failures - loginUtil login(): emit sable.auth.login_failed count metric keyed by errcode alongside the existing span attribute so login pressure is visible in Metrics without querying trace data
…rrors, verification outcomes
…ation docs with full metrics reference
- Tag all Sentry events with PR number via VITE_SENTRY_PR env var
(instrument.ts: setTag('pr', prNumber) on global scope)
- cloudflare-web-preview.yml: inject Sentry DSN, environment=preview,
PR number, and source-map secrets into build env for PR runs
- New workflow sentry-preview-issues.yml: on every PR push, query Sentry
for unresolved issues tagged with the PR number and environment=preview;
create a GitHub issue per unique error (deduplicated by sentry-id marker),
labelled 'sentry-preview' + 'pr-{N}'; maintain a sticky PR comment
with a summary table; reopen closed issues on regression
- Labels allow filtering: -label:sentry-preview hides all automated issues
- instrument.ts: remove useless escape \[ in character class regexes - matrix.ts, RoomInput.tsx: fix @sentry/react import order (third-party must precede local imports per import-x/order) - debugLogger.ts: replace for..of with Object.entries().forEach() (no-restricted-syntax) - ClientNonUIFeatures.tsx: replace nested ternary with if/else for memberCountRange (no-nested-ternary) - SentrySettings.tsx: remove useless Fragment wrapper; replace template literal with string literal where no interpolation is needed - Prettier format: apply auto-formatting to all touched files
Updated quotes in SENTRY_INTEGRATION.md for consistency.
# Conflicts: # src/app/pages/client/sidebar/DirectDMsList.tsx
- Add docs/PRIVACY_POLICY.md with full privacy policy including data collection details, third-party services (Sentry), and user controls - Add 'Diagnostics & Privacy' section to General settings with Error Reporting and Session Replay toggles (now accessible without enabling Developer Tools) - Remove basic toggles from SentrySettings in Developer Tools; keep only advanced/developer config (breadcrumbs, metrics, debug log export) - Add note in SentrySettings pointing to General for main toggles - Update settings path references in SENTRY_PRIVACY.md and SENTRY_INTEGRATION.md from 'Developer Tools' to 'General'
…ion values - Add .replace(/(\/preview_url)\?[^#\s]*/gi, '$1') to scrubMatrixUrl() so the full external URL in ?url= is stripped from preview_url breadcrumbs and spans - Apply scrubMatrixUrl() to exception.value strings in beforeSend so embedded URLs in MatrixError messages (e.g. 'Got error 403 (/preview_url?url=etsy.com/...)') are also redacted before sending to Sentry
Contributor
Deploying with
|
| Status | Preview URL | Commit | Alias | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! | https://pr-280-sable.raspy-dream-bb1d.workers.dev | 99ff314 | pr-280 |
Sun, 15 Mar 2026 20:29:02 GMT |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds comprehensive Sentry error tracking, performance monitoring, and session replay to Sable.
Commits
1. feat(sentry): initialize Sentry SDK with environment-based sampling
Adds
@sentry/reactand wiresinstrument.tsinto the app entry point. Environment-based sampling rates (production 10%, preview/dev 100%), full session replay with privacy masking, performance tracing, and data scrubbing for Matrix tokens and user IDs.2. feat(sentry): developer settings panel and debug log attachment
Adds a
SentrySettingspanel under Developer Tools for manually capturing events, using the feedback widget, and controlling session replay. ThedebugLoggernow attaches its buffered log buffer to Sentry events via breadcrumbs, extra data, and a file attachment for full diagnostic context on crash reports.3. feat(sentry): crash page and bug report Sentry integration
DefaultErrorPageshows the Sentry event ID and offers one-click feedback submission.BugReportModalprefers the Sentry feedback dialog over GitHub issues when Sentry is configured.App.tsxwraps the router in a SentryErrorBoundaryfor automatic capture of unhandled React render errors.4. feat(sentry): performance instrumentation across sync and messaging
Sentry spans and custom metrics across the critical paths:
ClientRoot(user context via hashed MXID),ClientNonUIFeatures(push notifications),DirectDMsList(DM sync),RoomTimeline(jump-load and pagination latency withsable.timeline.jump_load_msmetric),RoomInput(message send with threading context),EncryptedContent(per-event decryption latency),matrix.ts/room.ts(Matrix API calls),slidingSync.ts(sync cycle spans).5. docs(sentry): privacy information, integration guide, and CI environment configuration
Comprehensive
docs/SENTRY_INTEGRATION.mdcovering setup, environment variables, sampling rates, privacy configuration, and self-hosting. CI workflows passVITE_SENTRY_DSNandVITE_SENTRY_ENVIRONMENTto Cloudflare deploy and OpenTofu actions.Setup
Sentry is opt-in: the integration is a no-op unless
VITE_SENTRY_DSNis set at build time. Self-hosters who do not set this variable are unaffected.Environment variable reference:
VITE_SENTRY_DSNVITE_SENTRY_ENVIRONMENTproduction(10% sampling) orpreview(100%)See docs/SENTRY_INTEGRATION.md for full details.