You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
3.4 KiB
159 lines
3.4 KiB
import { Command } from "@oclif/command"; |
|
import chalk from "chalk"; |
|
import * as pump from "pump"; |
|
import * as split from "split2"; |
|
import { Transform, TransformCallback } from "stream"; |
|
|
|
type Level = "default" | 60 | 50 | 40 | 30 | 20 | 10; |
|
|
|
const levels = { |
|
default: "USERLVL", |
|
60: "FATAL", |
|
50: "ERROR", |
|
40: "WARN", |
|
30: "INFO", |
|
20: "DEBUG", |
|
10: "TRACE" |
|
}; |
|
|
|
const levelColors = { |
|
default: chalk.white.underline, |
|
60: chalk.bgRed.underline, |
|
50: chalk.red.underline, |
|
40: chalk.yellow.underline, |
|
30: chalk.green.underline, |
|
20: chalk.blue.underline, |
|
10: chalk.grey.underline |
|
}; |
|
|
|
const standardKeys = [ |
|
"pid", |
|
"hostname", |
|
"name", |
|
"level", |
|
"time", |
|
"v", |
|
"source", |
|
"msg" |
|
]; |
|
|
|
function formatter(value: any) { |
|
let line = formatTime(value, " "); |
|
line += formatSource(value); |
|
line += asColoredLevel(value); |
|
|
|
// line += " ("; |
|
// if (value.name) { |
|
// line += value.name + "/"; |
|
// } |
|
// line += value.pid + " on " + value.hostname + ")"; |
|
|
|
const isRequest = value.req && value.res; |
|
|
|
line += ": "; |
|
if (isRequest) { |
|
line += chalk.reset(formatRequest(value)); |
|
return line; |
|
} |
|
if (value.msg) { |
|
line += chalk.cyan(value.msg); |
|
} |
|
if (value.err) { |
|
line += "\n " + withSpaces(value.err.stack) + "\n"; |
|
} else { |
|
line += filter(value); |
|
} |
|
return line; |
|
} |
|
|
|
function formatRequest(value: any): string { |
|
const matches = /Content-Length: (\d+)/.exec(value.res.header); |
|
const contentLength = matches ? matches[1] : null; |
|
return ( |
|
`${value.req.remoteAddress} - ` + |
|
`"${value.req.method} ${value.req.url} ${value.res.statusCode}" ` + |
|
`${value.responseTime} ms - ${contentLength}` |
|
); |
|
} |
|
|
|
function withSpaces(value: string): string { |
|
const lines = value.split("\n"); |
|
for (let i = 1; i < lines.length; i++) { |
|
lines[i] = " " + lines[i]; |
|
} |
|
return lines.join("\n"); |
|
} |
|
|
|
function filter(value: any) { |
|
const keys = Object.keys(value); |
|
const filteredKeys = standardKeys; |
|
let result = ""; |
|
|
|
for (const key of keys) { |
|
if (filteredKeys.indexOf(key) < 0) { |
|
result += |
|
"\n " + key + ": " + withSpaces(JSON.stringify(value[key], null, 2)); |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
function asISODate(time: string) { |
|
return new Date(time).toISOString(); |
|
} |
|
|
|
function formatTime(value: any, after?: string) { |
|
after = after || ""; |
|
try { |
|
if (!value || !value.time) { |
|
return ""; |
|
} else { |
|
return "[" + asISODate(value.time) + "]" + after; |
|
} |
|
} catch (_) { |
|
return ""; |
|
} |
|
} |
|
|
|
function formatSource(value: any) { |
|
if (value.source) { |
|
return chalk.magenta("(" + value.source + ") "); |
|
} else { |
|
return ""; |
|
} |
|
} |
|
|
|
function asColoredLevel(value: any) { |
|
const level = value.level as Level; |
|
if (levelColors.hasOwnProperty(level)) { |
|
return levelColors[level](levels[level]); |
|
} else { |
|
return levelColors.default(levels.default); |
|
} |
|
} |
|
|
|
export class PinoPrettyTransform extends Transform { |
|
_transform(chunk: any, encoding: string, cb: TransformCallback) { |
|
let value: any; |
|
try { |
|
value = JSON.parse(chunk.toString()); |
|
} catch (e) { |
|
return cb(undefined, chunk.toString() + "\n"); |
|
} |
|
const line = formatter(value); |
|
if (!line) { |
|
return cb(); |
|
} |
|
cb(undefined, line + "\n"); |
|
} |
|
} |
|
|
|
export default class PrettyCommand extends Command { |
|
static description = |
|
"Transforms sprinklers3 log output into a pretty, colorized format"; |
|
|
|
async run(): Promise<any> { |
|
pump(process.stdin, split(), new PinoPrettyTransform(), process.stdout); |
|
} |
|
}
|
|
|