2021-03-03 00:57:41 +00:00
|
|
|
import * as color from 'colors/safe'
|
2021-06-03 03:36:25 +00:00
|
|
|
import {Logging} from '../service/Logging'
|
|
|
|
import {Inject} from '../di'
|
|
|
|
import {ErrorWithContext} from '../util'
|
2021-03-03 00:57:41 +00:00
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-03-03 00:57:41 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Wrap the given base Error instance into an ErrorWithContext.
|
|
|
|
* @param e
|
|
|
|
* @param context
|
|
|
|
*/
|
2021-03-03 00:57:41 +00:00
|
|
|
wrapContext(e: Error, context: {[key: string]: any}): ErrorWithContext {
|
|
|
|
if ( e instanceof ErrorWithContext ) {
|
2021-06-03 03:36:25 +00:00
|
|
|
e.context = {...e.context,
|
|
|
|
...context}
|
2021-03-03 00:57:41 +00:00
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
const error = new ErrorWithContext(e.message)
|
|
|
|
error.originalError = e
|
|
|
|
error.context = context
|
2021-03-21 19:04:51 +00:00
|
|
|
error.stack = e.stack
|
2021-03-03 00:57:41 +00:00
|
|
|
return error
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log the error to the logger.
|
|
|
|
* @param {Error} e
|
|
|
|
*/
|
2021-06-03 03:36:25 +00:00
|
|
|
display(e: Error): void {
|
2021-03-03 00:57:41 +00:00
|
|
|
let operativeError = e
|
|
|
|
let context: {[key: string]: string} = {}
|
|
|
|
if ( e instanceof ErrorWithContext ) {
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( e.originalError ) {
|
|
|
|
operativeError = e.originalError
|
|
|
|
}
|
2021-03-03 00:57:41 +00:00
|
|
|
context = e.context
|
|
|
|
}
|
|
|
|
|
2021-06-03 03:36:25 +00:00
|
|
|
const contextDisplay = Object.keys(context).map(key => ` - ${key}: ${context[key]}`)
|
|
|
|
.join('\n')
|
2021-03-03 00:57:41 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
let errorString = `RunLevelErrorHandler invoked:
|
|
|
|
|
|
|
|
${color.bgRed(' ')}
|
|
|
|
${color.bgRed(' UNCAUGHT RUN-LEVEL ERROR ')}
|
|
|
|
${color.bgRed(' ')}
|
|
|
|
|
2021-03-21 19:04:51 +00:00
|
|
|
${operativeError.constructor ? operativeError.constructor.name : operativeError.name}
|
2021-03-03 00:57:41 +00:00
|
|
|
${color.red(`---------------------------------------------------`)}
|
2021-03-21 19:04:51 +00:00
|
|
|
${operativeError.stack}
|
2021-03-03 00:57:41 +00:00
|
|
|
`
|
|
|
|
|
|
|
|
if ( contextDisplay ) {
|
|
|
|
errorString += `
|
|
|
|
With the following context:
|
|
|
|
${contextDisplay}
|
|
|
|
`
|
|
|
|
}
|
2021-06-03 03:36:25 +00:00
|
|
|
|
2021-03-03 00:57:41 +00:00
|
|
|
this.logging.error(errorString, true)
|
2021-10-18 18:03:28 +00:00
|
|
|
} 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
|
|
|
|
}
|
|
|
|
|
2021-03-03 00:57:41 +00:00
|
|
|
throw operativeError
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|