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.
This commit is contained in:
Spencer Ofwiti 2021-03-14 11:13:51 +03:00
parent d7dcece7fe
commit c96ebec1d2
6 changed files with 126 additions and 3 deletions

View File

@ -0,0 +1,7 @@
import { GlobalErrorHandler } from './global-error-handler';
describe('GlobalErrorHandler', () => {
it('should create an instance', () => {
expect(new GlobalErrorHandler()).toBeTruthy();
});
});

View File

@ -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;
}
}

View File

@ -9,3 +9,5 @@ export * from '@app/_helpers/http-getter';
export * from '@app/_helpers/pgp-signer'; export * from '@app/_helpers/pgp-signer';
export * from '@app/_helpers/registry'; export * from '@app/_helpers/registry';
export * from '@app/_helpers/token-registry'; export * from '@app/_helpers/token-registry';
export * from '@app/_helpers/logging.interceptor';
export * from '@app/_helpers/global-error-handler';

View File

@ -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();
});
});

View File

@ -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<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
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);
}));
}
}

View File

@ -1,4 +1,5 @@
import {MutableKeyStore} from '@app/_helpers/pgp-key-store'; import {MutableKeyStore} from '@app/_helpers/pgp-key-store';
import {LoggingService} from '@app/_services/logging.service';
const openpgp = require('openpgp'); const openpgp = require('openpgp');
@ -31,6 +32,7 @@ class PGPSigner implements Signer {
keyStore: MutableKeyStore; keyStore: MutableKeyStore;
onsign: (signature: Signature) => void; onsign: (signature: Signature) => void;
onverify: (flag: boolean) => void; onverify: (flag: boolean) => void;
loggingService: LoggingService;
constructor(keyStore: MutableKeyStore) { constructor(keyStore: MutableKeyStore) {
this.keyStore = keyStore; this.keyStore = keyStore;
@ -63,11 +65,11 @@ class PGPSigner implements Signer {
return; 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); this.onverify(false);
}); });
}).catch((e) => { }).catch((e) => {
console.error(e); this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
this.onverify(false); this.onverify(false);
}); });
} }
@ -94,7 +96,7 @@ class PGPSigner implements Signer {
}; };
this.onsign(this.signature); this.onsign(this.signature);
}).catch((e) => { }).catch((e) => {
console.error(e); this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
this.onsign(undefined); this.onsign(undefined);
}); });
} }