From c96ebec1d23c6d6b1f2cae6b632cf952fa59ebf4 Mon Sep 17 00:00:00 2001 From: Spencer Ofwiti Date: Sun, 14 Mar 2021 11:13:51 +0300 Subject: [PATCH] Add global error handler and logging interceptor. - Global error handler allows catching errors and parsing them to appropriate logging handler. - Logging interceptor allows logging of request information and status. --- src/app/_helpers/global-error-handler.spec.ts | 7 +++ src/app/_helpers/global-error-handler.ts | 61 +++++++++++++++++++ src/app/_helpers/index.ts | 2 + src/app/_helpers/logging.interceptor.spec.ts | 16 +++++ src/app/_helpers/logging.interceptor.ts | 35 +++++++++++ src/app/_helpers/pgp-signer.ts | 8 ++- 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/app/_helpers/global-error-handler.spec.ts create mode 100644 src/app/_helpers/global-error-handler.ts create mode 100644 src/app/_helpers/logging.interceptor.spec.ts create mode 100644 src/app/_helpers/logging.interceptor.ts diff --git a/src/app/_helpers/global-error-handler.spec.ts b/src/app/_helpers/global-error-handler.spec.ts new file mode 100644 index 0000000..75ba562 --- /dev/null +++ b/src/app/_helpers/global-error-handler.spec.ts @@ -0,0 +1,7 @@ +import { GlobalErrorHandler } from './global-error-handler'; + +describe('GlobalErrorHandler', () => { + it('should create an instance', () => { + expect(new GlobalErrorHandler()).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/global-error-handler.ts b/src/app/_helpers/global-error-handler.ts new file mode 100644 index 0000000..3446823 --- /dev/null +++ b/src/app/_helpers/global-error-handler.ts @@ -0,0 +1,61 @@ +import {ErrorHandler, Injectable} from '@angular/core'; +import {LoggingService} from '@app/_services'; +import {HttpErrorResponse} from '@angular/common/http'; + +@Injectable() +export class GlobalErrorHandler extends ErrorHandler { + private sentencesForWarningLogging: string[] = []; + private loggingService: LoggingService; + + constructor() { + super(); + } + + handleError(error: any): void { + this.logError(error); + const message = error.message ? error.message : error.toString(); + + if (error.status) { + error = new Error(message); + } + + const errorTraceString = `Error message:\n${message}.\nStack trace: ${error.stack}`; + + const isWarning = this.isWarning(errorTraceString); + if (isWarning) { + this.loggingService.sendWarnLevelMessage(errorTraceString, {error}); + } else { + this.loggingService.sendErrorLevelMessage(errorTraceString, this, {error}); + } + + throw error; + } + + logError(error: any): void { + if (error instanceof HttpErrorResponse) { + this.loggingService.sendErrorLevelMessage( + `There was an HTTP error. ${error.message}, Status code: ${(error as HttpErrorResponse).status}`, this, {error}); + } else if (error instanceof TypeError) { + this.loggingService.sendErrorLevelMessage(`There was a Type error. ${error.message}`, this, {error}); + } else if (error instanceof Error) { + this.loggingService.sendErrorLevelMessage(`There was a general error. ${error.message}`, this, {error}); + } else { + this.loggingService.sendErrorLevelMessage('Nobody threw an error but something happened!', this, {error}); + } + } + + private isWarning(errorTraceString: string): boolean { + let isWarning = true; + if (errorTraceString.includes('/src/app/')) { + isWarning = false; + } + + this.sentencesForWarningLogging.forEach((whiteListSentence) => { + if (errorTraceString.includes(whiteListSentence)) { + isWarning = true; + } + }); + + return isWarning; + } +} diff --git a/src/app/_helpers/index.ts b/src/app/_helpers/index.ts index 42ab912..c3f5f8b 100644 --- a/src/app/_helpers/index.ts +++ b/src/app/_helpers/index.ts @@ -9,3 +9,5 @@ export * from '@app/_helpers/http-getter'; export * from '@app/_helpers/pgp-signer'; export * from '@app/_helpers/registry'; export * from '@app/_helpers/token-registry'; +export * from '@app/_helpers/logging.interceptor'; +export * from '@app/_helpers/global-error-handler'; diff --git a/src/app/_helpers/logging.interceptor.spec.ts b/src/app/_helpers/logging.interceptor.spec.ts new file mode 100644 index 0000000..6956f7e --- /dev/null +++ b/src/app/_helpers/logging.interceptor.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { LoggingInterceptor } from './logging.interceptor'; + +describe('LoggingInterceptor', () => { + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + LoggingInterceptor + ] + })); + + it('should be created', () => { + const interceptor: LoggingInterceptor = TestBed.inject(LoggingInterceptor); + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/logging.interceptor.ts b/src/app/_helpers/logging.interceptor.ts new file mode 100644 index 0000000..a0e2c95 --- /dev/null +++ b/src/app/_helpers/logging.interceptor.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor, HttpResponse +} from '@angular/common/http'; +import { Observable } from 'rxjs'; +import {LoggingService} from '@app/_services/logging.service'; +import {finalize, tap} from 'rxjs/operators'; + +@Injectable() +export class LoggingInterceptor implements HttpInterceptor { + + constructor( + private loggingService: LoggingService + ) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + this.loggingService.sendInfoLevelMessage(request); + const startTime = Date.now(); + let status: string; + + return next.handle(request).pipe(tap(event => { + status = ''; + if (event instanceof HttpResponse) { + status = 'succeeded'; + } + }, error => status = 'failed'), finalize(() => { + const elapsedTime = Date.now() - startTime; + const message = `${request.method} ${request.urlWithParams} ${status} in ${elapsedTime} ms`; + this.loggingService.sendInfoLevelMessage(message); + })); + } +} diff --git a/src/app/_helpers/pgp-signer.ts b/src/app/_helpers/pgp-signer.ts index 33c27ee..a86b851 100644 --- a/src/app/_helpers/pgp-signer.ts +++ b/src/app/_helpers/pgp-signer.ts @@ -1,4 +1,5 @@ import {MutableKeyStore} from '@app/_helpers/pgp-key-store'; +import {LoggingService} from '@app/_services/logging.service'; const openpgp = require('openpgp'); @@ -31,6 +32,7 @@ class PGPSigner implements Signer { keyStore: MutableKeyStore; onsign: (signature: Signature) => void; onverify: (flag: boolean) => void; + loggingService: LoggingService; constructor(keyStore: MutableKeyStore) { this.keyStore = keyStore; @@ -63,11 +65,11 @@ class PGPSigner implements Signer { return; } } - console.error('checked ' + i + ' signature(s) but none valid'); + this.loggingService.sendErrorLevelMessage(`Checked ${i} signature(s) but none valid`, this, {error: '404 Not found!'}); this.onverify(false); }); }).catch((e) => { - console.error(e); + this.loggingService.sendErrorLevelMessage(e.message, this, {error: e}); this.onverify(false); }); } @@ -94,7 +96,7 @@ class PGPSigner implements Signer { }; this.onsign(this.signature); }).catch((e) => { - console.error(e); + this.loggingService.sendErrorLevelMessage(e.message, this, {error: e}); this.onsign(undefined); }); }