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));
 }