home-hosted 0.7.14 → 0.7.16
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/dist/cli.js +84 -3
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/uis/stock/dist/ui.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -346,14 +346,95 @@ function withoutTrailingBlank(lines) {
|
|
|
346
346
|
*
|
|
347
347
|
* The current file plus its `.1` ancestor, so `logs` shows what happened just before a
|
|
348
348
|
* rotation rather than starting blank. `lines <= 0` means the whole thing.
|
|
349
|
+
*
|
|
350
|
+
* Reads only the tail it needs. The log is capped at 5 MB per file *by rotation*, so a panel
|
|
351
|
+
* that has been up a while keeps a 5 MB current file plus a 5 MB `.1` — and the old version
|
|
352
|
+
* read both in full, split every line and threw all but the last fifty away: 24 ms and 28 MB
|
|
353
|
+
* of heap to print fifty lines, on the *default* invocation of a diagnostic command. Most of
|
|
354
|
+
* a tail is not looked at, so it is not read.
|
|
349
355
|
*/
|
|
350
356
|
function readLog(file, lines) {
|
|
357
|
+
if (lines <= 0) return withoutTrailingBlank(readBoth(file).join("").split("\n"));
|
|
358
|
+
const tail = tailText(file, lines);
|
|
359
|
+
if (tail === null) return withoutTrailingBlank(readBoth(file).join("").split("\n")).slice(-lines);
|
|
360
|
+
return withoutTrailingBlank(tail.split("\n")).slice(-lines);
|
|
361
|
+
}
|
|
362
|
+
/** Both sides of the rotation, oldest first. Missing files contribute nothing. */
|
|
363
|
+
function readBoth(file) {
|
|
351
364
|
const parts = [];
|
|
352
365
|
for (const candidate of [`${file}.1`, file]) try {
|
|
353
366
|
parts.push(fs.readFileSync(candidate, "utf8"));
|
|
354
367
|
} catch {}
|
|
355
|
-
|
|
356
|
-
|
|
368
|
+
return parts;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* The text of the last `lines` lines across the rotation, joined the way concatenating the
|
|
372
|
+
* two files would join it — or `null` when the tail cannot be reached within the block budget,
|
|
373
|
+
* in which case the caller reads everything.
|
|
374
|
+
*
|
|
375
|
+
* Concatenating `.1` and the current file makes `.1`'s unterminated last line and the current
|
|
376
|
+
* file's first line *one* line. Keeping the two tails as text and joining them reproduces that
|
|
377
|
+
* for free: no gluing to arrange, and no line to lose. (An earlier draft spliced the arrays
|
|
378
|
+
* instead and dropped that line.)
|
|
379
|
+
*/
|
|
380
|
+
function tailText(file, lines) {
|
|
381
|
+
const wanted = lines + 1;
|
|
382
|
+
const current = readTail(file, wanted);
|
|
383
|
+
if (current === null) return null;
|
|
384
|
+
if (countNewlines(current) >= wanted) return current;
|
|
385
|
+
const rotated = readTail(`${file}.1`, wanted);
|
|
386
|
+
if (rotated === null) return null;
|
|
387
|
+
return `${rotated}${current}`;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* The last `lines` lines of one file, as text — read backwards in blocks, stopping as soon as
|
|
391
|
+
* enough line separators have been seen. `''` when there is nothing to contribute, `null` when
|
|
392
|
+
* the file cannot be read at all (which the caller answers by reading everything).
|
|
393
|
+
*
|
|
394
|
+
* The first element may be a fragment: a block boundary lands mid-line. It is deliberately
|
|
395
|
+
* left in place. The loop only exits once `lines + 1` separators are present, so at least
|
|
396
|
+
* `lines` complete lines follow the fragment and the caller's `slice(-lines)` can never reach
|
|
397
|
+
* it — trimming it would be code no test could make fail, and a randomised comparison against
|
|
398
|
+
* the previous implementation confirmed removing it changes nothing.
|
|
399
|
+
*/
|
|
400
|
+
function readTail(file, lines) {
|
|
401
|
+
let handle;
|
|
402
|
+
try {
|
|
403
|
+
handle = fs.openSync(file, "r");
|
|
404
|
+
} catch {
|
|
405
|
+
return "";
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
const size = fs.fstatSync(handle).size;
|
|
409
|
+
if (size === 0) return "";
|
|
410
|
+
const block = 65536;
|
|
411
|
+
let end = size;
|
|
412
|
+
let text = "";
|
|
413
|
+
while (end > 0 && countNewlines(text) < lines + 1) {
|
|
414
|
+
const start = Math.max(0, end - block);
|
|
415
|
+
const length = end - start;
|
|
416
|
+
const buffer = Buffer$1.alloc(length);
|
|
417
|
+
fs.readSync(handle, buffer, 0, length, start);
|
|
418
|
+
text = buffer.toString("utf8") + text;
|
|
419
|
+
end = start;
|
|
420
|
+
}
|
|
421
|
+
return text;
|
|
422
|
+
} catch {
|
|
423
|
+
return null;
|
|
424
|
+
} finally {
|
|
425
|
+
fs.closeSync(handle);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* The last `lines` lines of one file, without reading the whole of it.
|
|
430
|
+
*
|
|
431
|
+
* Reads backwards in blocks and stops as soon as it has enough newlines. A single line can
|
|
432
|
+
* still be long, so the block grows rather than the read being capped at the exact byte.
|
|
433
|
+
*/
|
|
434
|
+
function countNewlines(text) {
|
|
435
|
+
let count = 0;
|
|
436
|
+
for (let index = 0; index < text.length; index += 1) if (text.charCodeAt(index) === 10) count += 1;
|
|
437
|
+
return count;
|
|
357
438
|
}
|
|
358
439
|
/**
|
|
359
440
|
* Prints the last `lines` of the log, then keeps printing whatever is appended.
|
|
@@ -388,7 +469,7 @@ async function followLog(file, lines, write, pollMs = 250) {
|
|
|
388
469
|
const length = size - offset;
|
|
389
470
|
const buffer = Buffer$1.alloc(length);
|
|
390
471
|
fs.readSync(handle, buffer, 0, length, offset);
|
|
391
|
-
write(buffer.toString("utf8")
|
|
472
|
+
write(buffer.toString("utf8"));
|
|
392
473
|
} finally {
|
|
393
474
|
fs.closeSync(handle);
|
|
394
475
|
}
|