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.ts86
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}`);
+ },
+ };
+};