diff options
Diffstat (limited to 'u/trace/log')
-rw-r--r-- | u/trace/log/ansi.ts | 15 | ||||
-rw-r--r-- | u/trace/log/index.ts | 5 | ||||
-rw-r--r-- | u/trace/log/level.ts | 19 | ||||
-rw-r--r-- | u/trace/log/logger.ts | 5 | ||||
-rw-r--r-- | u/trace/log/pretty_json_console.ts | 39 | ||||
-rw-r--r-- | u/trace/log/trace.ts | 60 |
6 files changed, 143 insertions, 0 deletions
diff --git a/u/trace/log/ansi.ts b/u/trace/log/ansi.ts new file mode 100644 index 0000000..7ff16a3 --- /dev/null +++ b/u/trace/log/ansi.ts @@ -0,0 +1,15 @@ +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', +}; diff --git a/u/trace/log/index.ts b/u/trace/log/index.ts new file mode 100644 index 0000000..670e333 --- /dev/null +++ b/u/trace/log/index.ts @@ -0,0 +1,5 @@ +export * from './ansi.js'; +export * from './level.js'; +export * from './logger.js'; +export * from './pretty_json_console.js'; +export * from './trace.js'; diff --git a/u/trace/log/level.ts b/u/trace/log/level.ts new file mode 100644 index 0000000..027dd71 --- /dev/null +++ b/u/trace/log/level.ts @@ -0,0 +1,19 @@ +export enum LogLevel { + UNKNOWN = 'UNKNOWN', + INFO = 'INFO', + WARN = 'WARN', + DEBUG = 'DEBUG', + ERROR = 'ERROR', + SYS = 'SYS', +} + +export 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); diff --git a/u/trace/log/logger.ts b/u/trace/log/logger.ts new file mode 100644 index 0000000..3ced60a --- /dev/null +++ b/u/trace/log/logger.ts @@ -0,0 +1,5 @@ +import { LogLevel } from './level.js'; + +export interface ILogger { + readonly log: (level: LogLevel, ...args: string[]) => void; +} diff --git a/u/trace/log/pretty_json_console.ts b/u/trace/log/pretty_json_console.ts new file mode 100644 index 0000000..758af51 --- /dev/null +++ b/u/trace/log/pretty_json_console.ts @@ -0,0 +1,39 @@ +import { ANSI, LogLevel, ILogger } from './index.js'; + +export class PrettyJsonConsoleLogger implements ILogger { + public log(level: LogLevel, ...trace: string[]) { + const message = JSON.stringify( + { + level, + trace, + }, + null, + 4, + ); + const styled = `${this.getStyle(level)}${message}${ANSI.RESET}\n`; + this.getStream(level)(styled); + } + + private getStream(level: LogLevel) { + if (level === LogLevel.ERROR) { + return console.error; + } + return console.log; + } + + 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}`; + } + } +} diff --git a/u/trace/log/trace.ts b/u/trace/log/trace.ts new file mode 100644 index 0000000..3f9f1b2 --- /dev/null +++ b/u/trace/log/trace.ts @@ -0,0 +1,60 @@ +import { isDebug, ITrace, ITraceWith, memoize, Supplier } from '@emprespresso/pengueno'; +import { ILogger, isLogLevel, LogLevel, logLevelOrder, PrettyJsonConsoleLogger } from './index.js'; + +export type LogTraceSupplier = ITraceWith<Supplier<string>> | ITraceWith<Error>; + +export class LogTrace implements ITrace<LogTraceSupplier> { + constructor( + private readonly logger: ILogger = new PrettyJsonConsoleLogger(), + private readonly traces: Array<LogTraceSupplier> = [defaultTrace], + private readonly defaultLevel: LogLevel = LogLevel.INFO, + private readonly allowedLevels: Supplier<Set<LogLevel>> = defaultAllowedLevelsSupplier, + ) {} + + public traceScope(trace: LogTraceSupplier): ITrace<LogTraceSupplier> { + return new LogTrace(this.logger, this.traces.concat(trace), this.defaultLevel, this.allowedLevels); + } + + public trace(trace: LogTraceSupplier) { + const { traces, level: _level } = this.foldTraces(this.traces.concat(trace)); + if (!this.allowedLevels().has(_level)) return; + + const level = _level === LogLevel.UNKNOWN ? this.defaultLevel : _level; + this.logger.log(level, ...traces); + } + + private foldTraces(_traces: Array<LogTraceSupplier>) { + const _logTraces = _traces.map((trace) => (typeof trace === 'function' ? trace() : trace)); + const _level = _logTraces + .filter((trace) => isLogLevel(trace)) + .reduce((acc, level) => Math.max(logLevelOrder.indexOf(level), acc), -1); + const level = logLevelOrder[_level] ?? LogLevel.UNKNOWN; + + 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, + }; + } +} + +const defaultTrace = () => `TimeStamp = ${new Date().toISOString()}`; +const defaultAllowedLevels = memoize( + (isDebug: boolean) => + new Set([ + LogLevel.UNKNOWN, + ...(isDebug ? [LogLevel.DEBUG] : []), + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + LogLevel.SYS, + ]), +); +const defaultAllowedLevelsSupplier = () => defaultAllowedLevels(isDebug()); |