diff options
Diffstat (limited to 'u/trace/logger.ts')
-rw-r--r-- | u/trace/logger.ts | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/u/trace/logger.ts b/u/trace/logger.ts new file mode 100644 index 0000000..79da367 --- /dev/null +++ b/u/trace/logger.ts @@ -0,0 +1,86 @@ +import { + isDebug, + isObject, + type ITrace, + type SideEffect, + type Supplier, +} from "@emprespresso/utils"; + +export interface ILogger { + log: (...args: unknown[]) => void; + debug: (...args: unknown[]) => void; + warn: (...args: unknown[]) => void; + error: (...args: unknown[]) => void; +} +export type ILoggerLevel = "UNKNOWN" | "INFO" | "WARN" | "DEBUG" | "ERROR"; +const logLevelOrder: Array<ILoggerLevel> = ["DEBUG", "INFO", "WARN", "ERROR"]; +const defaultAllowedLevels = () => + [ + "UNKNOWN", + ...(isDebug() ? ["DEBUG"] : []), + "INFO", + "WARN", + "ERROR", + ] as Array<ILoggerLevel>; + +export const logWithLevel = ( + logger: ILogger, + level: ILoggerLevel, +): SideEffect<unknown> => { + switch (level) { + case "UNKNOWN": + case "INFO": + return logger.log; + case "DEBUG": + return logger.debug; + case "WARN": + return logger.warn; + case "ERROR": + return logger.error; + } +}; + +export const LoggerImpl = console; + +export type LogTraceSupplier = string | Supplier<string> | { + level: ILoggerLevel; +}; + +const foldTraces = (traces: Array<LogTraceSupplier>) => { + const { line, level } = traces.reduce( + (acc: { line: string; level: number }, t) => { + if (isObject(t) && "level" in t) { + return { + ...acc, + level: Math.max(logLevelOrder.indexOf(t.level), acc.level), + }; + } + const prefix = [ + acc.line, + typeof t === "function" ? t() : t, + ].join(" "); + return { ...acc, prefix }; + }, + { line: "", level: -1 }, + ); + return { line, level: logLevelOrder[level] ?? "UNKNOWN" }; +}; + +const defaultTrace = () => `[${new Date().toISOString()}]`; +export const LogTrace = ( + logger: ILogger, + traces: Array<LogTraceSupplier> = [defaultTrace], + allowedLevels: Supplier<Array<ILoggerLevel>> = defaultAllowedLevels, + defaultLevel: ILoggerLevel = "INFO", +): ITrace<LogTraceSupplier> => { + return { + addTrace: (trace: LogTraceSupplier) => + LogTrace(logger, traces.concat(trace)), + trace: (trace: LogTraceSupplier) => { + const { line, level: _level } = foldTraces(traces.concat(trace)); + if (!allowedLevels().includes(_level)) return; + const level = _level === "UNKNOWN" ? defaultLevel : _level; + logWithLevel(logger, level)(`[${level}]${line}`); + }, + }; +}; |