Pārlūkot izejas kodu

Catch and log unhandled exceptions (#981)

This way exceptions also land in the troubleshooting log.
Danilo Bargen 5 gadi atpakaļ
vecāks
revīzija
1b8147307b
2 mainītis faili ar 60 papildinājumiem un 0 dzēšanām
  1. 25 0
      src/app.ts
  2. 35 0
      src/services/log.ts

+ 25 - 0
src/app.ts

@@ -24,9 +24,11 @@ import config from './config';
 import './controllers';
 import './directives';
 import './filters';
+import {hasValue} from './helpers';
 import './partials/messenger';
 import './partials/welcome';
 import './services';
+import {LogService} from './services/log';
 import './threema/container';
 
 // Configure asynchronous events
@@ -184,4 +186,27 @@ angular.module('3ema', [
     }]);
 }])
 
+.factory('$exceptionHandler', ['LogService', function(logService: LogService) {
+    const logger = logService.getLogger('UncaughtException');
+    return function myExceptionHandler(e: any, cause: any) {
+        if (!hasValue(e)) {
+            // Fun fact: `throw null` is prefectly valid
+            logger.error(`Unhandled exception (ng): ${e}`);
+            return;
+        }
+        const data: any[] = [];
+        data.push('Unhandled exception (ng):');
+        if (e.message && e.name) {
+            // Firefox does not include the exception name in the message, Chrome does.
+            data.push(e.message.includes(e.name) ? `${e.message}\n` : `${e.name}: ${e.message}\n`);
+        }
+        data.push(e.stack ? e.stack : e);
+        if (cause) {
+            data.push('\nCaused by:\n');
+            data.push(cause.stack ? cause.stack : cause);
+        }
+        logger.error(...data);
+    };
+}])
+
 ;

+ 35 - 0
src/services/log.ts

@@ -51,6 +51,9 @@ export class LogService {
 
         // Initialise tee logging
         this.root = new TeeLogger(loggers);
+
+        // Log uncaught exceptions
+        this.setupUncaughtExceptionHandling()
     }
 
     /**
@@ -85,4 +88,36 @@ export class LogService {
         }
         return logger;
     }
+
+    /**
+     * Catch uncaught exceptions and log them.
+     *
+     * Note: Most uncaught exceptions should already be caught by AngularJS!
+     * See $exceptionHandler factory in app.ts.
+     */
+    public setupUncaughtExceptionHandling() {
+        const logger = this.getLogger('UncaughtException');
+
+        // Listen for uncaught exceptions
+        window.addEventListener('error', (e: ErrorEvent) => {
+            const data: any[] = [];
+            data.push(`Unhandled exception (w): ${e.message}\n`);
+            data.push((e.error && e.error.stack) ? e.error.stack : e.error);
+            logger.error(...data);
+            e.stopPropagation();
+            e.preventDefault();
+            return true;
+        });
+
+        // Listen for unhandled rejections
+        window.addEventListener('unhandledrejection', (e: PromiseRejectionEvent) => {
+            logger.error(
+                'Unhandled promise rejection:',
+                (e.reason && e.reason.stack) ? e.reason.stack : e.reason,
+            )
+            e.stopPropagation();
+            e.preventDefault();
+            return true;
+        });
+    }
 }