Skip to main content
Module

x/scaffold/src/logger.ts

scaffold your next project with style and πŸ’—
Latest
File
import { colors } from "./deps/cli.ts";import { isArray, isBoolean, isError, isNullOrUndefined, isNumber, isObject, isPrimitive, isString, isSymbol,} from "./deps/npm.ts";import { BaseHandler, HandlerOptions, LevelName, Logger, LogLevels, LogRecord,} from "./deps/std.ts";
export class ConsoleHandler extends BaseHandler { constructor( level: LevelName, options: HandlerOptions = { formatter: "{levelName} {msg} {args}" }, ) { super(level, options); } override format(logRecord: LogRecord): string { if (this.formatter instanceof Function) { return this.formatter(logRecord); }
let symbol = ""; let badge = "";
switch (logRecord.level) { case LogLevels.DEBUG: symbol = `${symbols["DEBUG"]} `; badge = colors.black.bgRgb24(" debug ", 0xdddddd); break; case LogLevels.INFO: symbol = `${symbols["INFO"]} `; badge = colors.bgBlue.black(" info "); break; case LogLevels.WARNING: symbol = `${symbols["WARNING"]} `; badge = colors.bgBrightYellow.black(" warn "); break; case LogLevels.ERROR: symbol = `${symbols["ERROR"]} `; badge = colors.bgRed.white(" error "); break; case LogLevels.CRITICAL: symbol = `${symbols["ERROR"]} `; badge = colors.bold.bgRed.blue(" fatal "); break; default: break; }
return this.formatter.replace(/{([^\s}]+)}/g, (match, p1): string => { if (p1 === "symbol") { return symbol; }
if (p1 === "badge") { return badge; }
const value = logRecord[p1 as keyof LogRecord]; // do not interpolate missing values if (value == null) { return match; }
if (value instanceof Date || isObject(value)) { return Deno.inspect(value, { colors: !Deno.noColor }); }
if (!isArray(value) || p1 !== "args") { return logRecord.level === LogLevels.CRITICAL ? colors.red(String(value)) : String(value); }
const args: string[] = [];
for (const arg of value) { if (arg) { args.push( isPrimitive(arg) ? String(arg) : Deno.inspect(arg, { colors: !Deno.noColor }), ); } }
return args.join(" "); }); }
override log(msg: string): void { console.log(msg); }}
// it would be better to use `isUnicodeSupported()` and `isColorSupported()` but// they require extra permissions.const symbols: Record<LevelName, string> = Deno.build.os === "windows" ? { NOTSET: "", CRITICAL: colors.bold.red("Γ—"), ERROR: colors.red("Γ—"), WARNING: colors.yellow("β€Ό"), INFO: colors.blue("i"), DEBUG: colors.gray("β€Ί"), } : { NOTSET: "", CRITICAL: colors.bold.red("ⓧ"), ERROR: colors.red("βœ–"), WARNING: colors.yellow("⚠"), INFO: colors.blue("β„Ή"), // success: colors.green('βœ”'), DEBUG: colors.gray("β€Ί"), };
class BetterLogger extends Logger { override asString(data: unknown): string { if (isString(data)) { return data; } else if ( isNullOrUndefined(data) || isNumber(data) || typeof data === "bigint" || isBoolean(data) || isSymbol(data) ) { return String(data); } else if (isError(data)) { return data.stack ?? ""; } else if (typeof data === "object") { return Deno.inspect(data, { colors: !Deno.noColor }); }
return "[unknown]"; }}
interface CreateLoggerProps { name: string; levelName?: LevelName; formatter?: string;}
/** * Create a logger with the given name and level. */export function createLogger( props: CreateLoggerProps,) { const { name, levelName = "CRITICAL", formatter = `{symbol} ${colors.gray(name)} {msg} {args}`, } = props;
return new BetterLogger(name, levelName, { handlers: [new ConsoleHandler(levelName, { formatter })], });}