1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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}`);
},
};
};
|