Unverified 提交 bfeaebfe authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

feat: capture uncaught IPC handler errors as PostHog exceptions (#2886)

## Summary - Added `sendTelemetryException()` utility that sends `$exception` events to PostHog via the main→renderer telemetry bridge, including error type, message, and stack trace - Wired error capture into all three IPC handler factories (`createTypedHandler`, `createLoggedTypedHandler`, `createLoggedHandler`) so every `ipcMain.handle` error is automatically reported with the IPC channel name as context - Errors are still re-thrown after capture so existing behavior is unchanged ## Test plan - All 844 existing tests pass - Verify in dev that IPC handler errors appear as `$exception` events in PostHog with `ipc_channel` property - Confirm telemetry opt-out still prevents exception events from being sent (via existing `before_send` filter) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2886" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> Co-authored-by: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
上级 d6ab8290
import { ipcMain, IpcMainInvokeEvent } from "electron"; import { ipcMain, IpcMainInvokeEvent } from "electron";
import { z } from "zod"; import { z } from "zod";
import type { IpcContract } from "../contracts/core"; import type { IpcContract } from "../contracts/core";
import { sendTelemetryException } from "../utils/telemetry";
/** /**
* Creates a typed IPC handler from a contract. * Creates a typed IPC handler from a contract.
...@@ -37,7 +38,13 @@ export function createTypedHandler< ...@@ -37,7 +38,13 @@ export function createTypedHandler<
throw new Error(`[${contract.channel}] Invalid input: ${errorMessage}`); throw new Error(`[${contract.channel}] Invalid input: ${errorMessage}`);
} }
const result = await handler(event, parsed.data); let result: z.infer<TOutput>;
try {
result = await handler(event, parsed.data);
} catch (err) {
sendTelemetryException(err, { ipc_channel: contract.channel });
throw err;
}
// Validate output in development mode only (catches handler bugs without prod overhead) // Validate output in development mode only (catches handler bugs without prod overhead)
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
...@@ -118,6 +125,7 @@ export function createLoggedTypedHandler(logger: { ...@@ -118,6 +125,7 @@ export function createLoggedTypedHandler(logger: {
return result; return result;
} catch (err) { } catch (err) {
logger.error(`[${contract.channel}] Handler error`, err); logger.error(`[${contract.channel}] Handler error`, err);
sendTelemetryException(err, { ipc_channel: contract.channel });
throw err; throw err;
} }
}, },
......
import { ipcMain, IpcMainInvokeEvent } from "electron"; import { ipcMain, IpcMainInvokeEvent } from "electron";
import log from "electron-log"; import log from "electron-log";
import { sendTelemetryException } from "../utils/telemetry";
import { IS_TEST_BUILD } from "../utils/test_utils"; import { IS_TEST_BUILD } from "../utils/test_utils";
export function createLoggedHandler(logger: log.LogFunctions) { export function createLoggedHandler(logger: log.LogFunctions) {
...@@ -22,6 +23,7 @@ export function createLoggedHandler(logger: log.LogFunctions) { ...@@ -22,6 +23,7 @@ export function createLoggedHandler(logger: log.LogFunctions) {
`Error in ${fn.name}: args: ${JSON.stringify(args)}`, `Error in ${fn.name}: args: ${JSON.stringify(args)}`,
error, error,
); );
sendTelemetryException(error, { ipc_channel: channel });
throw new Error(`[${channel}] ${error}`); throw new Error(`[${channel}] ${error}`);
} }
}, },
......
...@@ -24,3 +24,22 @@ export function sendTelemetryEvent( ...@@ -24,3 +24,22 @@ export function sendTelemetryEvent(
logger.warn("Error sending telemetry event:", error); logger.warn("Error sending telemetry event:", error);
} }
} }
/**
* Sends an exception from the main process to the renderer as a PostHog $exception event.
*/
export function sendTelemetryException(
error: unknown,
context?: Record<string, unknown>,
): void {
const err =
error instanceof Error
? error
: new Error(String(error ?? "Unknown error"));
sendTelemetryEvent("$exception", {
$exception_type: err.name,
$exception_message: err.message,
$exception_stack_trace_raw: err.stack,
...context,
});
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论