diff options
| author | Martin Ridgers <[email protected]> | 2024-11-18 08:41:46 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2024-11-18 08:41:46 +0100 |
| commit | cca69117b7ffac5cdd8933148ed9c94dd241528d (patch) | |
| tree | ba9dfce342e86d9cbdf6cf54059e1e7d618eecee /src/zenserver/frontend/html/pages/stat.js | |
| parent | oplog prep gc fix (#216) (diff) | |
| download | zen-cca69117b7ffac5cdd8933148ed9c94dd241528d.tar.xz zen-cca69117b7ffac5cdd8933148ed9c94dd241528d.zip | |
Dashboard: oplog tree view (#217)
* Turned tables and progress bars and friends into "widgets!"
* A step to abstracting away a page's the internal DOM structure
* Folded sector creation into Page and pivoted it to a widget host
* Try and keep start/count as numbers regardless of input
* No need for the entry table to be defined up front now
* Add op count and log sixe to oplog list page
* Cache left side toolbar object
* Bounds count page start when building list of oplog entrie
* Start/end navigation tools
* Build rest of entry page while waiting for indexer to load
* Consistent naming with other pages
* Spacially consolidate fetching code
* Hide fetch latency to speed up index generation workers
* Extract dashboard structure from zen.js monolith
* Fix breadcrumbs after restructuring
* Add view link to actions cell of oplogs list
* Generator to enumerate names of entries in indexer
* Methods for simple traversal of component relations
* is() to check if a component is of a certain type
* Extend attr() to get and unset a component's attributes
* Unsetting all styles of anchor tags was underisrable
* Restore page name as id of container element
* A tree view of an oplog
* Move helper class out to private module scope
* Small tweak to use left var that already exists
* Changelog update
* Updated frontend .zip archive
Diffstat (limited to 'src/zenserver/frontend/html/pages/stat.js')
| -rw-r--r-- | src/zenserver/frontend/html/pages/stat.js | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/pages/stat.js b/src/zenserver/frontend/html/pages/stat.js new file mode 100644 index 000000000..c7902d5ed --- /dev/null +++ b/src/zenserver/frontend/html/pages/stat.js @@ -0,0 +1,153 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +"use strict"; + +import { ZenPage } from "./page.js" +import { Fetcher } from "../util/fetcher.js" +import { Friendly } from "../util/friendly.js" +import { PropTable, Toolbar } from "../util/widgets.js" + +//////////////////////////////////////////////////////////////////////////////// +class TemporalStat +{ + constructor(data, as_bytes) + { + this._data = data; + this._as_bytes = as_bytes; + } + + toString() + { + const columns = [ + /* count */ {}, + /* rate */ {}, + /* t */ {}, {}, + ]; + const data = this._data; + for (var key in data) + { + var out = columns[0]; + if (key.startsWith("rate_")) out = columns[1]; + else if (key.startsWith("t_p")) out = columns[3]; + else if (key.startsWith("t_")) out = columns[2]; + out[key] = data[key]; + } + + var friendly = this._as_bytes ? Friendly.kib : Friendly.sep; + + var content = ""; + for (var i = 0; i < columns.length; ++i) + { + content += "<pre>"; + const column = columns[i]; + for (var key in column) + { + var value = column[key]; + if (i) + { + value = Friendly.sep(value, 2); + key = key.padStart(9); + content += key + ": " + value; + } + else + content += friendly(value); + content += "\n"; + } + content += "</pre>"; + } + + return content; + } +} + +//////////////////////////////////////////////////////////////////////////////// +export class Page extends ZenPage +{ + async main() + { + const provider = this.get_param("provider", "z$"); + var stats = new Fetcher() + .resource("stats", provider) + .param("cidstorestats", "true") + .param("cachestorestats", "true") + .json(); + + this.set_title("stat - " + provider); + const section = this.add_section(provider); + + var toolbar = section.add_widget(Toolbar); + var tb_right = toolbar.right(); + tb_right.add("filter:"); + tb_right.add("-none-").on_click((x) => this.update_filter("")); + for (var preset of ["read.", "write.", ".request", ".bytes"]) + tb_right.add(preset).on_click((x) => this.update_filter(x), preset); + this._filter_input = tb_right.add("", "label").tag("input"); + this._filter_input.on("change", (x) => this.update_filter(x.inner().value), this._filter_input); + + this._table = section.add_widget(PropTable); + + this._stats = stats = await stats; + this._condense(stats); + + var first = undefined; + for (var name in stats) + { + first = first || name; + toolbar.left().add(name).on_click((x) => this.view_category(x), name); + } + + var filter = this.get_param("filter"); + + first = this.get_param("view", first); + this.view_category(first); + + if (filter) + this.update_filter(filter); + } + + view_category(name) + { + const friendly = (this.get_param("raw") == undefined); + this._table.clear(); + this._table.add_object(this._stats[name], friendly, 3); + this.set_param("view", name); + this.update_filter(""); + } + + update_filter(needle) + { + this._filter_input.attr("value", needle); + + this.set_param("filter", needle); + if (!needle) + return this._table.filter(); + + var needles = needle.split(" "); + this._table.filter(...needles); + } + + _condense(stats) + { + const impl = function(node) + { + for (var name in node) + { + const candidate = node[name]; + if (!(candidate instanceof Object)) + continue; + + if (candidate["rate_mean"] != undefined) + { + const as_bytes = (name.indexOf("bytes") >= 0); + node[name] = new TemporalStat(candidate, as_bytes); + continue; + } + + impl(candidate); + } + } + + for (var name in stats) + impl(stats[name]); + } +} |