import util from 'node:util' import chalk from 'chalk' import _ from 'lodash' // @ts-ignore import highlightStack from '@bmp/highlight-stack' const LEVELS: Record = { default: 'USERLVL', 60: 'FATAL', 50: 'ERROR', 40: 'WARN', 30: 'INFO', 20: 'DEBUG', 10: 'TRACE', } const COLORS: Record = { 60: chalk.bgRed, 50: chalk.red, 40: chalk.yellow, 30: chalk.green, 20: chalk.blue, 10: chalk.grey, } const requests = new Map() function colorStatusCode(statusCode: number) { if (statusCode < 300) { return chalk.bold.green(statusCode) } else if (statusCode < 400) { return chalk.bold.blue(statusCode) } else if (statusCode < 500) { return chalk.bold.yellow(statusCode) } else { return chalk.bold.red(statusCode) } } interface BaseLineObject { level: number time: number pid: number reqId: string msg: string } interface ErrorLineObject extends BaseLineObject { level: 50 err: ANY } interface IncomingRequestLineObject extends BaseLineObject { msg: 'incoming request' req: Record } interface RequestCompletedLineObject extends BaseLineObject { msg: 'request completed' res: Record responseTime: number } type LineObject = ErrorLineObject | IncomingRequestLineObject | RequestCompletedLineObject | BaseLineObject export default { write(line: string) { const obj: LineObject = JSON.parse(line) if (obj.msg === 'incoming request') { requests.set(obj.reqId, (obj as IncomingRequestLineObject).req) } else if (obj.msg === 'request completed') { const req = requests.get(obj.reqId) requests.delete(obj.reqId) process.stdout.write( `${chalk.bold(req.method)} ${req.url} ${colorStatusCode((obj as RequestCompletedLineObject).res.statusCode)} ${( obj as RequestCompletedLineObject ).responseTime.toFixed(3)} ms\n`, ) } else if (obj.level === 50) { // TODO figure out if there is a way to get the error instances here... console.log(Error) and util.inspect(Error) // looks better than the serialized errors if ((obj as ErrorLineObject).err.status < 500) return process.stdout.write( `${COLORS[obj.level](LEVELS[obj.level] || LEVELS.default)} ${highlightStack((obj as ErrorLineObject).err.stack)}\n`, ) const details = _.omit((obj as ErrorLineObject).err, ['type', 'message', 'stack']) if (!_.isEmpty(details)) { process.stdout.write(`${chalk.red('Details')} ${util.inspect(details, false, 4, true)}\n`) } } else { process.stdout.write(`${COLORS[obj.level](LEVELS[obj.level] || LEVELS.default)} ${obj.msg}\n`) } }, }