diff options
Diffstat (limited to 'u/trace/logger.ts')
-rw-r--r-- | u/trace/logger.ts | 169 |
1 files changed, 96 insertions, 73 deletions
diff --git a/u/trace/logger.ts b/u/trace/logger.ts index 8e62b02..29cabd4 100644 --- a/u/trace/logger.ts +++ b/u/trace/logger.ts @@ -2,89 +2,27 @@ import { isDebug, type ITrace, type ITraceWith, - type SideEffect, type Supplier, } from "@emprespresso/pengueno"; -export interface ILogger { - log: (...args: unknown[]) => void; - debug: (...args: unknown[]) => void; - warn: (...args: unknown[]) => void; - error: (...args: unknown[]) => void; - sys: (...args: unknown[]) => void; -} -export enum LogLevel { - UNKNOWN = "UNKNOWN", - INFO = "INFO", - WARN = "WARN", - DEBUG = "DEBUG", - ERROR = "ERROR", - SYS = "SYS", -} -const logLevelOrder: Array<LogLevel> = [ - LogLevel.DEBUG, - LogLevel.INFO, - LogLevel.WARN, - LogLevel.ERROR, -]; -export const isLogLevel = (l: string): l is LogLevel => - logLevelOrder.some((level) => <string>level === l); - -const defaultAllowedLevels = () => - [ - LogLevel.UNKNOWN, - ...(isDebug() ? [LogLevel.DEBUG] : []), - LogLevel.INFO, - LogLevel.WARN, - LogLevel.ERROR, - LogLevel.SYS, - ] as Array<LogLevel>; - -export const logWithLevel = ( - logger: ILogger, - level: LogLevel, -): SideEffect<unknown> => { - switch (level) { - case LogLevel.UNKNOWN: - case LogLevel.INFO: - return logger.log; - case LogLevel.DEBUG: - return logger.debug; - case LogLevel.WARN: - return logger.warn; - case LogLevel.ERROR: - return logger.error; - case LogLevel.SYS: - return logger.sys; - } -}; - -export type LogTraceSupplier = ITraceWith<Supplier<string>>; - -const defaultTrace = () => `[${new Date().toISOString()}]`; -export const LoggerImpl = { - log: console.log, - debug: console.debug, - warn: console.warn, - error: console.error, - sys: console.log, -}; +export type LogTraceSupplier = ITraceWith<Supplier<string> | Error>; +const defaultTrace = () => `TimeStamp = ${new Date().toISOString()}`; export class LogTrace implements ITrace<LogTraceSupplier> { constructor( - private readonly logger: ILogger = LoggerImpl, + private readonly logger: ILogger = new LoggerImpl(), private readonly traces: Array<LogTraceSupplier> = [defaultTrace], + private readonly defaultLevel: LogLevel = LogLevel.INFO, private readonly allowedLevels: Supplier< Array<LogLevel> > = defaultAllowedLevels, - private readonly defaultLevel: LogLevel = LogLevel.INFO, ) {} public addTrace(trace: LogTraceSupplier): ITrace<LogTraceSupplier> { return new LogTrace( this.logger, this.traces.concat(trace), - this.allowedLevels, this.defaultLevel, + this.allowedLevels, ); } @@ -95,11 +33,7 @@ export class LogTrace implements ITrace<LogTraceSupplier> { if (!this.allowedLevels().includes(_level)) return; const level = _level === LogLevel.UNKNOWN ? this.defaultLevel : _level; - const line = { level, message: traces.at(-1), traces: traces.slice(0, -1) }; - logWithLevel( - this.logger, - level, - )(line); + this.logger.log(level, ...traces); } private foldTraces(_traces: Array<LogTraceSupplier>) { @@ -111,10 +45,99 @@ export class LogTrace implements ITrace<LogTraceSupplier> { .reduce((acc, level) => Math.max(logLevelOrder.indexOf(level), acc), -1); const level = logLevelOrder[_level] ?? LogLevel.UNKNOWN; - const traces = _logTraces.filter((trace) => !isLogLevel(trace)); + const traces = _logTraces.filter((trace) => !isLogLevel(trace)).map((trace) => { + if (typeof trace === 'object') { + return `TracedException.Name = ${trace.name}, TracedException.Message = ${trace.message}, TracedException.Stack = ${trace.stack}` + } + return trace; + }); return { level, traces, }; } } + +export enum LogLevel { + UNKNOWN = "UNKNOWN", + INFO = "INFO", + WARN = "WARN", + DEBUG = "DEBUG", + ERROR = "ERROR", + SYS = "SYS", +} +const logLevelOrder: Array<LogLevel> = [ + LogLevel.DEBUG, + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + LogLevel.SYS, +]; +export const isLogLevel = (l: unknown): l is LogLevel => + typeof l === "string" && logLevelOrder.some((level) => level === l); + + +const defaultAllowedLevels = () => + [ + LogLevel.UNKNOWN, + ...(isDebug() ? [LogLevel.DEBUG] : []), + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + LogLevel.SYS, + ] as Array<LogLevel>; + +export interface ILogger { + readonly log: (level: LogLevel, ...args: string[]) => void; +} +class LoggerImpl implements ILogger { + private readonly textEncoder = new TextEncoder(); + + public log(level: LogLevel, ...msg: string[]) { + const message = JSON.stringify({ + level, + msg, + }, null, 2); + const styled = `${this.getStyle(level)}${message}${ANSI.RESET}\n`; + this.getStream(level).writeSync(this.textEncoder.encode(styled)); + } + + private getStream(level: LogLevel) { + if (level === LogLevel.ERROR) { + return Deno.stderr; + } + return Deno.stdout; + } + + private getStyle(level: LogLevel) { + switch (level) { + case LogLevel.UNKNOWN: + case LogLevel.INFO: + return `${ANSI.MAGENTA}`; + case LogLevel.DEBUG: + return `${ANSI.CYAN}`; + case LogLevel.WARN: + return `${ANSI.BRIGHT_YELLOW}`; + case LogLevel.ERROR: + return `${ANSI.BRIGHT_RED}`; + case LogLevel.SYS: + return `${ANSI.DIM}${ANSI.BLUE}`; + } + } +} + +export const ANSI = { + RESET: "\x1b[0m", + BOLD: "\x1b[1m", + DIM: "\x1b[2m", + RED: "\x1b[31m", + GREEN: "\x1b[32m", + YELLOW: "\x1b[33m", + BLUE: "\x1b[34m", + MAGENTA: "\x1b[35m", + CYAN: "\x1b[36m", + WHITE: "\x1b[37m", + BRIGHT_RED: "\x1b[91m", + BRIGHT_YELLOW: "\x1b[93m", + GRAY: "\x1b[90m", +}; |