metalog 4.0.2-prerelease → 4.0.3-prerelease
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/metalog.d.ts +0 -16
- package/metalog.js +91 -83
- package/package.json +7 -7
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -361,6 +361,6 @@ logger.console.debug('Debug information', { data: 'value' });
|
|
|
361
361
|
|
|
362
362
|
## License & Contributors
|
|
363
363
|
|
|
364
|
-
Copyright (c) 2017-
|
|
364
|
+
Copyright (c) 2017-2026 [Metarhia contributors](https://github.com/metarhia/metalog/graphs/contributors).
|
|
365
365
|
Metalog is [MIT licensed](./LICENSE).\
|
|
366
366
|
Metalog is a part of [Metarhia](https://github.com/metarhia) technology stack.
|
package/metalog.d.ts
CHANGED
|
@@ -79,22 +79,6 @@ export class Logger extends EventEmitter {
|
|
|
79
79
|
rotate(): Promise<void>;
|
|
80
80
|
write(tag: string, indent: number, args: unknown[]): void;
|
|
81
81
|
flush(callback?: (error?: Error) => void): void;
|
|
82
|
-
|
|
83
|
-
#options: LoggerOptions;
|
|
84
|
-
#worker: string;
|
|
85
|
-
#createStream: () => NodeJS.WritableStream;
|
|
86
|
-
#keepDays: number;
|
|
87
|
-
#stream: NodeJS.WritableStream | null;
|
|
88
|
-
#rotationTimer: NodeJS.Timer | null;
|
|
89
|
-
#file: string;
|
|
90
|
-
#fsEnabled: boolean;
|
|
91
|
-
#toFile: Record<string, boolean> | null;
|
|
92
|
-
#toStdout: Record<string, boolean> | null;
|
|
93
|
-
#buffer: BufferedStream | null;
|
|
94
|
-
#formatter: Formatter;
|
|
95
|
-
|
|
96
|
-
#createDir(): Promise<void>;
|
|
97
|
-
#setupCrashHandling(): void;
|
|
98
82
|
}
|
|
99
83
|
|
|
100
84
|
export function nowDays(): number;
|
package/metalog.js
CHANGED
|
@@ -23,6 +23,16 @@ const TIME_END = TIME_START + 'HH:MM:SS'.length;
|
|
|
23
23
|
|
|
24
24
|
const LOG_TAGS = ['log', 'info', 'warn', 'debug', 'error'];
|
|
25
25
|
|
|
26
|
+
const CRASH_EVENTS = [
|
|
27
|
+
'SIGTERM',
|
|
28
|
+
'SIGINT',
|
|
29
|
+
'SIGUSR1',
|
|
30
|
+
'SIGUSR2',
|
|
31
|
+
'uncaughtException',
|
|
32
|
+
'unhandledRejection',
|
|
33
|
+
'exit',
|
|
34
|
+
];
|
|
35
|
+
|
|
26
36
|
const TAG_COLOR = concolor({
|
|
27
37
|
log: 'b,black/white',
|
|
28
38
|
info: 'b,white/blue',
|
|
@@ -55,11 +65,10 @@ const logTags = (tags) => {
|
|
|
55
65
|
|
|
56
66
|
const nowDays = () => {
|
|
57
67
|
const now = new Date();
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
return Math.floor(date.getTime() / DAY_MILLISECONDS);
|
|
68
|
+
const y = now.getUTCFullYear();
|
|
69
|
+
const m = now.getUTCMonth();
|
|
70
|
+
const d = now.getUTCDate();
|
|
71
|
+
return Math.floor(Date.UTC(y, m, d) / DAY_MILLISECONDS);
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
const nameToDays = (fileName = '') => {
|
|
@@ -68,12 +77,11 @@ const nameToDays = (fileName = '') => {
|
|
|
68
77
|
}
|
|
69
78
|
const date = fileName.substring(0, DATE_LEN);
|
|
70
79
|
const [year, month, day] = date.split('-').map(Number);
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
if (isNaN(fileTime)) {
|
|
80
|
+
const utc = Date.UTC(year, month - 1, day);
|
|
81
|
+
if (isNaN(utc)) {
|
|
74
82
|
throw new Error(`Invalid filename: ${fileName}`);
|
|
75
83
|
}
|
|
76
|
-
return Math.floor(
|
|
84
|
+
return Math.floor(utc / DAY_MILLISECONDS);
|
|
77
85
|
};
|
|
78
86
|
|
|
79
87
|
const getNextReopen = () => {
|
|
@@ -165,7 +173,7 @@ class Formatter {
|
|
|
165
173
|
const markColor = TAG_COLOR[tag];
|
|
166
174
|
const time = normalColor(dateTime.substring(TIME_START, TIME_END));
|
|
167
175
|
const id = normalColor(this.#worker);
|
|
168
|
-
const mark = markColor(
|
|
176
|
+
const mark = markColor(` ${tag.padEnd(TAG_LENGTH)}`);
|
|
169
177
|
const msg = normalColor(message);
|
|
170
178
|
return `${time} ${id} ${mark} ${msg}`;
|
|
171
179
|
}
|
|
@@ -173,30 +181,31 @@ class Formatter {
|
|
|
173
181
|
formatFile(tag, indent, args) {
|
|
174
182
|
const dateTime = new Date().toISOString();
|
|
175
183
|
const message = this.format(tag, indent, args);
|
|
176
|
-
const msg =
|
|
184
|
+
const msg = message.replaceAll('\n', LINE_SEPARATOR);
|
|
177
185
|
return `${dateTime} [${tag}] ${msg}`;
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
formatJson(tag, indent, args) {
|
|
181
189
|
const timestamp = new Date().toISOString();
|
|
182
190
|
const json = { timestamp, worker: this.#worker, tag, message: null };
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
const head = args[0];
|
|
192
|
+
let start = 0;
|
|
185
193
|
if (metautil.isError(head)) {
|
|
186
194
|
json.error = this.expandError(head);
|
|
187
|
-
|
|
195
|
+
start = 1;
|
|
188
196
|
} else if (typeof head === 'object') {
|
|
189
197
|
Object.assign(json, head);
|
|
190
|
-
|
|
198
|
+
start = 1;
|
|
191
199
|
}
|
|
192
|
-
|
|
200
|
+
const rest = start === 0 ? args : args.slice(start);
|
|
201
|
+
json.message = util.format(...rest);
|
|
193
202
|
return JSON.stringify(json);
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
normalizeStack(stack) {
|
|
197
206
|
if (!stack) return 'No stack trace to log';
|
|
198
|
-
let res =
|
|
199
|
-
if (this.#home) res =
|
|
207
|
+
let res = stack.replaceAll(STACK_AT, '');
|
|
208
|
+
if (this.#home) res = res.replaceAll(this.#home, '');
|
|
200
209
|
return res;
|
|
201
210
|
}
|
|
202
211
|
|
|
@@ -312,15 +321,20 @@ class Console {
|
|
|
312
321
|
}
|
|
313
322
|
|
|
314
323
|
time(label = 'default') {
|
|
315
|
-
this.#times.set(label, process.hrtime());
|
|
324
|
+
this.#times.set(label, process.hrtime.bigint());
|
|
316
325
|
}
|
|
317
326
|
|
|
318
327
|
timeEnd(label = 'default') {
|
|
319
328
|
const startTime = this.#times.get(label);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
329
|
+
if (startTime === undefined) {
|
|
330
|
+
const msg = `Warning: No such label '${label}'`;
|
|
331
|
+
this.#logger.write('warn', this.#groupIndent, [msg]);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
323
334
|
this.#times.delete(label);
|
|
335
|
+
const elapsed = Number(process.hrtime.bigint() - startTime) / 1e6;
|
|
336
|
+
const output = `${label}: ${elapsed}ms`;
|
|
337
|
+
this.#logger.write('debug', this.#groupIndent, [output]);
|
|
324
338
|
}
|
|
325
339
|
|
|
326
340
|
timeLog(label = 'default', ...data) {
|
|
@@ -330,10 +344,10 @@ class Console {
|
|
|
330
344
|
this.#logger.write('warn', this.#groupIndent, [msg]);
|
|
331
345
|
return;
|
|
332
346
|
}
|
|
333
|
-
const
|
|
334
|
-
const totalTimeMs = totalTime[0] * 1e3 + totalTime[1] / 1e6;
|
|
347
|
+
const elapsed = Number(process.hrtime.bigint() - startTime) / 1e6;
|
|
335
348
|
const message = data.length > 0 ? util.format(...data) : '';
|
|
336
|
-
const
|
|
349
|
+
const suffix = message ? ` ${message}` : '';
|
|
350
|
+
const output = `${label}: ${elapsed}ms${suffix}`;
|
|
337
351
|
this.#logger.write('debug', this.#groupIndent, [output]);
|
|
338
352
|
}
|
|
339
353
|
}
|
|
@@ -352,6 +366,7 @@ class Logger extends EventEmitter {
|
|
|
352
366
|
#toStdout = null;
|
|
353
367
|
#buffer = null;
|
|
354
368
|
#formatter = null;
|
|
369
|
+
#exitHandler = null;
|
|
355
370
|
|
|
356
371
|
constructor(options) {
|
|
357
372
|
super();
|
|
@@ -383,7 +398,7 @@ class Logger extends EventEmitter {
|
|
|
383
398
|
this.active = true;
|
|
384
399
|
if (!this.#fsEnabled) return this;
|
|
385
400
|
await this.#createDir();
|
|
386
|
-
const fileName = metautil.nowDate()
|
|
401
|
+
const fileName = `${metautil.nowDate()}-${this.#worker}.log`;
|
|
387
402
|
this.#file = path.join(this.path, fileName);
|
|
388
403
|
const nextReopen = getNextReopen();
|
|
389
404
|
this.#rotationTimer = setTimeout(() => {
|
|
@@ -408,36 +423,26 @@ class Logger extends EventEmitter {
|
|
|
408
423
|
}
|
|
409
424
|
|
|
410
425
|
async close() {
|
|
411
|
-
if (!this.active) return
|
|
426
|
+
if (!this.active) return;
|
|
427
|
+
this.#removeCrashHandling();
|
|
412
428
|
if (!this.#fsEnabled) {
|
|
413
429
|
this.active = false;
|
|
414
430
|
this.emit('close');
|
|
415
|
-
return
|
|
431
|
+
return;
|
|
416
432
|
}
|
|
417
433
|
const stream = this.#stream;
|
|
418
|
-
if (stream.destroyed || stream.closed) return
|
|
434
|
+
if (stream.destroyed || stream.closed) return;
|
|
419
435
|
clearTimeout(this.#rotationTimer);
|
|
420
436
|
this.#rotationTimer = null;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
fs.stat(fileName, (error, stats) => {
|
|
431
|
-
if (error || stats.size > 0) {
|
|
432
|
-
return void resolve();
|
|
433
|
-
}
|
|
434
|
-
fsp.unlink(fileName).catch(() => {});
|
|
435
|
-
resolve();
|
|
436
|
-
});
|
|
437
|
-
})
|
|
438
|
-
.catch(reject);
|
|
439
|
-
});
|
|
440
|
-
});
|
|
437
|
+
this.active = false;
|
|
438
|
+
await this.#buffer.close();
|
|
439
|
+
this.emit('close');
|
|
440
|
+
try {
|
|
441
|
+
const stats = await fsp.stat(this.#file);
|
|
442
|
+
if (stats.size === 0) await fsp.unlink(this.#file).catch(() => {});
|
|
443
|
+
} catch {
|
|
444
|
+
this.emit('error', new Error(`Can't delete log file: ${this.#file}`));
|
|
445
|
+
}
|
|
441
446
|
}
|
|
442
447
|
|
|
443
448
|
async rotate() {
|
|
@@ -461,36 +466,35 @@ class Logger extends EventEmitter {
|
|
|
461
466
|
}
|
|
462
467
|
}
|
|
463
468
|
|
|
464
|
-
#createDir() {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
return void resolve();
|
|
471
|
-
} else {
|
|
472
|
-
const error = new Error(`Can not create directory: ${this.path}`);
|
|
473
|
-
this.emit('error', error);
|
|
474
|
-
reject(error);
|
|
475
|
-
}
|
|
476
|
-
});
|
|
469
|
+
async #createDir() {
|
|
470
|
+
try {
|
|
471
|
+
await fsp.mkdir(this.path, { recursive: true });
|
|
472
|
+
} catch (cause) {
|
|
473
|
+
const error = new Error(`Can not create directory: ${this.path}`, {
|
|
474
|
+
cause,
|
|
477
475
|
});
|
|
478
|
-
|
|
476
|
+
this.emit('error', error);
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
write(tag, indent, args) {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
482
|
+
const toStdout = this.#toStdout[tag];
|
|
483
|
+
const toFile = this.#toFile[tag];
|
|
484
|
+
if (!toStdout && !toFile) return;
|
|
485
|
+
if (this.#options.json) {
|
|
486
|
+
const line = `${this.#formatter.formatJson(tag, indent, args)}\n`;
|
|
487
|
+
if (toStdout) process.stdout.write(line);
|
|
488
|
+
if (toFile) this.#buffer.write(Buffer.from(line));
|
|
489
|
+
} else {
|
|
490
|
+
if (toStdout) {
|
|
491
|
+
const pretty = this.#formatter.formatPretty(tag, indent, args);
|
|
492
|
+
process.stdout.write(`${pretty}\n`);
|
|
493
|
+
}
|
|
494
|
+
if (toFile) {
|
|
495
|
+
const file = this.#formatter.formatFile(tag, indent, args);
|
|
496
|
+
this.#buffer.write(Buffer.from(`${file}\n`));
|
|
497
|
+
}
|
|
494
498
|
}
|
|
495
499
|
}
|
|
496
500
|
|
|
@@ -510,16 +514,20 @@ class Logger extends EventEmitter {
|
|
|
510
514
|
}
|
|
511
515
|
|
|
512
516
|
#setupCrashHandling() {
|
|
513
|
-
|
|
517
|
+
this.#exitHandler = () => {
|
|
514
518
|
if (this.active) this.flush();
|
|
515
519
|
};
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
520
|
+
for (const event of CRASH_EVENTS) {
|
|
521
|
+
process.on(event, this.#exitHandler);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
#removeCrashHandling() {
|
|
526
|
+
if (!this.#exitHandler) return;
|
|
527
|
+
for (const event of CRASH_EVENTS) {
|
|
528
|
+
process.removeListener(event, this.#exitHandler);
|
|
529
|
+
}
|
|
530
|
+
this.#exitHandler = null;
|
|
523
531
|
}
|
|
524
532
|
}
|
|
525
533
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metalog",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3-prerelease",
|
|
4
4
|
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>",
|
|
5
5
|
"description": "Logger for Metarhia",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"impress",
|
|
13
13
|
"server",
|
|
14
14
|
"jstp",
|
|
15
|
-
"
|
|
15
|
+
"globalstorage",
|
|
16
16
|
"highload",
|
|
17
17
|
"cloud",
|
|
18
18
|
"api",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"types": "metalog.d.ts",
|
|
40
40
|
"readmeFilename": "README.md",
|
|
41
41
|
"files": [
|
|
42
|
-
"
|
|
42
|
+
"metalog.js",
|
|
43
43
|
"metalog.d.ts"
|
|
44
44
|
],
|
|
45
45
|
"scripts": {
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"concolor": "^1.1.4",
|
|
56
|
-
"metautil": "^5.
|
|
56
|
+
"metautil": "^5.5.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@types/node": "^25.0
|
|
60
|
-
"eslint": "^9.39.
|
|
61
|
-
"eslint-config-metarhia": "^9.1.
|
|
59
|
+
"@types/node": "^25.4.0",
|
|
60
|
+
"eslint": "^9.39.4",
|
|
61
|
+
"eslint-config-metarhia": "^9.1.8",
|
|
62
62
|
"prettier": "^3.7.4",
|
|
63
63
|
"typescript": "^5.9.2"
|
|
64
64
|
}
|