feat(diagnostics): renderer interaction timing — measure every UI switch/click, not just main-thread (v3.3.100)
Closes the last measurement gap. The main process was already fully instrumented (every ipcMain handler timed at >=50ms, a 100ms main-thread long-task monitor with channel attribution, config load/serialize timing). Account switches were already covered too: switchAccount is a trivial synchronous Map-set and the rotation work is async, so a switch cannot block the main thread, and any block that did occur would surface in the long-task monitor. The real gap was the renderer side: the renderer-perf line was gated on active uploads (idle clicks were never logged) and only reported an aggregate longtask count — no per-interaction latency and no element attribution. So a switch/sort/tab that janked in the renderer (not main) was invisible. renderer/app.js (additive, self-silencing, wrapped in try/catch): - An Event Timing observer (PerformanceObserver type:'event', durationThreshold:50, buffered) logs `renderer-interaction <type> dur=Xms proc=Yms target=<el>` for every user interaction whose latency exceeds 50ms — always on, idle or under load — and names the element (id / first class / data-action / aria-label / title). This is the direct click->reaction latency the user feels. - The longtask observer now also logs `renderer-longtask dur=Xms` immediately for any single renderer long task >=100ms, regardless of upload state. 405 tests pass; clean Electron boot. Every action — main or renderer, idle or under load — now names itself in the log if it is slow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c3381d360f
commit
003e14dfe9
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "multi-hoster-uploader",
|
||||
"version": "3.3.99",
|
||||
"version": "3.3.100",
|
||||
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
@ -20,13 +20,38 @@ let uploading = false;
|
||||
let healthCheckRunning = false;
|
||||
|
||||
let _rLongTasks = 0, _rLongTaskMax = 0, _rFrameLast = 0, _rFrameWorst = 0, _rFrameCount = 0, _rFrameJank = 0, _rPerfLastLog = 0, _rPerfWindowStart = 0;
|
||||
function _rElLabel(el) {
|
||||
try {
|
||||
if (!el || !el.tagName) return '?';
|
||||
let s = el.tagName.toLowerCase();
|
||||
if (el.id) s += '#' + el.id;
|
||||
else if (el.className && typeof el.className === 'string') { const c = el.className.trim().split(/\s+/)[0]; if (c) s += '.' + c; }
|
||||
const a = el.getAttribute && (el.getAttribute('data-action') || el.getAttribute('data-tab') || el.getAttribute('aria-label') || el.getAttribute('title'));
|
||||
if (a) s += `[${String(a).slice(0, 24)}]`;
|
||||
return s;
|
||||
} catch { return '?'; }
|
||||
}
|
||||
try {
|
||||
if (window.PerformanceObserver) {
|
||||
new window.PerformanceObserver((list) => {
|
||||
for (const e of list.getEntries()) { _rLongTasks++; if (e.duration > _rLongTaskMax) _rLongTaskMax = e.duration; }
|
||||
for (const e of list.getEntries()) {
|
||||
_rLongTasks++;
|
||||
if (e.duration > _rLongTaskMax) _rLongTaskMax = e.duration;
|
||||
if (e.duration >= 100 && window.api && window.api.debugLog) window.api.debugLog(`renderer-longtask dur=${Math.round(e.duration)}ms`);
|
||||
}
|
||||
}).observe({ entryTypes: ['longtask'] });
|
||||
}
|
||||
} catch {}
|
||||
try {
|
||||
if (window.PerformanceObserver) {
|
||||
new window.PerformanceObserver((list) => {
|
||||
for (const e of list.getEntries()) {
|
||||
const proc = Math.round((e.processingEnd || 0) - (e.processingStart || 0));
|
||||
if (window.api && window.api.debugLog) window.api.debugLog(`renderer-interaction ${e.name} dur=${Math.round(e.duration)}ms proc=${proc}ms target=${_rElLabel(e.target)}`);
|
||||
}
|
||||
}).observe({ type: 'event', durationThreshold: 50, buffered: true });
|
||||
}
|
||||
} catch {}
|
||||
function _rFrameTick(ts) {
|
||||
if (_rFrameLast) { const d = ts - _rFrameLast; _rFrameCount++; if (d > _rFrameWorst) _rFrameWorst = d; if (d > 33) _rFrameJank++; }
|
||||
_rFrameLast = ts;
|
||||
|
||||
@ -1,3 +1,19 @@
|
||||
# v3.3.100 — close the LAST measurement gap: renderer interaction timing (switches/clicks)
|
||||
|
||||
User asked "haben wir wirklich ALLES gemessen, auch switches/wechsel?". Audit: main-side was already
|
||||
fully covered (IPC wrapper ≥50ms on every handler, main-longtask >100ms with lastIpc, config instrument);
|
||||
account switchAccount is a trivial sync Map-set + the rotation work is async (can't block) → already covered.
|
||||
The REAL gap was RENDERER-side: renderer-perf was upload-gated (idle clicks unmeasured) and only aggregate
|
||||
(no per-interaction latency, no element attribution). Closed it (renderer/app.js, additive, self-silencing):
|
||||
- Event Timing API observer (`type:'event', durationThreshold:50, buffered`) → `renderer-interaction <type>
|
||||
dur=Xms proc=Yms target=<el>` for EVERY UI interaction ≥50ms (switch/sort-header/tab/button), always-on,
|
||||
names the element (id/class/data-action/aria-label). The direct "click→reaction" latency.
|
||||
- Idle renderer-longtask logging: any longtask ≥100ms logged immediately (`renderer-longtask dur=Xms`),
|
||||
not just during uploads.
|
||||
405 tests pass, clean boot. Now EVERY action — main or renderer, idle or under load — names itself if slow.
|
||||
|
||||
---
|
||||
|
||||
# v3.3.99 — THE KILL: 38.5MB config-thrash → history split out of the hot config + full instrumentation
|
||||
|
||||
THE ROOT CAUSE (from v3.3.98's instrument, the real "bread"): electron-config.json was **38.5MB** and got
|
||||
|
||||
Loading…
Reference in New Issue
Block a user