diff options
Diffstat (limited to 'src/zenserver/frontend/html/pages/page.js')
| -rw-r--r-- | src/zenserver/frontend/html/pages/page.js | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/pages/page.js b/src/zenserver/frontend/html/pages/page.js index d969d651d..d3069f506 100644 --- a/src/zenserver/frontend/html/pages/page.js +++ b/src/zenserver/frontend/html/pages/page.js @@ -4,6 +4,27 @@ import { WidgetHost } from "../util/widgets.js" import { Fetcher } from "../util/fetcher.js" +import { Friendly } from "../util/friendly.js" + +function _deep_merge_stats(base, update) +{ + const result = Object.assign({}, base); + for (const key of Object.keys(update)) + { + const bv = result[key]; + const uv = update[key]; + if (uv && typeof uv === "object" && !Array.isArray(uv) + && bv && typeof bv === "object" && !Array.isArray(bv)) + { + result[key] = _deep_merge_stats(bv, uv); + } + else + { + result[key] = uv; + } + } + return result; +} //////////////////////////////////////////////////////////////////////////////// export class PageBase extends WidgetHost @@ -78,6 +99,11 @@ export class ZenPage extends PageBase this._banner = banner; this._poll_status(); + + new Fetcher().resource("/health/version").text().then((data) => { + const v = data ? data.trim() : ""; + if (v) banner.attr("version", v); + }).catch(() => {}); } static _mode_taglines = { @@ -148,8 +174,10 @@ export class ZenPage extends PageBase const service_dashboards = [ { base_uri: "/sessions/", label: "Sessions", href: "/dashboard/?page=sessions" }, { base_uri: "/z$/", label: "Cache", href: "/dashboard/?page=cache" }, + { base_uri: "/builds/", label: "Build Store", href: "/dashboard/?page=builds" }, { base_uri: "/prj/", label: "Projects", href: "/dashboard/?page=projects" }, { base_uri: "/obj/", label: "Object Store", href: "/dashboard/?page=objectstore" }, + { base_uri: "/ws/", label: "Workspaces", href: "/dashboard/?page=workspaces" }, { base_uri: "/compute/", label: "Compute", href: "/dashboard/?page=compute" }, { base_uri: "/orch/", label: "Orchestrator", href: "/dashboard/?page=orchestrator" }, { base_uri: "/hub/", label: "Hub", href: "/dashboard/?page=hub" }, @@ -265,4 +293,113 @@ export class ZenPage extends PageBase new_crumb(auto_name); } + + _metric(parent, value, label, hero = false) + { + const m = parent.tag().classify("tile-metric"); + if (hero) + { + m.classify("tile-metric-hero"); + } + m.tag().classify("metric-value").text(value); + m.tag().classify("metric-label").text(label); + } + + _render_http_requests_tile(grid, req, bad_requests = undefined) + { + req = req || {}; + const tile = grid.tag().classify("card").classify("stats-tile"); + tile.tag().classify("card-title").text("HTTP Requests"); + const columns = tile.tag().classify("tile-columns"); + + const left = columns.tag().classify("tile-metrics"); + const reqData = req.requests || req; + this._metric(left, Friendly.sep(reqData.count || 0), "total requests", true); + if (reqData.rate_mean > 0) + { + this._metric(left, Friendly.sep(reqData.rate_mean, 1) + "/s", "req/sec (mean)"); + } + if (reqData.rate_1 > 0) + { + this._metric(left, Friendly.sep(reqData.rate_1, 1) + "/s", "req/sec (1m)"); + } + if (reqData.rate_5 > 0) + { + this._metric(left, Friendly.sep(reqData.rate_5, 1) + "/s", "req/sec (5m)"); + } + if (reqData.rate_15 > 0) + { + this._metric(left, Friendly.sep(reqData.rate_15, 1) + "/s", "req/sec (15m)"); + } + if (bad_requests !== undefined) + { + this._metric(left, Friendly.sep(bad_requests), "bad requests"); + } + + const right = columns.tag().classify("tile-metrics"); + this._metric(right, Friendly.duration(reqData.t_avg || 0), "avg latency", true); + if (reqData.t_p75) + { + this._metric(right, Friendly.duration(reqData.t_p75), "p75"); + } + if (reqData.t_p95) + { + this._metric(right, Friendly.duration(reqData.t_p95), "p95"); + } + if (reqData.t_p99) + { + this._metric(right, Friendly.duration(reqData.t_p99), "p99"); + } + if (reqData.t_p999) + { + this._metric(right, Friendly.duration(reqData.t_p999), "p999"); + } + if (reqData.t_max) + { + this._metric(right, Friendly.duration(reqData.t_max), "max"); + } + } + + _merge_last_stats(stats) + { + if (this._last_stats) + { + stats = _deep_merge_stats(this._last_stats, stats); + } + this._last_stats = stats; + return stats; + } + + _collapsible_section(name) + { + const section = this.add_section(name); + const container = section._parent.inner(); + const heading = container.firstElementChild; + + heading.style.cursor = "pointer"; + heading.style.userSelect = "none"; + + const indicator = document.createElement("span"); + indicator.textContent = " \u25BC"; + indicator.style.fontSize = "0.7em"; + heading.appendChild(indicator); + + let collapsed = false; + heading.addEventListener("click", (e) => { + if (e.target !== heading && e.target !== indicator) + { + return; + } + collapsed = !collapsed; + indicator.textContent = collapsed ? " \u25B6" : " \u25BC"; + let sibling = heading.nextElementSibling; + while (sibling) + { + sibling.style.display = collapsed ? "none" : ""; + sibling = sibling.nextElementSibling; + } + }); + + return section; + } } |