shoc-frontend-new/docs/adr/0002-sentry-observability.md
2026-09-03 17:12:49 -03:00

5.2 KiB

0002. Browser error and transaction telemetry with Sentry

Status

Accepted

Context

The SeaHaven admin SPA has no centralized browser error reporting or distributed transaction tracing. Operational failures therefore have to be reconstructed from user reports and isolated backend logs. We need browser navigation and API request transactions to join the backend trace while preserving the existing UI, routing, authentication, and server-state behavior. Every emitted transaction must also identify its exact build, trace, transaction, operation, function, and opaque actor without sending credentials, request bodies, query values, or personal data.

Decision

Use the pinned @sentry/react 10.73.0 SDK. Initialize it before the application router is created, and wrap createBrowserRouter with Sentry's current React Router compatibility API. Configure the browser tracing integration with a 100% transaction sample rate, and propagate trace headers only to the SPA origin and the configured VITE_API_URL base. Pass Sentry.reactErrorHandler() as the onCaughtError, onUncaughtError, and onRecoverableError callbacks of ReactDOM.createRoot, so React 19's root error hooks capture caught, uncaught, and recoverable component errors as Sentry events.

All shared API helpers, direct Ky calls, vendor-link fetch calls, and vendor document XMLHttpRequest calls run inside explicit http.client transactions. They use normalized route templates and attach code.function, operation.type, HTTP method, trace_id, and transaction_id. Browser navigation transactions receive the same searchable trace identifiers in beforeSendTransaction. Authenticated sessions set only the application's opaque numeric/string user ID; logout and session expiry clear it.

The DSN comes from VITE_SENTRY_DSN. It is intentionally treated as public browser configuration because every VITE_ value is embedded in the built JavaScript. .env.production commits the public DSN of the real shoc-frontend Sentry project, so dev and staging deploys are live; a missing or blank DSN in any other build leaves telemetry inactive without preventing the application from starting. Default personally identifiable information collection remains disabled.

The Sentry environment label comes from VITE_SENTRY_ENVIRONMENT when set and non-blank, falling back to the Vite build mode otherwise. The committed .env.production labels dev deploys development, the staging deploy job overrides the label to staging, and a future production pipeline can set production without code changes.

Every deployment embeds shoc-frontend@<40-character commit SHA> as the Sentry release and exposes the same SHA as the app.commit tag. CI fails if it cannot resolve that identity. Vite creates hidden source maps, and the dev/staging deployment workflows upload them to the private shoc-frontend Sentry project using the repository's SENTRY_AUTH_TOKEN secret. The public S3 sync explicitly excludes *.map; the upload credential is never available to the browser build.

Before-send processors reduce request data to normalized URL plus HTTP method, reduce users to opaque ID only, attach native trace/transaction identifiers as searchable tags, and reduce breadcrumbs to safe network method/status/normalized URL metadata. Root and child span attributes use the same allowlist, retaining function, operation, actor type, normalized route, method/status, service host, timing, and native IDs while removing raw URLs and query values. Bodies, headers, cookies, query strings/values, free-form breadcrumb messages, usernames, email addresses, IP addresses, tokens, and filenames are not intentionally sent. sendDefaultPii remains disabled.

Alternatives considered

  • Manual error and timing calls — rejected because they would miss route transitions and distributed request context, and would require every feature to maintain its own instrumentation.
  • Propagate tracing headers to every request — rejected because third-party requests must not receive SeaHaven trace metadata and broad propagation can introduce cross-origin request failures.
  • Publish source maps with the SPA — rejected because source code belongs in Sentry's private artifact store, not the public application bucket.

Consequences

  • Browser navigations and API requests are recorded as connected transactions when the deployment supplies a valid DSN.
  • Events are labeled by VITE_SENTRY_ENVIRONMENT (development on dev deploys, staging on the staging job), falling back to the Vite build mode when unset.
  • Every transaction is sampled. This meets the current requirement but increases event volume and should be reviewed against Sentry quota and retention after real traffic is measured.
  • Cross-origin API tracing depends on the backend accepting sentry-trace and baggage request headers; the current API CORS policy allows request headers.
  • Deployed stack traces resolve against the exact commit's private source maps once the scoped upload secret is present and the deployment workflow runs.
  • Rollback is isolated: remove @sentry/react, the initialization module and import, restore the unwrapped router factory, and remove the Sentry env entries.