Browse Source

Log on unhandled errors and promise rejections

Test code:

    useEffect(() => {
        setTimeout(() => testFunctionSync(), 5000);
    }, []);

    useEffect(() => {
        testFunction();
    }, []);

    const testFunctionSync = () => {
        console.log("sleeping not");
        // sleep(2000);
        console.log("woke up not");
        throw new Error("Handle me");
    };

    const testFunction = async () => {
        console.log("sleeping");
        sleep(2000);
        console.log("woke up");
        throw new Error("Handle me");
    };

Refs:
- https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event
- https://github.com/megahertz/electron-log/blob/master/src/renderer/lib/RendererErrorHandler.js
Manav Rathi 1 year ago
parent
commit
e0fe018f34
2 changed files with 35 additions and 1 deletions
  1. 8 1
      web/apps/photos/src/pages/_app.tsx
  2. 27 0
      web/packages/next/log-web.ts

+ 8 - 1
web/apps/photos/src/pages/_app.tsx

@@ -1,7 +1,10 @@
 import { CustomHead } from "@/next/components/Head";
 import { setupI18n } from "@/next/i18n";
 import log from "@/next/log";
-import { logStartupBanner } from "@/next/log-web";
+import {
+    logStartupBanner,
+    logUnhandledErrorsAndRejections,
+} from "@/next/log-web";
 import { AppUpdateInfo } from "@/next/types/ipc";
 import {
     APPS,
@@ -147,9 +150,13 @@ export default function App({ Component, pageProps }: AppProps) {
         setupI18n().finally(() => setIsI18nReady(true));
         const userId = (getData(LS_KEYS.USER) as User)?.id;
         logStartupBanner(APPS.PHOTOS, userId);
+        logUnhandledErrorsAndRejections(true);
         HTTPService.setHeaders({
             "X-Client-Package": CLIENT_PACKAGE_NAMES.get(APPS.PHOTOS),
         });
+        return () => {
+            logUnhandledErrorsAndRejections(false);
+        };
     }, []);
 
     useEffect(() => {

+ 27 - 0
web/packages/next/log-web.ts

@@ -18,6 +18,33 @@ export const logStartupBanner = (appId: string, userId?: number) => {
     log.info(`Starting ente-${appIdL}-web ${buildId}uid ${userId ?? 0}`);
 };
 
+/**
+ * Attach handlers to log any unhandled exceptions and promise rejections.
+ *
+ * @param attach If true, attach handlers, and if false, remove them. This
+ * allows us to use this in a React hook that cleans up after itself.
+ */
+export const logUnhandledErrorsAndRejections = (attach: boolean) => {
+    const handleError = (event: ErrorEvent) => {
+        log.error("Unhandled error", event.error);
+    };
+
+    const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
+        log.error("Unhandled promise rejection", event.reason);
+    };
+
+    if (attach) {
+        window.addEventListener("error", handleError);
+        window.addEventListener("unhandledrejection", handleUnhandledRejection);
+    } else {
+        window.removeEventListener("error", handleError);
+        window.removeEventListener(
+            "unhandledrejection",
+            handleUnhandledRejection,
+        );
+    }
+};
+
 interface LogEntry {
     timestamp: number;
     logLine: string;