From 244a121e980ded2258dd4e89dc4d522e7a4dd668 Mon Sep 17 00:00:00 2001 From: jason5ng32 Date: Thu, 3 Sep 2026 10:55:10 +0800 Subject: [PATCH] Chore(sentry): skip Sentry init in dev runs A local pnpm dev with a DSN in .env reported every half-finished edit against the production projects. The frontend gate now also checks import.meta.env.DEV; the backend bootstrap skips init under SENTRY_ENVIRONMENT=development, mirroring the cron check-in rule. Co-Authored-By: Claude Fable 5.1 --- api/AGENTS.md | 4 +++- frontend/AGENTS.md | 4 +++- frontend/main.js | 18 ++++++++++-------- sentry-instrument.js | 8 ++++++-- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/api/AGENTS.md b/api/AGENTS.md index a4d34e10..0d016c71 100644 --- a/api/AGENTS.md +++ b/api/AGENTS.md @@ -60,7 +60,9 @@ currently `dns-resolvers.js`, the country-annotated resolver list behind Root-level `sentry-instrument.js` (loaded via `node --import` *before* express, so ESM loader hooks can auto-instrument route tracing) does the init; `backend-server.js` attaches `setupExpressErrorHandler` after the - routes. No `SENTRY_DSN_BACKEND` → `@sentry/node` never loads. Handlers + routes. No `SENTRY_DSN_BACKEND` → `@sentry/node` never loads; + `SENTRY_ENVIRONMENT=development` skips the init, so a local run reports + nothing (same rule the cron check-ins already follow). Handlers never import Sentry or capture manually: uncaught throws and 5xx traces are automatic; caught failures stay on the logger — a hook in `common/logger.js` mirrors warn+ to Sentry Logs and elevates error+ to diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 0094ce1e..369e59f2 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -76,7 +76,9 @@ replaces the map, and Home clears it on unmount — shortcuts are home-route onl ### Error monitoring (Sentry) is env-gated and invisible to app code No `VITE_SENTRY_DSN_FRONTEND` → no Sentry code in the bundle at all -(build-time-gated dynamic import, like `firebase-init.js`). Rules: +(build-time-gated dynamic import, like `firebase-init.js`). The same gate in +`main.js` skips the load under `import.meta.env.DEV`, so a `pnpm dev` run +reports nothing even with a DSN in `.env`. Rules: - **Never import `@sentry/vue` in app code** — a static import drags the SDK into the main bundle. All config lives in `sentry-init.js`. diff --git a/frontend/main.js b/frontend/main.js index 6b84c8cf..559b9e6e 100644 --- a/frontend/main.js +++ b/frontend/main.js @@ -66,16 +66,18 @@ const store = useMainStore(pinia); app.use(i18n); app.use(router); -// Sentry — build-time env gate: without the DSN the chunk neither ships nor -// loads. The SDK chunk stays OFF the boot critical path: it loads after -// mount (see the mount chain's finally below) so it never competes with the -// locale pack the first render waits on. Until init, a tiny buffer catches -// uncaught errors / rejections — and any of them triggers an immediate -// load, so a boot that never reaches mount still reports. Perf data -// survives the late init (buffered observers, backdated pageload span). +// Sentry — build-time env gate: without the DSN, or in a dev server run, the +// chunk neither ships nor loads (local runs would otherwise report every +// experiment against the production project). The SDK chunk stays OFF the +// boot critical path: it loads after mount (see the mount chain's finally +// below) so it never competes with the locale pack the first render waits +// on. Until init, a tiny buffer catches uncaught errors / rejections — and +// any of them triggers an immediate load, so a boot that never reaches mount +// still reports. Perf data survives the late init (buffered observers, +// backdated pageload span). const earlyErrors = []; let loadSentry = () => {}; -if (import.meta.env.VITE_SENTRY_DSN_FRONTEND) { +if (import.meta.env.VITE_SENTRY_DSN_FRONTEND && !import.meta.env.DEV) { const onEarlyError = (event) => { earlyErrors.push(event); loadSentry(); diff --git a/sentry-instrument.js b/sentry-instrument.js index e432669d..a47ad4ed 100644 --- a/sentry-instrument.js +++ b/sentry-instrument.js @@ -5,14 +5,18 @@ // // Gated on SENTRY_DSN_BACKEND, same philosophy as the frontend: when the env // var is unset this module does nothing and @sentry/node is never even -// loaded. backend-server.js attaches the matching Express error handler. +// loaded. SENTRY_ENVIRONMENT=development skips init too, so a local run never +// reports. backend-server.js attaches the matching Express error handler. import dotenv from 'dotenv'; import { scrubBreadcrumb, scrubEventRequest, scrubSpan } from './common/sentry-scrub.js'; dotenv.config({ quiet: true }); -if (process.env.SENTRY_DSN_BACKEND) { +// SENTRY_ENVIRONMENT=development (a local `pnpm dev` machine) skips init: +// experiments in progress are not production signal, same rule as the cron +// check-ins in common/sentry-cron.js. +if (process.env.SENTRY_DSN_BACKEND && process.env.SENTRY_ENVIRONMENT !== 'development') { const Sentry = await import('@sentry/node'); Sentry.init({ dsn: process.env.SENTRY_DSN_BACKEND,