feat(errors): add Sentry observability behind a sinks facade

logError / reportError now route through a pair of sinks installed at
bootstrap by each process (main + every renderer entry). No call site
knows about Sentry — if sentryDsn is empty, the sinks simply aren't
installed and logError/reportError stay console-only.

- appConfig.sentryDsn: per-env string (empty for now — populate when
  ops creates the DSNs). Empty is the no-op mode for dev.
- lib/errors.ts: installErrorSinks({ capture, breadcrumb }) gates
  Sentry.captureException / Sentry.addBreadcrumb. Everything flows
  through toUserMessage and the two existing call types.
- lib/sentry.ts: initSentryRenderer() for the main window + autoplay,
  huddle, and screen-record renderers.
- main/sentry.ts: initSentryMain() runs before anything else in
  main.ts to catch bootstrap failures. Captures uncaught exceptions
  and the crash reporter automatically.
- main/ipc-utils.ts::safeHandle now routes through reportError.
- main.ts::fetchLinkMetadata logs via logError.
This commit is contained in:
Claude
2026-04-17 03:01:02 +00:00
parent 795bdfc286
commit b0281ae8f9
12 changed files with 677 additions and 7 deletions
+2 -2
View File
@@ -1,4 +1,5 @@
import { ipcMain, type IpcMainInvokeEvent } from "electron";
import { reportError } from "../lib/errors";
type InvokeHandler = (
event: IpcMainInvokeEvent,
@@ -14,8 +15,7 @@ export function safeHandle(channel: string, handler: InvokeHandler): void {
try {
return await handler(event, ...(args as unknown[]));
} catch (err) {
// eslint-disable-next-line no-console
console.error(`[ipc:${channel}]`, err);
reportError(err, { scope: `ipc.${channel}` });
const message = err instanceof Error ? err.message : "IPC error";
throw new Error(message);
}