-
Notifications
You must be signed in to change notification settings - Fork 83
/
Copy pathindex.js
341 lines (301 loc) · 9.52 KB
/
index.js
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
const fs = require("node:fs");
class RollingSizeOptions {
static OneKB = 1024;
static FiveKB = 5 * 1024;
static TenKB = 10 * 1024;
static TwentyKB = 20 * 1024;
static FiftyKB = 50 * 1024;
static HundredKB = 100 * 1024;
static HalfMB = 512 * 1024;
static OneMB = 1024 * 1024;
static FiveMB = 5 * 1024 * 1024;
static TenMB = 10 * 1024 * 1024;
static TwentyMB = 20 * 1024 * 1024;
static FiftyMB = 50 * 1024 * 1024;
static HundredMB = 100 * 1024 * 1024;
static assert(size_threshold) {
if (typeof size_threshold !== "number" || size_threshold < RollingSizeOptions.OneKB) {
throw new Error(
`size_threshold must be at-least 1 KB. Unsupported param ${JSON.stringify(size_threshold)}`
);
}
}
}
class RollingTimeOptions {
static Minutely = 60; // Every 60 seconds
static Hourly = 60 * this.Minutely;
static Daily = 24 * this.Hourly;
static Weekly = 7 * this.Daily;
static Monthly = 30 * this.Daily;
static Yearly = 12 * this.Monthly;
static assert(time_option) {
if (![this.Minutely, this.Hourly, this.Daily, this.Weekly, this.Monthly, this.Yearly].includes(time_option)) {
throw new Error(
`time_option must be an instance of RollingConfig. Unsupported param ${JSON.stringify(time_option)}`
);
}
}
}
class RollingConfig {
/**
* @type {RollingTimeOptions}
* @private
*
* @description Units - seconds
*/
#time_threshold = RollingTimeOptions.Hourly;
#size_threshold = RollingSizeOptions.FiveMB;
/**
* @returns {RollingConfig} A new instance of RollingConfig with default values.
*/
static with_defaults() {
return new RollingConfig();
}
/**
* @param {number} size_threshold Roll/Create new file every time the current file size exceeds this threshold.
* @returns {RollingConfig} The current instance of RollingConfig.
*/
with_size_threshold(size_threshold) {
RollingSizeOptions.assert(size_threshold);
this.#size_threshold = size_threshold;
return this;
}
/**
* @param {RollingTimeOptions} time_threshold Roll/Create new file every time the current file size exceeds this threshold.
* @returns {RollingConfig} The current instance of RollingConfig.
* @throws {Error} If the time_threshold is not an instance of RollingTimeOptions.
*/
with_time_threshold(time_threshold) {
RollingTimeOptions.assert(time_threshold);
this.#time_threshold = time_threshold;
return this;
}
/**
* @param {Object} json The json object to be parsed into {RollingConfig}.
* @returns {RollingConfig} A new instance of RollingConfig with values from the json object.
* @throws {Error} If the json is not an object.
*/
static from_json(json) {
let rolling_config = new RollingConfig();
Object.keys(json).forEach((key) => {
switch (key) {
case "size_threshold":
rolling_config = rolling_config.with_size_threshold(json[key]);
break;
case "time_threshold":
rolling_config = rolling_config.with_time_threshold(json[key]);
break;
}
});
return rolling_config;
}
}
class LogConfig {
/**
* @type {LogLevel}
* @private
* @description The log level to be used.
*/
#level = LogLevel.Info;
/**
* @type {RollingConfig}
* @private
*/
#rolling_config;
/**
* @type {string}
* @private
* @description The prefix to be used for the log file name.
*
* If the file prefix is `MyFilePrefix_` the log files created will have the name
* `MyFilePrefix_2021-09-01.log`, `MyFilePrefix_2021-09-02.log` and so on.
*/
#file_prefix = "Logtar_";
constructor() {
this.#rolling_config = RollingConfig.with_defaults();
}
/**
* @returns {LogConfig} A new instance of LogConfig with default values.
*/
static with_defaults() {
return new LogConfig();
}
/**
* @param {string} file_path The path to the config file.
* @returns {LogConfig} A new instance of LogConfig with values from the config file.
* @throws {Error} If the file_path is not a string.
*/
static from_file(file_path) {
const file_contents = fs.readFileSync(file_path);
return LogConfig.from_json(JSON.parse(file_contents));
}
/**
* @param {Object} json The json object to be parsed into {LogConfig}.
* @returns {LogConfig} A new instance of LogConfig with values from the json object.
*/
static from_json(json) {
let log_config = new LogConfig();
Object.keys(json).forEach((key) => {
switch (key) {
case "level":
log_config = log_config.with_log_level(json[key]);
break;
case "rolling_config":
log_config = log_config.with_rolling_config(json[key]);
break;
case "file_prefix":
log_config = log_config.with_file_prefix(json[key]);
break;
}
});
return log_config;
}
static assert(log_config) {
if (arguments.length > 0 && !(log_config instanceof LogConfig)) {
throw new Error(
`log_config must be an instance of LogConfig. Unsupported param ${JSON.stringify(log_config)}`
);
}
}
/**
* @returns {LogLevel} The current log level.
*/
get level() {
return this.#level;
}
/**
* @param {LogLevel} log_level The log level to be set.
* @returns {LogConfig} The current instance of LogConfig.
* @throws {Error} If the log_level is not an instance of LogLevel.
*/
with_log_level(log_level) {
LogLevel.assert(log_level);
this.#level = log_level;
return this;
}
/**
* @returns {RollingConfig} The current rolling config.
*/
get rolling_config() {
return this.#rolling_config;
}
/**
* @param {RollingConfig} config The rolling config to be set.
* @returns {LogConfig} The current instance of LogConfig.
* @throws {Error} If the config is not an instance of RollingConfig.
*/
with_rolling_config(config) {
this.#rolling_config = RollingConfig.from_json(config);
return this;
}
/**
* @returns {String} The current max file size.
*/
get file_prefix() {
return this.#file_prefix;
}
/**
* @param {string} file_prefix The file prefix to be set.
* @returns {LogConfig} The current instance of LogConfig.
* @throws {Error} If the file_prefix is not a string.
*/
with_file_prefix(file_prefix) {
if (typeof file_prefix !== "string") {
throw new Error(`file_prefix must be a string. Unsupported param ${JSON.stringify(file_prefix)}`);
}
this.#file_prefix = file_prefix;
return this;
}
}
class LogLevel {
static #Debug = 0;
static #Info = 1;
static #Warn = 2;
static #Error = 3;
static #Critical = 4;
/**
* @returns {LogLevel} A new instance of LogLevel with default values.
*/
static get Debug() {
return this.#Debug;
}
/**
* @returns {LogLevel} A new instance of LogLevel with default values.
*/
static get Info() {
return this.#Info;
}
/**
* @returns {LogLevel} A new instance of LogLevel with default values.
*/
static get Warn() {
return this.#Warn;
}
/**
* @returns {LogLevel} A new instance of LogLevel with default values.
*/
static get Error() {
return this.#Error;
}
/**
* @returns {LogLevel} A new instance of LogLevel with default values.
*/
static get Critical() {
return this.#Critical;
}
static assert(log_level) {
if (![this.Debug, this.Info, this.Warn, this.Error, this.Critical].includes(log_level)) {
throw new Error(
`log_level must be an instance of LogLevel. Unsupported param ${JSON.stringify(log_level)}`
);
}
}
}
class Logger {
/**
* @type {LogConfig}
*/
#config;
/**
* @returns {Logger} A new instance of Logger with default values.
* @description The default log level is `LogLevel.Info`.
*/
static with_defaults() {
return new Logger();
}
/**
* @param {LogConfig} log_config The log config to be used.
* @returns {Logger} A new instance of Logger with the specified log config.
* @throws {Error} If the log_config is not an instance of LogConfig.
*/
static with_config(log_config) {
return new Logger(log_config);
}
constructor(log_config) {
log_config = log_config || LogConfig.with_defaults();
LogConfig.assert(log_config);
this.#config = log_config;
}
/**
* Get the current log level.
*
* @returns {LogLevel} The current log level.
*
* const logger = new Logger(LogLevel.Debug);
* console.log(logger.level); // LogLevel.Debug
* logger.level = LogLevel.Error; // throws error
* logger.level = LogLevel.Debug; // works fine
* logger.level = 0; // throws error
*/
get level() {
return this.#config.level;
}
}
module.exports = {
Logger,
LogLevel,
LogConfig,
RollingConfig,
RollingSizeOptions,
RollingTimeOptions,
};