this repo has no description
1const COLOURS = {
2 RESET: "\x1b[0m",
3 BRIGHT: "\x1b[1m",
4 DIM: "\x1b[2m",
5 UNDERSCORE: "\x1b[4m",
6 BLINK: "\x1b[5m",
7 REVERSE: "\x1b[7m",
8 HIDDEN: "\x1b[8m",
9
10 FG_BLACK: "\x1b[30m",
11 FG_RED: "\x1b[31m",
12 FG_GREEN: "\x1b[32m",
13 FG_YELLOW: "\x1b[33m",
14 FG_BLUE: "\x1b[34m",
15 FG_MAGENTA: "\x1b[35m",
16 FG_CYAN: "\x1b[36m",
17 FG_WHITE: "\x1b[37m",
18
19 BG_BLACK: "\x1b[40m",
20 BG_RED: "\x1b[41m",
21 BG_GREEN: "\x1b[42m",
22 BG_YELLOW: "\x1b[43m",
23 BG_BLUE: "\x1b[44m",
24 BG_MAGENTA: "\x1b[45m",
25 BG_CYAN: "\x1b[46m",
26 BG_WHITE: "\x1b[47m",
27};
28
29enum LogLevel {
30 DEBUG = 0,
31 INFO = 1,
32 WARN = 2,
33 ERROR = 3,
34 FATAL = 4,
35 OFF = 5,
36}
37
38const methodMaps = {
39 [LogLevel.DEBUG]: console.debug,
40 [LogLevel.INFO]: console.info,
41 [LogLevel.WARN]: console.warn,
42 [LogLevel.ERROR]: console.error,
43 [LogLevel.FATAL]: console.error,
44 [LogLevel.OFF]: () => {},
45};
46
47interface LogOptions {
48 timestamp?: boolean;
49 colourize?: boolean;
50 scopecolour?: string;
51}
52
53class Logger {
54 private static logLevel: LogLevel = LogLevel.INFO;
55 private scope: string[];
56 private options: LogOptions;
57 private static readonly MAX_LEVEL_LENGTH = Math.max(
58 ...Object.keys(LogLevel)
59 .filter((k) => Number.isNaN(Number(k)))
60 .map((k) => k.length),
61 );
62
63 private static readonly SCOPE_COLOURS = [
64 COLOURS.FG_RED,
65 COLOURS.FG_GREEN,
66 COLOURS.FG_YELLOW,
67 COLOURS.FG_BLUE,
68 COLOURS.FG_MAGENTA,
69 COLOURS.FG_CYAN,
70 ];
71
72 private static scopeColours: { [scopeName: string]: string } = {};
73
74 constructor(scope: string | string[] = [], options: LogOptions = {}) {
75 this.scope = Array.isArray(scope) ? scope : [scope];
76 this.options = {
77 timestamp: true,
78 colourize: true,
79 scopecolour: this.getScopecolour(this.scope.join("/")),
80 ...options,
81 };
82
83 if (typeof window === "undefined") {
84 if (process.env.LOG_LEVEL) {
85 try {
86 const envLogLevel = process.env.LOG_LEVEL.toUpperCase();
87 if (envLogLevel in LogLevel) {
88 Logger.setLogLevel(LogLevel[envLogLevel as keyof typeof LogLevel]);
89 } else {
90 console.warn(
91 `Invalid LOG_LEVEL environment variable: ${process.env.LOG_LEVEL}. Using default: INFO.`,
92 );
93 }
94 } catch (e) {
95 console.warn(`Error parsing LOG_LEVEL: ${e}. Using default: INFO.`);
96 }
97 }
98 }
99 }
100
101 static setLogLevel(level: LogLevel): void {
102 Logger.logLevel = level;
103 }
104
105 private getLevelcolour(level: LogLevel): string {
106 switch (level) {
107 case LogLevel.DEBUG:
108 return COLOURS.FG_WHITE;
109 case LogLevel.INFO:
110 return COLOURS.FG_GREEN;
111 case LogLevel.WARN:
112 return COLOURS.FG_YELLOW;
113 case LogLevel.ERROR:
114 return COLOURS.FG_RED;
115 case LogLevel.FATAL:
116 return COLOURS.BG_RED + COLOURS.FG_WHITE;
117 default:
118 return COLOURS.RESET;
119 }
120 }
121
122 private getScopecolour(scopeName: string): string {
123 if (Logger.scopeColours[scopeName]) {
124 return Logger.scopeColours[scopeName];
125 }
126
127 const colourIndex =
128 Object.keys(Logger.scopeColours).length % Logger.SCOPE_COLOURS.length;
129 const colour = Logger.SCOPE_COLOURS[colourIndex];
130 Logger.scopeColours[scopeName] = colour;
131 return colour;
132 }
133
134 private formatMessage(level: LogLevel, message: string): string {
135 const now = new Date();
136 const timestamp = this.options.timestamp
137 ? `${COLOURS.DIM}[${now.toLocaleString()}]${COLOURS.RESET} `
138 : "";
139 const levelString = LogLevel[level];
140 const scopeString =
141 this.scope.length > 0 ? `[${this.scope.join("/")}] ` : "";
142 const levelcolour = this.getLevelcolour(level);
143 const colourStart = this.options.colourize ? levelcolour : "";
144 const colourEnd = this.options.colourize ? COLOURS.RESET : "";
145
146 const formattedMessage = `${timestamp}${colourStart}[${levelString}]${colourEnd} ${this.options.colourize && this.options.scopecolour ? this.options.scopecolour : ""}${scopeString}${COLOURS.RESET}${message}`;
147 return formattedMessage;
148 }
149
150 private log(level: LogLevel, message: string, ...args: unknown[]): void {
151 if (level >= Logger.logLevel && level !== LogLevel.OFF) {
152 const log = methodMaps[level];
153
154 if (typeof window !== "undefined") {
155 const levelString = LogLevel[level];
156 const scopeString =
157 this.scope.length > 0 ? `[${this.scope.join("/")}]` : "";
158
159 let levelStyle = "";
160 switch (level) {
161 case LogLevel.DEBUG:
162 levelStyle = "color: gray; font-weight: normal;";
163 break;
164 case LogLevel.INFO:
165 levelStyle = "color: green; font-weight: bold;";
166 break;
167 case LogLevel.WARN:
168 levelStyle = "color: orange; font-weight: bold;";
169 break;
170 case LogLevel.ERROR:
171 levelStyle = "color: red; font-weight: bold;";
172 break;
173 case LogLevel.FATAL:
174 levelStyle =
175 "color: white; background-color: red; font-weight: bold; padding: 2px 5px;";
176 break;
177 }
178
179 const scopeStyle = "color: magenta; font-weight: bold;";
180
181 log(
182 `%c[${levelString}]%c ${scopeString} ${message}`,
183 levelStyle,
184 scopeStyle,
185 ...args,
186 );
187 return;
188 }
189
190 const formattedMessage = this.formatMessage(level, message);
191 log(formattedMessage, ...args);
192 }
193 }
194
195 debug(message: string, ...args: unknown[]): void {
196 this.log(LogLevel.DEBUG, message, ...args);
197 }
198
199 info(message: string, ...args: unknown[]): void {
200 this.log(LogLevel.INFO, message, ...args);
201 }
202
203 warn(message: string, ...args: unknown[]): void {
204 this.log(LogLevel.WARN, message, ...args);
205 }
206
207 error(message: string, ...args: unknown[]): void {
208 this.log(LogLevel.ERROR, message, ...args);
209 }
210
211 fatal(message: string, ...args: unknown[]): void {
212 this.log(LogLevel.FATAL, message, ...args);
213 }
214
215 child(scope: string | string[], options: LogOptions = {}): Logger {
216 const newScope = Array.isArray(scope)
217 ? [...this.scope, ...scope]
218 : [...this.scope, scope];
219 return new Logger(newScope, { ...this.options, ...options });
220 }
221}
222
223const singleton = new Logger("global", {
224 timestamp: true,
225 colourize: true,
226 scopecolour: COLOURS.FG_MAGENTA,
227});
228
229export default singleton;
230export { Logger, LogLevel, COLOURS };