summaryrefslogtreecommitdiff
path: root/u/trace/logger.ts
diff options
context:
space:
mode:
Diffstat (limited to 'u/trace/logger.ts')
-rw-r--r--u/trace/logger.ts108
1 files changed, 108 insertions, 0 deletions
diff --git a/u/trace/logger.ts b/u/trace/logger.ts
new file mode 100644
index 0000000..a5739c8
--- /dev/null
+++ b/u/trace/logger.ts
@@ -0,0 +1,108 @@
+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;
+}
+export enum LogLevel {
+ UNKNOWN = "UNKNOWN",
+ INFO = "INFO",
+ WARN = "WARN",
+ DEBUG = "DEBUG",
+ ERROR = "ERROR",
+}
+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,
+ ] 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;
+ }
+};
+
+export type LogTraceSupplier = ITraceWith<Supplier<string>>;
+
+const defaultTrace = () => `[${new Date().toISOString()}]`;
+export const LoggerImpl = console;
+export class LogTrace implements ITrace<LogTraceSupplier> {
+ constructor(
+ private readonly logger: ILogger = LoggerImpl,
+ private readonly traces: Array<LogTraceSupplier> = [defaultTrace],
+ 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,
+ );
+ }
+
+ public trace(trace: LogTraceSupplier) {
+ const { line, level: _level } = this.foldTraces(this.traces.concat(trace));
+ if (!this.allowedLevels().includes(_level)) return;
+
+ const level = _level === LogLevel.UNKNOWN ? this.defaultLevel : _level;
+ logWithLevel(this.logger, level)(`[${level}]${line}`);
+ }
+
+ private foldTraces(traces: Array<LogTraceSupplier>) {
+ const { line, level } = traces.reduce(
+ (acc: { line: string; level: number }, t) => {
+ const val = typeof t === "function" ? t() : t;
+ if (isLogLevel(val)) {
+ return {
+ ...acc,
+ level: Math.max(logLevelOrder.indexOf(val), acc.level),
+ };
+ }
+ const prefix = [
+ acc.line,
+ val,
+ ].join(" ");
+ return { ...acc, prefix };
+ },
+ { line: "", level: -1 },
+ );
+ return { line, level: logLevelOrder[level] ?? LogLevel.UNKNOWN };
+ }
+}