Add Sentry enabled FF
Some checks are pending
E2E Tests / e2e (push) Waiting to run
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run

This commit is contained in:
Kalle 2026-05-16 07:25:00 +03:00
parent d004efa3b1
commit 3a0953f33d
5 changed files with 62 additions and 42 deletions

View File

@ -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

View File

@ -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<DB>({
});
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.

View File

@ -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,
<I18nextProvider i18n={i18next}>
<HydratedRouter
instrumentations={[tracing.clientInstrumentation]}
instrumentations={tracing ? [tracing.clientInstrumentation] : []}
onError={(error) => {
if (error && error instanceof Error) {
if (SENTRY_ENABLED && error && error instanceof Error) {
Sentry.captureException(error);
}
}}

View File

@ -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<any>) => {
// 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()]
: [];

View File

@ -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();
}
}
}