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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
import {
isDebug,
type ITrace,
type ITraceWith,
type Supplier,
} from "@emprespresso/pengueno";
export type LogTraceSupplier = ITraceWith<Supplier<string> | Error>;
const defaultTrace = () => `TimeStamp = ${new Date().toISOString()}`;
export class LogTrace implements ITrace<LogTraceSupplier> {
constructor(
private readonly logger: ILogger = new LoggerImpl(),
private readonly traces: Array<LogTraceSupplier> = [defaultTrace],
private readonly defaultLevel: LogLevel = LogLevel.INFO,
private readonly allowedLevels: Supplier<
Array<LogLevel>
> = defaultAllowedLevels,
) {}
public addTrace(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().includes(_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,
};
}
}
export enum LogLevel {
UNKNOWN = "UNKNOWN",
INFO = "INFO",
WARN = "WARN",
DEBUG = "DEBUG",
ERROR = "ERROR",
SYS = "SYS",
}
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);
const defaultAllowedLevels = () =>
[
LogLevel.UNKNOWN,
...(isDebug() ? [LogLevel.DEBUG] : []),
LogLevel.INFO,
LogLevel.WARN,
LogLevel.ERROR,
LogLevel.SYS,
] as Array<LogLevel>;
export interface ILogger {
readonly log: (level: LogLevel, ...args: string[]) => void;
}
class LoggerImpl implements ILogger {
private readonly textEncoder = new TextEncoder();
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).writeSync(this.textEncoder.encode(styled));
}
private getStream(level: LogLevel) {
if (level === LogLevel.ERROR) {
return Deno.stderr;
}
return Deno.stdout;
}
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}`;
}
}
}
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",
};
|