This commit is contained in:
Mateusz Gruszczyński committed 2026-10-13 21:29:57 +02:00
1 parent bb025dcf44
commit b0f211555a
74 files changed
+6089 -556

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
window.rustProxyWideOverview = (() => {
const limit = 60;
const configurations = {
wide_protocol_rates: {
unit: "rate", names: ["h1", "h2", "h3"], counters: s =>
[s.metrics?.http1_requests, s.metrics?.http2_requests, s.metrics?.http3_requests]
},
wide_cpu: {
unit: "percent", names: ["system", "process"], values: s =>
[s.system?.cpu_percent, s.system?.process_cpu_percent]
},
wide_memory: {
unit: "bytes", names: ["process", "cache"], values: s =>
[s.system?.process_rss_bytes, s.cache?.resident_bytes]
},
wide_cache_activity: {
unit: "rate", names: ["hits", "misses"], counters: s =>
[s.cache?.hits, s.cache?.misses]
},
wide_connections: {
unit: "count", names: ["open", "websockets"], values: s =>
[s.metrics?.active_connections, s.metrics?.websocket_active]
},
wide_backend_health: {
unit: "count", names: ["up", "down"], values: s => {
if (!Array.isArray(s.backend_pools)) return [null, null];
const members = s.backend_pools.flatMap(pool => pool.members || []);
return [members.filter(m => m.status === "up").length, members.filter(m => m.status === "down").length];
}
},
wide_latency: {
unit: "ms", names: ["response", "probe"], values: s => {
if (!Array.isArray(s.backend_pools)) return [s.metrics?.mean_duration_ms, null];
const members = s.backend_pools.flatMap(pool => pool.members || []);
const probes = members.filter(m => m.status === "up" && m.health_mode !== "off"
&& finite(m.last_probe_ms) !== null && finite(m.latency_ms) !== null
&& Number(m.last_probe_ms) > 0);
return [s.metrics?.mean_duration_ms,
probes.length ? probes.reduce((total, m) => total + Number(m.latency_ms), 0) / probes.length : null];
}
},
};
const history = new Map(), baselines = new Map(), last = new Map();
let revision = null;
const finite = value => value != null && Number.isFinite(Number(value)) && Number(value) >= 0 ? Number(value) : null;
const format = (n, unit) => n === null ? "-" : unit === "bytes" ? fmtBytes(n)
: unit === "percent" ? `${fmt(n, 1)}%` : unit === "ms" ? `${fmt(n, 1)} ms`
: unit === "rate" ? `${fmt(n, 1)}/s` : fmt(n);
const tile = id => document.querySelector(`[data-dashboard-widget="${id}"]`);
function counterRates(id, values, now, record) {
const counters = values.map(finite);
if (counters.some(n => n === null)) { baselines.delete(id); return counters.map(() => null); }
if (!record) return last.get(id) || counters.map(() => null);
const previous = baselines.get(id);
baselines.set(id, { counters, now });
if (!previous || now <= previous.now || counters.some((n, i) => n < previous.counters[i]))
return counters.map(() => null);
const elapsed = (now - previous.now) / 1000;
return counters.map((n, i) => (n - previous.counters[i]) / elapsed);
}
function path(values, peak) {
const count = values.length;
let result = "", drawing = false;
for (let i = 0; i < count; i++) {
const value = values[i];
if (value === null) { drawing = false; continue; }
const x = Math.round(600 * i / Math.max(1, count - 1));
const y = Math.round(137 - 120 * value / peak);
result += `${drawing ? " L" : " M"}${x} ${y}`;
drawing = true;
}
return result.trim();
}
function render(status, record = true) {
if (!status || currentPanel !== "overview") return;
if (revision !== status.revision) { reset(); revision = status.revision; }
const now = performance.now();
for (const [id, config] of Object.entries(configurations)) {
const node = tile(id);
if (!node || node.hidden) { baselines.delete(id); continue; }
const values = config.counters ? counterRates(id, config.counters(status), now, record)
: config.values(status).map(finite);
const series = history.get(id) || config.names.map(() => []);
if (record) {
values.forEach((value, i) => {
series[i].push(value);
if (series[i].length > limit) series[i].shift();
});
history.set(id, series);
last.set(id, values);
}
const peak = Math.max(config.unit === "percent" ? 100 : 1,
...series.flat().filter(n => n !== null));
config.names.forEach((key, i) => {
const value = values[i];
const field = $(`overview-wide-${id}-${key}-value`);
if (field) field.textContent = format(value, config.unit);
$(`overview-wide-${id}-${key}-line`)?.setAttribute("d", path(series[i], peak));
});
const summary = $(`overview-wide-${id}-summary`);
if (summary) summary.textContent = format(values[0], config.unit);
}
}
function reset() { history.clear(); baselines.clear(); last.clear(); revision = null; }
return { render, reset };
})();