mikser-io 10.7.0 → 10.9.0
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/package.json +1 -1
- package/src/logger.js +63 -22
package/package.json
CHANGED
package/src/logger.js
CHANGED
|
@@ -498,10 +498,21 @@ onLoad(() => {
|
|
|
498
498
|
//
|
|
499
499
|
// A minimum TOTAL would fix the count and get the other half wrong: four PDFs
|
|
500
500
|
// through Chrome is a small phase and a slow one, and it is exactly the phase
|
|
501
|
-
// worth narrating. Elapsed time is what "long" means
|
|
502
|
-
// tuning
|
|
503
|
-
//
|
|
504
|
-
|
|
501
|
+
// worth narrating. Elapsed time is what "long" means and it needs no
|
|
502
|
+
// per-phase tuning.
|
|
503
|
+
//
|
|
504
|
+
// It is an INTERVAL rather than a threshold, and quartiles are gone with it.
|
|
505
|
+
// A quartile is a fraction of the WORK, so it says nothing about how often a
|
|
506
|
+
// line appears: four records land in three seconds on a fast phase and four
|
|
507
|
+
// records cover an hour on a slow one. Time is the axis a reader cares about,
|
|
508
|
+
// so one line per interval, however much work passed in between.
|
|
509
|
+
//
|
|
510
|
+
// This gates the RUNNING commentary only. `progress-finished` is not behind
|
|
511
|
+
// it: every phase says that it ran and what it cost, whatever the duration.
|
|
512
|
+
// Gated, a short phase produced no record at all and a build could not say
|
|
513
|
+
// what it had done — and off a TTY that line is the only place phase timings
|
|
514
|
+
// come from.
|
|
515
|
+
export const PROGRESS_INTERVAL_MS = 30_000
|
|
505
516
|
|
|
506
517
|
// Which item, not just how far. A phase name alone says a build is doing
|
|
507
518
|
// something; the thing a stuck build needs to say is WHAT it is stuck on.
|
|
@@ -519,6 +530,23 @@ function progressDetail(detail) {
|
|
|
519
530
|
return relative.startsWith('..') ? text : relative
|
|
520
531
|
}
|
|
521
532
|
|
|
533
|
+
// A duration rounded away to nothing is not a measurement.
|
|
534
|
+
//
|
|
535
|
+
// `finished: 5 0s` was the first version of this and `0s` said nothing;
|
|
536
|
+
// millisecond resolution moved the same defect one order of magnitude down,
|
|
537
|
+
// where nine of a plain build's thirteen phases still printed `0ms`. The
|
|
538
|
+
// clock is the cause rather than the format — Date.now() cannot resolve
|
|
539
|
+
// below a millisecond — so phases are timed with performance.now(), and each
|
|
540
|
+
// band is printed at the resolution it actually has. The floor exists
|
|
541
|
+
// because even a monotonic clock can report two identical readings, and
|
|
542
|
+
// `0.00ms` would be the same lie a third time.
|
|
543
|
+
export function formatDuration(ms) {
|
|
544
|
+
if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`
|
|
545
|
+
if (ms >= 1) return `${Math.round(ms)}ms`
|
|
546
|
+
if (ms >= 0.01) return `${ms.toFixed(2)}ms`
|
|
547
|
+
return '<0.01ms'
|
|
548
|
+
}
|
|
549
|
+
|
|
522
550
|
function emitProgress({ name, total, value, detail }) {
|
|
523
551
|
const at = progressDetail(detail)
|
|
524
552
|
const fields = { code: 'progress', phase: name, total, value }
|
|
@@ -551,7 +579,8 @@ export function trackProgress(name, total) {
|
|
|
551
579
|
// --json would have extended that to every machine reading the output.
|
|
552
580
|
// Losing the graphics is the point; losing the information is not.
|
|
553
581
|
const drawn = !carriesDocument && Boolean(process.stdout.isTTY) && Boolean(runtime.options.info)
|
|
554
|
-
|
|
582
|
+
const now = performance.now()
|
|
583
|
+
currentBar = { name, total, value: 0, started: now, drawn, lastReport: now, detail: null }
|
|
555
584
|
if (drawn) ensureGauge().show({ section: name, subsection: `0/${total}` }, 0)
|
|
556
585
|
}
|
|
557
586
|
|
|
@@ -567,17 +596,15 @@ export function updateProgress(detail) {
|
|
|
567
596
|
if (drawn) {
|
|
568
597
|
gauge?.show({ section: name, subsection: `${value}/${total}` }, value / total)
|
|
569
598
|
} else {
|
|
570
|
-
//
|
|
571
|
-
// line, a log record does not
|
|
572
|
-
// this counts the way the bar does.
|
|
573
|
-
//
|
|
574
|
-
//
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
currentBar.milestone = reached
|
|
580
|
-
if (Date.now() - currentBar.started >= PROGRESS_MIN_MS) emitProgress(currentBar)
|
|
599
|
+
// One line per interval, not per item and not per quartile: a bar
|
|
600
|
+
// redraws in place and costs one line, a log record does not, so 800
|
|
601
|
+
// documents is 800 lines of noise if this counts the way the bar does.
|
|
602
|
+
// A phase that finishes inside the interval says nothing at all while
|
|
603
|
+
// it runs — its finished line covers it.
|
|
604
|
+
const now = performance.now()
|
|
605
|
+
if (now - currentBar.lastReport >= PROGRESS_INTERVAL_MS && value < total) {
|
|
606
|
+
currentBar.lastReport = now
|
|
607
|
+
emitProgress(currentBar)
|
|
581
608
|
}
|
|
582
609
|
}
|
|
583
610
|
if (value >= total) stopProgress()
|
|
@@ -587,19 +614,33 @@ export function stopProgress() {
|
|
|
587
614
|
if (!currentBar) return
|
|
588
615
|
const logger = useLogger()
|
|
589
616
|
const { name, total, value, started } = currentBar
|
|
590
|
-
|
|
617
|
+
// Rounded so the field carries real sub-millisecond precision without
|
|
618
|
+
// float noise; the message formats it separately.
|
|
619
|
+
const ms = Math.round((performance.now() - started) * 1000) / 1000
|
|
591
620
|
gauge?.hide()
|
|
592
621
|
// Structured either way, so a machine reading --json's stderr gets the
|
|
593
622
|
// same facts a person reads off the bar.
|
|
594
623
|
//
|
|
595
|
-
//
|
|
596
|
-
//
|
|
624
|
+
// This line has to stand alone. With the running commentary on an
|
|
625
|
+
// interval it is the ONLY record most phases produce, and `Documents
|
|
626
|
+
// import finished: 5 0s` said neither what the five were nor how long it
|
|
627
|
+
// took — a count with no subject and a duration rounded away to nothing.
|
|
628
|
+
//
|
|
629
|
+
// The subject is the PHASE, not an item. The last entity a phase happened
|
|
630
|
+
// to walk is not what the phase was about: seven journal phases in a row
|
|
631
|
+
// reported the same `/layouts/page.hbs` because that is where the walk
|
|
632
|
+
// ended, and `Files import finished: 3, last .../social-fb.svg` put a
|
|
633
|
+
// filename into the build log that nothing had anything to say about — it
|
|
634
|
+
// broke a test asserting that file is never mentioned, which is exactly
|
|
635
|
+
// the misreading it invites. `Files import finished: 3 in 3ms` already
|
|
636
|
+
// says what finished, how much of it, and what it cost. The running
|
|
637
|
+
// records keep the item, because there it shows MOVEMENT.
|
|
597
638
|
if (value < total) {
|
|
598
639
|
logger.warn({ code: 'progress-unfinished', phase: name, total, value, missing: total - value },
|
|
599
|
-
'%s unfinished: %d', name, total - value)
|
|
600
|
-
} else
|
|
640
|
+
'%s unfinished: %d of %d after %s', name, total - value, total, formatDuration(ms))
|
|
641
|
+
} else {
|
|
601
642
|
logger.info({ code: 'progress-finished', phase: name, total, ms },
|
|
602
|
-
'%s finished: %d %
|
|
643
|
+
'%s finished: %d in %s', name, total, formatDuration(ms))
|
|
603
644
|
}
|
|
604
645
|
currentBar = null
|
|
605
646
|
}
|