99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
import util from 'node:util'
|
|
import chalk from 'chalk'
|
|
import _ from 'lodash'
|
|
// @ts-ignore
|
|
import highlightStack from '@bmp/highlight-stack'
|
|
|
|
const LEVELS: Record<string, string> = {
|
|
default: 'USERLVL',
|
|
60: 'FATAL',
|
|
50: 'ERROR',
|
|
40: 'WARN',
|
|
30: 'INFO',
|
|
20: 'DEBUG',
|
|
10: 'TRACE',
|
|
}
|
|
|
|
const COLORS: Record<string, any> = {
|
|
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<string, any>
|
|
}
|
|
|
|
interface RequestCompletedLineObject extends BaseLineObject {
|
|
msg: 'request completed'
|
|
res: Record<string, any>
|
|
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`)
|
|
}
|
|
},
|
|
}
|