import * as color from 'colors/safe' import {Logging} from "../service/Logging"; import {Inject} from "../di"; import {ErrorWithContext} from "../util"; /** * Class with logic for handling errors that are thrown at the run-level of the application. * * Colloquially, these are errors thrown ourside the request-lifecycle that are not caught by a unit. */ export class RunLevelErrorHandler { @Inject() protected logging!: Logging /** * Get the error handler function. * @type (e: Error) => void */ get handle(): (e: Error) => void { return (e: Error) => { this.display(e) process.exit(1) } } /** * Wrap the given base Error instance into an ErrorWithContext. * @param e * @param context */ wrapContext(e: Error, context: {[key: string]: any}): ErrorWithContext { if ( e instanceof ErrorWithContext ) { e.context = {...e.context, ...context} return e } const error = new ErrorWithContext(e.message) error.originalError = e error.context = context error.stack = e.stack return error } /** * Log the error to the logger. * @param {Error} e */ display(e: Error) { let operativeError = e let context: {[key: string]: string} = {} if ( e instanceof ErrorWithContext ) { if ( e.originalError ) operativeError = e.originalError context = e.context } const contextDisplay = Object.keys(context).map(key => ` - ${key}: ${context[key]}`).join('\n') try { let errorString = `RunLevelErrorHandler invoked: ${color.bgRed(' ')} ${color.bgRed(' UNCAUGHT RUN-LEVEL ERROR ')} ${color.bgRed(' ')} ${operativeError.constructor ? operativeError.constructor.name : operativeError.name} ${color.red(`---------------------------------------------------`)} ${operativeError.stack} ` if ( contextDisplay ) { errorString += ` With the following context: ${contextDisplay} ` } this.logging.error(errorString, true) } catch (display_e) { // The error display encountered an error... // just throw the original so it makes it out console.error('RunLevelErrorHandler encountered an error:', display_e.message) throw operativeError } } }