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): void { 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 (displayError: unknown) { if ( displayError instanceof Error ) { // The error display encountered an error... // just throw the original so it makes it out console.error('RunLevelErrorHandler encountered an error:', displayError.message) // eslint-disable-line no-console } throw operativeError } } }