feat(errors): fallback handlers for unhandled rejections and errors
Sentry's global integrations natively capture window.onerror / unhandledrejection (renderer) and uncaughtException / unhandledRejection (main). When a DSN is configured we let Sentry own those paths to avoid double-capturing. When it isn't (dev, unconfigured prod), we attach minimal listeners that route through reportError so stray promise rejections and uncaught errors at least hit the console and the facade. This closes the one gap where a bare click-handler promise rejection (no try/catch, not a useMutation) would otherwise be invisible.
This commit is contained in:
+31
-18
@@ -1,28 +1,41 @@
|
|||||||
import * as Sentry from "@sentry/electron/renderer";
|
import * as Sentry from "@sentry/electron/renderer";
|
||||||
import { appConfig } from "@/config/env";
|
import { appConfig } from "@/config/env";
|
||||||
import { installErrorSinks } from "@/lib/errors";
|
import { installErrorSinks, reportError } from "@/lib/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise Sentry for a renderer process. No-ops when `sentryDsn` is empty
|
* Initialise renderer error handling. When a DSN is configured, Sentry's
|
||||||
* so dev builds and unconfigured envs stay quiet.
|
* own global handlers capture uncaught exceptions and unhandled promise
|
||||||
|
* rejections. Otherwise we install a minimal fallback so stray rejections
|
||||||
|
* aren't silent in dev / unconfigured envs.
|
||||||
*/
|
*/
|
||||||
export function initSentryRenderer(): void {
|
export function initSentryRenderer(): void {
|
||||||
if (!appConfig.sentryDsn) return;
|
if (appConfig.sentryDsn) {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: appConfig.sentryDsn,
|
||||||
|
tracesSampleRate: 0,
|
||||||
|
});
|
||||||
|
|
||||||
Sentry.init({
|
installErrorSinks({
|
||||||
dsn: appConfig.sentryDsn,
|
capture: (err, context) =>
|
||||||
tracesSampleRate: 0,
|
Sentry.captureException(err, { extra: context }),
|
||||||
|
breadcrumb: (err, context) =>
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
category: "error",
|
||||||
|
level: "error",
|
||||||
|
message: err instanceof Error ? err.message : String(err),
|
||||||
|
data: context,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("unhandledrejection", (event) => {
|
||||||
|
reportError(event.reason, { scope: "unhandledrejection" });
|
||||||
});
|
});
|
||||||
|
window.addEventListener("error", (event) => {
|
||||||
installErrorSinks({
|
reportError(event.error ?? new Error(event.message), {
|
||||||
capture: (err, context) =>
|
scope: "window.error",
|
||||||
Sentry.captureException(err, { extra: context }),
|
});
|
||||||
breadcrumb: (err, context) =>
|
|
||||||
Sentry.addBreadcrumb({
|
|
||||||
category: "error",
|
|
||||||
level: "error",
|
|
||||||
message: err instanceof Error ? err.message : String(err),
|
|
||||||
data: context,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-19
@@ -1,30 +1,39 @@
|
|||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import * as Sentry from "@sentry/electron/main";
|
import * as Sentry from "@sentry/electron/main";
|
||||||
import { appConfig } from "@/config/env";
|
import { appConfig } from "@/config/env";
|
||||||
import { installErrorSinks } from "@/lib/errors";
|
import { installErrorSinks, reportError } from "@/lib/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise Sentry for the main process. Captures uncaught exceptions from
|
* Initialise main-process error handling. Sentry owns uncaught-exception
|
||||||
* the Node side and the crash reporter. Safe to call before `app.whenReady`.
|
* capture when a DSN is configured; otherwise we install a minimal Node
|
||||||
|
* fallback so rejections aren't silent. Safe to call before `app.whenReady`.
|
||||||
*/
|
*/
|
||||||
export function initSentryMain(): void {
|
export function initSentryMain(): void {
|
||||||
if (!appConfig.sentryDsn) return;
|
if (appConfig.sentryDsn) {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: appConfig.sentryDsn,
|
||||||
|
tracesSampleRate: 0,
|
||||||
|
release: app.getVersion(),
|
||||||
|
});
|
||||||
|
|
||||||
Sentry.init({
|
installErrorSinks({
|
||||||
dsn: appConfig.sentryDsn,
|
capture: (err, context) =>
|
||||||
tracesSampleRate: 0,
|
Sentry.captureException(err, { extra: context }),
|
||||||
release: app.getVersion(),
|
breadcrumb: (err, context) =>
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
category: "error",
|
||||||
|
level: "error",
|
||||||
|
message: err instanceof Error ? err.message : String(err),
|
||||||
|
data: context,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on("uncaughtException", (err) => {
|
||||||
|
reportError(err, { scope: "uncaughtException" });
|
||||||
});
|
});
|
||||||
|
process.on("unhandledRejection", (reason) => {
|
||||||
installErrorSinks({
|
reportError(reason, { scope: "unhandledRejection" });
|
||||||
capture: (err, context) =>
|
|
||||||
Sentry.captureException(err, { extra: context }),
|
|
||||||
breadcrumb: (err, context) =>
|
|
||||||
Sentry.addBreadcrumb({
|
|
||||||
category: "error",
|
|
||||||
level: "error",
|
|
||||||
message: err instanceof Error ? err.message : String(err),
|
|
||||||
data: context,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user