aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/dashboard
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-02 19:59:32 +0100
committertv <tv@krebsco.de>2026-11-02 19:59:32 +0100
commiteaf680d539e0c6545bc6bd517428ecc71244d6f3 (patch)
tree308b0646eec3cb82323ffa5fcf6835a4282cf3a4 /src/api/dashboard
parentfa065ad9e198d5012a53170267b2a52b34702e12 (diff)
dashboard: add msg/s
Diffstat (limited to 'src/api/dashboard')
-rw-r--r--src/api/dashboard/dashboard.html54
1 files changed, 53 insertions, 1 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html
index 4232cbd..0ae99f4 100644
--- a/src/api/dashboard/dashboard.html
+++ b/src/api/dashboard/dashboard.html
@@ -79,6 +79,10 @@
margin-left: 1em; cursor: pointer; }
section h2 label.choice input { vertical-align: -2px; margin: 0 3px 0 0; }
section.collapsed h2 label.choice { display: none; }
+ /* The console's rates, after its filter. */
+ section h2 .rates { font-size: 12px; text-transform: none; letter-spacing: 0;
+ margin-left: 1em; font-variant-numeric: tabular-nums; }
+ section.collapsed h2 .rates { display: none; }
/* The right column dragged off to the side: the map takes the width. */
@media (min-width: 901px) {
main.right-hidden > section:not(#map) { display: none; }
@@ -258,7 +262,7 @@
<div id="column-hint" class="note">Drag the space left of these boxes to the right to
hide the column.</div>
<section id="console-box">
- <h2>Console <label class="choice" title="Only the messages with the node or over the link in focus (clicked on the map or in the peers); all while nothing is"><input type="checkbox" id="console-filter">filter selected</label></h2>
+ <h2>Console <label class="choice" title="Only the messages with the node or over the link in focus (clicked on the map or in the peers); all while nothing is"><input type="checkbox" id="console-filter">filter selected</label><span id="console-rates" class="rates" title="Messages per second sent and received, as load averages are: over the last 1, 5 and 15 minutes (exponentially weighted), since the page was opened; with the filter, those with the node or over the link in focus">msg/s –</span></h2>
<div class="scroll" id="console-scroll"><div id="console" class="console"></div>
<div id="console-none" class="note" hidden>No information available</div></div>
</section>
@@ -1800,10 +1804,57 @@ function trafficArrived() {
}
}
}
+ measureRates(current);
lastTraffic = current;
if (flows.length > 0 && flowFrame === null) flowFrame = requestAnimationFrame(drawFlows);
}
+// --- The console's rates: messages per second sent and received, per peer and in all,
+// averaged as load averages are, over 1, 5 and 15 minutes (each sample weighs in by
+// how long ago it was, `e^(-age/window)`), from the counts each update brings. The
+// first sample is taken as it is, so they needn't climb from 0 first. ---
+const RATE_WINDOWS_S = [60, 300, 900];
+// By peer, and in all (`null`): the averages; and when the counts were last taken.
+const rates = new Map();
+let ratesAt = null;
+function measureRates(current) {
+ const now = performance.now();
+ if (lastTraffic !== null && ratesAt !== null && now > ratesAt) {
+ const secs = (now - ratesAt) / 1000;
+ const total = (counts, way) => Object.values(counts[way])
+ .reduce((sum, count) => sum + count.messages, 0);
+ let all = 0;
+ const sample = (key, rate) => {
+ const known = rates.get(key);
+ rates.set(key, known ? known.map((average, i) =>
+ rate + (average - rate) * Math.exp(-secs / RATE_WINDOWS_S[i])) : RATE_WINDOWS_S.map(() => rate));
+ };
+ for (const [id, counts] of current) {
+ const before = lastTraffic.get(id);
+ if (!before) continue;
+ const grown = Math.max(0, total(counts, "sent") - total(before, "sent")) +
+ Math.max(0, total(counts, "received") - total(before, "received"));
+ sample(id, grown / secs);
+ all += grown;
+ }
+ sample(null, all / secs);
+ // Peers gone from the list: their averages too.
+ for (const key of [...rates.keys()]) if (key !== null && !current.has(key)) rates.delete(key);
+ }
+ ratesAt = now;
+ showRates();
+}
+// The averages shown: in all, or with the filter, of the peer in focus; none (–) for a
+// link between other nodes, a node that is no peer, or before two updates came.
+function showRates() {
+ const filtering = consoleFiltered && focused !== null;
+ const peer = filtering ? peerOf(focused) : null;
+ const shown = filtering && peer === null ? undefined : rates.get(peer);
+ const format = rate => rate < 100 ? rate.toFixed(2) : String(Math.round(rate));
+ text(document.getElementById("console-rates"),
+ "msg/s " + (shown ? shown.map(format).join(" ") : "–"));
+}
+
// --- Console: the messages this node exchanged with its peers, one by one, as the
// node logs them (`messages` events, `traffic::MessageLog`), the latest at the bottom,
// `CONSOLE_LINES` at most. Scrolled along while at the bottom. A click on one shows it
@@ -1880,6 +1931,7 @@ consoleFilter.addEventListener("change", () => {
// follows from there.
let consoleView = null;
function filterConsole() {
+ showRates();
const log = document.getElementById("console");
const filtering = consoleFiltered && focused !== null;
log.classList.toggle("filtering", filtering);