Mercurial > code > home > repos > light9
changeset 2280:e462853f1ef6
redo homepage and metrics calcs. still a mess
author | drewp@bigasterisk.com |
---|---|
date | Mon, 29 May 2023 19:35:37 -0700 |
parents | 9e7500543ab6 |
children | f31df46edfdd |
files | light9/collector/collector_client_asyncio.py light9/effect/sequencer/eval_faders.py light9/homepage/ServiceButtonRow.ts light9/homepage/StatsLine.ts light9/homepage/StatsProcess.ts light9/homepage/index.html light9/web/floating_color_picker.ts |
diffstat | 7 files changed, 161 insertions(+), 140 deletions(-) [+] |
line wrap: on
line diff
--- a/light9/collector/collector_client_asyncio.py Mon May 29 19:33:16 2023 -0700 +++ b/light9/collector/collector_client_asyncio.py Mon May 29 19:35:37 2023 -0700 @@ -4,15 +4,19 @@ from light9.effect.settings import DeviceSettings import aiohttp +from prometheus_client import Summary log = logging.getLogger('coll_client') +SESS = Summary('coll_client_new_session', 'aiohttp.ClientSession') + class _Sender: def __init__(self): self.reconnect() + @SESS.time() def reconnect(self): self.http_session = aiohttp.ClientSession()
--- a/light9/effect/sequencer/eval_faders.py Mon May 29 19:33:16 2023 -0700 +++ b/light9/effect/sequencer/eval_faders.py Mon May 29 19:35:37 2023 -0700 @@ -3,6 +3,8 @@ from dataclasses import dataclass from typing import List, Optional, cast +from prometheus_client import Summary + from rdfdb import SyncedGraph from rdflib import URIRef from rdflib.term import Node @@ -16,6 +18,8 @@ log = logging.getLogger('seq.fader') +COMPILE=Summary('compile_graph_fader', '') + @dataclass class Fader: graph: SyncedGraph @@ -46,7 +50,7 @@ log.debug('seq.onCodeChange') self.graph.addHandler(self._compile) - @metrics('compile_graph_fader').time() + @COMPILE.time() def _compile(self) -> None: """rebuild our data from the graph""" self.faders = []
--- a/light9/homepage/ServiceButtonRow.ts Mon May 29 19:33:16 2023 -0700 +++ b/light9/homepage/ServiceButtonRow.ts Mon May 29 19:35:37 2023 -0700 @@ -5,10 +5,12 @@ @customElement("service-button-row") export class ServiceButtonRow extends LitElement { @property() name: string = "?"; + @property({ attribute: "metrics" }) hasMetrics: boolean = false; static styles = [ css` :host { padding-bottom: 10px; + border-bottom: 1px solid #333; } a { color: #7d7dec; @@ -50,10 +52,10 @@ <div> <div class="left"><a class="big" href="${this.name}/">${this.name}</a></div> <div class="window"><button @click="${this.click}">window</button></div> - <div><a href="${this.name}/metrics">metrics</a></div> + ${this.hasMetrics ? html`<div><a href="${this.name}/metrics">metrics</a></div>` : ""} </div> - <div id="stats"><stats-line name="${this.name}"></div> + ${this.hasMetrics ? html`<div id="stats"><stats-line name="${this.name}"></div>` : ""} `; }
--- a/light9/homepage/StatsLine.ts Mon May 29 19:33:16 2023 -0700 +++ b/light9/homepage/StatsLine.ts Mon May 29 19:35:37 2023 -0700 @@ -2,19 +2,38 @@ import { customElement, property } from "lit/decorators.js"; export { StatsProcess } from "./StatsProcess"; import parsePrometheusTextFormat from "parse-prometheus-text-format"; +import debug from "debug"; +import { clamp } from "../web/floating_color_picker"; +const log = debug("home"); interface Value { labels: { string: string }; - value: string; + value?: string; + count?: number; + sum?: number; + buckets?: { [value: string]: string }; } interface Metric { name: string; help: string; - type: "GAUGE" | "SUMMARY" | "COUNTER"; + type: "GAUGE" | "SUMMARY" | "COUNTER" | "HISTOGRAM" | "UNTYPED"; metrics: Value[]; } type Metrics = Metric[]; +function nonBoring(m: Metric) { + return ( + !m.name.endsWith("_created") && // + !m.name.startsWith("python_gc_") && + m.name != "python_info" && + m.name != "process_max_fds" && + m.name != "process_virtual_memory_bytes" && + m.name != "process_resident_memory_bytes" && + m.name != "process_start_time_seconds" && + m.name != "process_cpu_seconds_total" + ); +} + @customElement("stats-line") export class StatsLine extends LitElement { @property() name = "?"; @@ -113,119 +132,124 @@ `, ]; + tdWrap(content: TemplateResult): TemplateResult { + return html`<td>${content}</td>`; + } + + recents(d: any, path: string[]): TemplateResult { + const hi = Math.max.apply(null, d.recents); + const scl = 30 / hi; + + const bar = (y: number) => { + let color; + if (y < d.average) { + color = "#6a6aff"; + } else { + color = "#d09e4c"; + } + return html`<div class="bar" style="height: ${y * scl}px; background: ${color};"></div>`; + }; + return html`<td> + <div class="recents">${d.recents.map(bar)}</div> + <div>avg=${d.average.toPrecision(3)}</div> + </td>`; + } + + table(d: Metrics, path: string[]): TemplateResult { + const byName = new Map<string, Metric>(); + d.forEach((row) => { + byName.set(row.name, row); + }); + let cols = d.map((row) => row.name); + cols.sort(); + + if (path.length == 0) { + ["webServer", "process"].forEach((earlyKey) => { + let i = cols.indexOf(earlyKey); + if (i != -1) { + cols = [earlyKey].concat(cols.slice(0, i), cols.slice(i + 1)); + } + }); + } + + const th = (col: string): TemplateResult => { + return html`<th>${col}</th>`; + }; + const td = (col: string): TemplateResult => { + const cell = byName.get(col)!; + return html`${this.drawLevel(cell, path.concat(col))}`; + }; + return html` <table> + <tr> + ${cols.map(th)} + </tr> + <tr> + ${cols.map(td)} + </tr> + </table>`; + } + + drawLevel(d: Metric, path: string[]) { + return html`[NEW ${JSON.stringify(d)} ${path}]`; + } + + + valueDisplay(m: Metric, v: Value): TemplateResult { + if (m.type == "GAUGE") { + return html`${v.value}`; + } else if (m.type == "COUNTER") { + return html`${v.value}`; + } else if (m.type == "HISTOGRAM") { + return this.histoDisplay(v.buckets!); + } else if (m.type == "UNTYPED") { + return html`${v.value}`; + } else if (m.type == "SUMMARY") { + log(v); + if (!v.count) { + return html`err: summary without count`; + } + return html`c=${v.count} percall=${((v.count && v.sum ? v.sum / v.count : 0) * 1000).toPrecision(3)}ms`; + } else { + throw m.type; + } + } + + private histoDisplay(b: { [value: string]: string }) { + const lines: TemplateResult[] = []; + let firstLevel; + let lastLevel; + let prev = 0; + + let maxDelta = 0; + for (let level in b) { + if (firstLevel === undefined) firstLevel = level; + lastLevel = level; + let count = parseFloat(b[level]); + let delta = count - prev; + prev = count; + if (delta > maxDelta) maxDelta = delta; + } + prev = 0; + const maxBarH = 60; + for (let level in b) { + let count = parseFloat(b[level]); + let delta = count - prev; + prev = count; + let levelf = parseFloat(level); + const h = clamp((delta / maxDelta) * maxBarH, 1, maxBarH); + lines.push( + html`<div + title="bucket=${level} count=${count}" + style="background: yellow; margin-right: 1px; width: 8px; height: ${h}px; display: inline-block" + ></div>` + ); + } + return html`${firstLevel} ${lines} ${lastLevel}`; + } + render() { const now = Date.now() / 1000; - const table = (d: Metrics, path: string[]): TemplateResult => { - const byName = new Map<string, Metric>(); - d.forEach((row) => { - byName.set(row.name, row); - }); - let cols = d.map((row) => row.name); - cols.sort(); - - if (path.length == 0) { - ["webServer", "process"].forEach((earlyKey) => { - let i = cols.indexOf(earlyKey); - if (i != -1) { - cols = [earlyKey].concat(cols.slice(0, i), cols.slice(i + 1)); - } - }); - } - - const th = (col: string): TemplateResult => { - return html`<th>${col}</th>`; - }; - const td = (col: string): TemplateResult => { - const cell = byName.get(col)!; - return html`${drawLevel(cell, path.concat(col))}`; - }; - return html` <table> - <tr> - ${cols.map(th)} - </tr> - <tr> - ${cols.map(td)} - </tr> - </table>`; - }; - - const tdWrap = (content: TemplateResult): TemplateResult => { - return html`<td>${content}</td>`; - }; - - const recents = (d: any, path: string[]) => { - const hi = Math.max.apply(null, d.recents); - const scl = 30 / hi; - - const bar = (y: number) => { - let color; - if (y < d.average) { - color = "#6a6aff"; - } else { - color = "#d09e4c"; - } - return html`<div class="bar" style="height: ${y * scl}px; background: ${color};"></div>`; - }; - return html`<td> - <div class="recents">${d.recents.map(bar)}</div> - <div>avg=${rounding(d.average, 3)}</div> - </td>`; - }; - - const pmf = (d: Metrics, path: string[]) => { - return tdWrap( - table( - { - count: d.count, - "values [ms]": html` - <div>mean=${rounding(d.mean * 1000, 3)}</div> - <div>sd=${rounding(d.stddev * 1000, 3)}</div> - <div>99=${rounding(d["99percentile"] * 1000, 3)}</div> - `, - }, - path - ) - ); - }; - - const drawLevel = (d: Metric, path: string[]) => { - if (path.length == 1 && path[0] === "process") { - const elem = this.shadowRoot!.querySelector("#proc"); - if (elem) { - (elem as StatsProcess).data = d; - } - return html`<stats-process id="proc"></stats-process>`; - } - if (typeof d === "object") { - if (d.strings) { - //} instanceof TemplateResult) { - return html`<td class="val">${d}</td>`; - } else if (d.count !== undefined && d.min !== undefined) { - return pmf(d, path); - } else if (d.average !== undefined && d.recents !== undefined) { - return recents(d, path); - } else { - return tdWrap(table(d, path)); - } - } else { - return html`<td class="val bigInt">${d}</td>`; - } - }; - - const nonBoring = (m: Metric) => { - return ( - !m.name.endsWith("_created") && // - !m.name.startsWith("python_gc_") && - m.name != "python_info" && - m.name != "process_max_fds" && - m.name != "process_virtual_memory_bytes" && - m.name != "process_resident_memory_bytes" && - m.name != "process_start_time_seconds" && - m.name != "process_cpu_seconds_total" - ); - }; - const displayedStats = this.stats.filter(nonBoring); return html` <div> @@ -233,14 +257,14 @@ ${displayedStats.map( (row, rowNum) => html` <tr> - <th>${row.name}</th> + <th>${row.type.slice(0, 1)} ${row.name}</th> <td> <table> ${row.metrics.map( (v) => html` <tr> <td>${JSON.stringify(v.labels)}</td> - <td>${v.value}</td> + <td>${this.valueDisplay(row, v)}</td> </tr> ` )}
--- a/light9/homepage/StatsProcess.ts Mon May 29 19:33:16 2023 -0700 +++ b/light9/homepage/StatsProcess.ts Mon May 29 19:35:37 2023 -0700 @@ -59,7 +59,7 @@ const dt = now - this.prev; this.prev = now; - const size = remap(this.mem.valueOf() / 1024 / 1024, /*in*/ 20, 600, /*out*/ 3, 30); + const size = remap(this.mem.valueOf() / 1024 / 1024, /*in*/ 20, 80, /*out*/ 3, 30); this.revs += dt * remap(this.cpu.valueOf(), /*in*/ 0, 100, /*out*/ 4, 120); const rad = remap(size, /*in*/ 3, 30, /*out*/ 14, 5);
--- a/light9/homepage/index.html Mon May 29 19:33:16 2023 -0700 +++ b/light9/homepage/index.html Mon May 29 19:35:37 2023 -0700 @@ -1,34 +1,21 @@ -<!doctype html> +<!DOCTYPE html> <html> <head> <title>light9 home</title> <meta charset="utf-8" /> - <link rel="stylesheet" href="style.css"> + <link rel="stylesheet" href="style.css" /> <script type="module" src="./ServiceButtonRow.ts"></script> </head> <body> <h1>light9 home page</h1> <div style="display: grid"> - <service-button-row name="rdfdb"></service-button-row> - <hr> - <service-button-row name="ascoltami"></service-button-row> - <hr> - <!-- <service-button-row name="picamserve"></service-button-row> --> - <service-button-row name="vidref"></service-button-row> - <hr> - <service-button-row name="collector"></service-button-row> - <service-button-row name="effectSequencer"></service-button-row> - <service-button-row name="live"></service-button-row> - <service-button-row name="effects"></service-button-row> - <service-button-row name="timeline"></service-button-row> - <service-button-row name="paint"></service-button-row> - <service-button-row name="effectEval"></service-button-row> -<!-- <hr> - <service-button-row name="subServer"></service-button-row> - <service-button-row name="subComposer"></service-button-row> - --> + <service-button-row name="ascoltami" metrics="1"></service-button-row> + <service-button-row name="fade"></service-button-row> + <service-button-row name="effectListing"></service-button-row> + <service-button-row name="effectSequencer" metrics="1"></service-button-row> + <service-button-row name="collector" metrics="1"></service-button-row> + <service-button-row name="rdfdb" metrics="1"></service-button-row> </div> - </body> </html>
--- a/light9/web/floating_color_picker.ts Mon May 29 19:33:16 2023 -0700 +++ b/light9/web/floating_color_picker.ts Mon May 29 19:35:37 2023 -0700 @@ -8,7 +8,7 @@ const log = debug("control.color.pick"); -function clamp(x: number, lo: number, hi: number) { +export function clamp(x: number, lo: number, hi: number) { return Math.max(lo, Math.min(hi, x)); }