From 3a0953f33d2e8bcc218381ff18c2eeb9db89c028 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 16 May 2026 07:25:00 +0300 Subject: [PATCH] Add Sentry enabled FF --- .env.example | 4 ++++ app/db/sql.ts | 4 +++- app/entry.client.tsx | 44 +++++++++++++++++++--------------- app/entry.server.tsx | 14 +++++++---- app/routines/routine.server.ts | 38 +++++++++++++++-------------- 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/.env.example b/.env.example index 569b6c3c7..ee4e0dda7 100644 --- a/.env.example +++ b/.env.example @@ -50,3 +50,7 @@ SENTRY_PROJECT= SENTRY_AUTH_TOKEN= VITE_SENTRY_DSN= + +// Toggles Sentry runtime instrumentation (error capture + tracing spans). +// Disabled by default since spans add overhead — flip on when you need it. +VITE_SENTRY_ENABLED=false diff --git a/app/db/sql.ts b/app/db/sql.ts index 2868edeea..086321221 100644 --- a/app/db/sql.ts +++ b/app/db/sql.ts @@ -17,6 +17,8 @@ const LOG_LEVEL = (["trunc", "full", "none"] as const).find( (val) => val === process.env.SQL_LOG, ); +const SENTRY_ENABLED = import.meta.env.VITE_SENTRY_ENABLED === "true"; + const migratedEmptyDb = new Database("db-test.sqlite3").serialize(); invariant(process.env.DB_PATH, "DB_PATH env variable must be set"); @@ -41,7 +43,7 @@ export const db = new Kysely({ }); function log(event: LogEvent) { - if (event.level === "query") { + if (SENTRY_ENABLED && event.level === "query") { // Backdated span so the query nests under the active loader/action span // in Sentry's waterfall. `onlyIfParent: true` skips emission when there's // no active trace (e.g. cron routines), avoiding orphan root spans. diff --git a/app/entry.client.tsx b/app/entry.client.tsx index 88ec88a5b..a5f5653f1 100644 --- a/app/entry.client.tsx +++ b/app/entry.client.tsx @@ -8,24 +8,30 @@ import { i18nLoader } from "./modules/i18n/loader"; import { logger } from "./utils/logger"; import { getSessionId } from "./utils/session-id"; -const tracing = Sentry.reactRouterTracingIntegration({ - useInstrumentationAPI: true, -}); +const SENTRY_ENABLED = import.meta.env.VITE_SENTRY_ENABLED === "true"; -Sentry.init({ - dsn: import.meta.env.VITE_SENTRY_DSN, - sendDefaultPii: false, - integrations: [ - tracing, - Sentry.thirdPartyErrorFilterIntegration({ - filterKeys: ["sendou-ink"], - behaviour: "drop-error-if-contains-third-party-frames", - }), - ], - enableLogs: true, - tracesSampleRate: 0.1, - tracePropagationTargets: [/^\//], -}); +const tracing = SENTRY_ENABLED + ? Sentry.reactRouterTracingIntegration({ + useInstrumentationAPI: true, + }) + : null; + +if (SENTRY_ENABLED) { + Sentry.init({ + dsn: import.meta.env.VITE_SENTRY_DSN, + sendDefaultPii: false, + integrations: [ + tracing!, + Sentry.thirdPartyErrorFilterIntegration({ + filterKeys: ["sendou-ink"], + behaviour: "drop-error-if-contains-third-party-frames", + }), + ], + enableLogs: true, + tracesSampleRate: 0.1, + tracePropagationTargets: [/^\//], + }); +} const originalFetch = window.fetch; window.fetch = (input, init) => { @@ -62,9 +68,9 @@ i18nLoader() document, { - if (error && error instanceof Error) { + if (SENTRY_ENABLED && error && error instanceof Error) { Sentry.captureException(error); } }} diff --git a/app/entry.server.tsx b/app/entry.server.tsx index d2eb709b9..59bf9d01d 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -26,6 +26,8 @@ import { logger } from "./utils/logger"; // Reject/cancel all pending promises after 5 seconds export const streamTimeout = 5000; +const SENTRY_ENABLED = import.meta.env.VITE_SENTRY_ENABLED === "true"; + async function handleRequest( request: Request, responseStatusCode: number, @@ -69,7 +71,7 @@ async function handleRequest( }), ); - pipe(Sentry.getMetaTagTransformer(body)); + pipe(SENTRY_ENABLED ? Sentry.getMetaTagTransformer(body) : body); }, onShellError(error: unknown) { reject(error); @@ -128,10 +130,14 @@ process.on("unhandledRejection", (reason: string, p: Promise) => { // wrapper so we get request id shown in the server logs export const handleError: HandleErrorFunction = (error, { request }) => { - if (!request.signal.aborted) { + if (SENTRY_ENABLED && !request.signal.aborted) { Sentry.captureException(error); } logger.error(error); }; -export default Sentry.wrapSentryHandleRequest(handleRequest); -export const instrumentations = [Sentry.createSentryServerInstrumentation()]; +export default SENTRY_ENABLED + ? Sentry.wrapSentryHandleRequest(handleRequest) + : handleRequest; +export const instrumentations = SENTRY_ENABLED + ? [Sentry.createSentryServerInstrumentation()] + : []; diff --git a/app/routines/routine.server.ts b/app/routines/routine.server.ts index c075b1815..ed664051d 100644 --- a/app/routines/routine.server.ts +++ b/app/routines/routine.server.ts @@ -1,6 +1,8 @@ import * as Sentry from "@sentry/react-router"; import { logger } from "../utils/logger"; +const SENTRY_ENABLED = import.meta.env.VITE_SENTRY_ENABLED === "true"; + export class Routine { private name; private func; @@ -18,25 +20,25 @@ export class Routine { async run() { logger.info(`Running routine: ${this.name}`); - await Sentry.startSpan( - { - name: this.name, - op: "cron", - }, - async () => { - const startTime = performance.now(); - try { - await this.func(); - } catch (error) { - logger.error(`Error running routine ${this.name}: ${error}`); + const work = async () => { + const startTime = performance.now(); + try { + await this.func(); + } catch (error) { + logger.error(`Error running routine ${this.name}: ${error}`); + if (SENTRY_ENABLED) { Sentry.captureException(error); - return; } - const endTime = performance.now(); - logger.info( - `Routine ${this.name} completed in ${endTime - startTime}ms`, - ); - }, - ); + return; + } + const endTime = performance.now(); + logger.info(`Routine ${this.name} completed in ${endTime - startTime}ms`); + }; + + if (SENTRY_ENABLED) { + await Sentry.startSpan({ name: this.name, op: "cron" }, work); + } else { + await work(); + } } }