Skip to content

feat: report errors to Sentry from all three runtimes - #493

Merged
RobotHanzo merged 2 commits into
mainfrom
feat/sentry
Sep 13, 2026
Merged

RobotHanzo merged 2 commits into
mainfrom
feat/sentry

Conversation

@RobotHanzo

Copy link
Copy Markdown
Contributor

Wires Sentry into the browser app, the SSR webserver on Cloud Run, and the Cloud Functions. Full rationale is in the new Error reporting (Sentry) section of CLAUDE.md.

Runtime SDK Entry point DSN source
Browser @sentry/vue src/boot/sentry.ts Build time, inlined via build.defineEnv
SSR webserver @sentry/node (src-ssr/package.json) src-ssr/sentry.ts Cloud Run env, from .apphosting/service.template.yaml
Cloud Functions @sentry/node functions/src/sentry.ts functions/.env, appended by the deploy workflow

Notable choices

  • Inert without a DSN. The client guards fold to constants, so Rolldown drops @sentry/vue out of the bundle entirely — verified: with no DSN the built boot file is literally ({ app, router }) => {}.
  • Tracing and Session Replay default to 0, at which they are not merely unsampled but never bundled (~15 KB gz and ~50 KB gz saved).
  • SSR capture is explicit in src-ssr/middlewares/render.ts. That middleware catches everything itself and never calls next(err), so an Express error handler would never see a failed render. Redirect and route-not-found rejections are filtered out as normal control flow.
  • Functions are instrumented by import, not by editing 16 handlers: functions/src/sentry.ts re-exports onCall / onRequest / onDocumentWritten with identical signatures plus a try/catch. HttpsErrors with caller-fault codes (permission-denied, invalid-argument, …) are deliberately not reported — checkRole() throws those at people who simply are not allowed to do the thing.
  • The Sentry lines are appended to functions/.env by the workflow rather than stored inside the FUNCTIONS_ENV secret, so one SENTRY_DSN secret configures all three runtimes and rotating it does not mean hand-editing an opaque blob.

Verification

  • yarn build (SSR) and yarn spa-build green — which is the typecheck + lint gate. Built both with and without a DSN.
  • cd functions && yarn build && yarn lint green.
  • Function wrappers exercised directly: pass-through works, permission-denied rethrows in ~1 ms without flushing, a generic error reports and rethrows.
  • Both workflows and the rendered service.yaml parse as valid YAML with the variables set and unset; the generated functions/.env parses with GMAIL_* intact.

Config already applied to this repo

  • Secret SENTRY_DSN; variables SENTRY_ORG=cksc, SENTRY_PROJECT=legislation, and the three sample rates at 0.
  • Still needed: SENTRY_AUTH_TOKEN (Sentry → Settings → Auth Tokens, scope project:releases). Until it exists, source maps are not uploaded and production stack traces stay minified.

🤖 Generated with Claude Code

RobotHanzo and others added 2 commits September 14, 2026 00:15
Adds @sentry/vue for the browser, @sentry/node for the SSR webserver on
Cloud Run, and @sentry/node for the Cloud Functions. Each reads its own
DSN — the browser's is inlined at build time via build.defineEnv, the
other two read process.env at runtime — so they can point at one Sentry
project or three.

Every piece is inert without a DSN: the client guards fold to constants
and Rolldown drops @sentry/vue out of the bundle entirely, so a build
with no Sentry env in scope ships what it shipped before.

- SSR render failures are captured explicitly in the render middleware,
  which catches everything itself and never calls next(err), so an
  Express error handler would never see one.
- Functions are instrumented by re-exporting onCall / onRequest /
  onDocumentWritten with the firebase-functions signatures plus a
  try/catch, leaving all 16 handlers untouched. HttpsErrors carrying
  caller-fault codes are deliberately not reported.
- notifyError() reports next to the existing GA4 exception event, and
  updateCustomClaims() mirrors the Firebase user and roles onto the
  Sentry scope.
- Tracing and Session Replay default to a sample rate of 0, at which
  they are not merely unsampled but never bundled.
- Source maps upload only when SENTRY_AUTH_TOKEN + SENTRY_ORG +
  SENTRY_PROJECT are all set; the plugin deletes them from dist/ before
  the deploy, so they never reach visitors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sentry/vite-plugin throws by default when release creation or the
sourcemap upload fails, which aborts `quasar build`. That put a Sentry
outage, an expired auth token or a rate limit in the critical path of
deploying the site itself — for the sake of stack traces being readable.

An errorHandler downgrades it to a warning. Verified against a rejected
token: both failures log and the build still succeeds, and the plugin
deletes the generated maps either way, so nothing can leak.

The merge workflow also sweeps any stray *.js.map before the asset
shuffle. That normally removes nothing, but it makes "no source maps on
the CDN" a property of the deploy rather than of one plugin's cleanup.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@RobotHanzo
RobotHanzo merged commit 40a90f2 into main Sep 13, 2026
2 checks passed
@RobotHanzo
RobotHanzo deleted the feat/sentry branch September 13, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant