Changeset - e462853f1ef6
[Not reviewed]
default
0 7 0
drewp@bigasterisk.com - 20 months ago 2023-05-30 02:35:37
drewp@bigasterisk.com
redo homepage and metrics calcs. still a mess
7 files changed with 123 insertions and 102 deletions:
0 comments (0 inline, 0 general)
light9/collector/collector_client_asyncio.py
Show inline comments
 
@@ -4,15 +4,19 @@ from light9.collector.collector_client i
 
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()
 

	
light9/effect/sequencer/eval_faders.py
Show inline comments
 
@@ -3,6 +3,8 @@ import time
 
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 @@ from light9.typedgraph import typedValue
 

	
 
log = logging.getLogger('seq.fader')
 

	
 
COMPILE=Summary('compile_graph_fader', '')
 

	
 
@dataclass
 
class Fader:
 
    graph: SyncedGraph
 
@@ -46,7 +50,7 @@ class FaderEval:
 
        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 = []
light9/homepage/ServiceButtonRow.ts
Show inline comments
 
@@ -5,10 +5,12 @@ export { StatsLine } from "./StatsLine";
 
@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 @@ export class ServiceButtonRow extends Li
 
      <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>` : ""}
 
      `;
 
  }
 

	
light9/homepage/StatsLine.ts
Show inline comments
 
@@ -2,19 +2,38 @@ import { css, html, LitElement, Template
 
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,10 +132,30 @@ export class StatsLine extends LitElemen
 
    `,
 
  ];
 

	
 
  render() {
 
    const now = Date.now() / 1000;
 
  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 table = (d: Metrics, path: string[]): TemplateResult => {
 
    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);
 
@@ -138,7 +177,7 @@ export class StatsLine extends LitElemen
 
      };
 
      const td = (col: string): TemplateResult => {
 
        const cell = byName.get(col)!;
 
        return html`${drawLevel(cell, path.concat(col))}`;
 
      return html`${this.drawLevel(cell, path.concat(col))}`;
 
      };
 
      return html` <table>
 
        <tr>
 
@@ -148,83 +187,68 @@ export class StatsLine extends LitElemen
 
          ${cols.map(td)}
 
        </tr>
 
      </table>`;
 
    };
 
  }
 

	
 
    const tdWrap = (content: TemplateResult): TemplateResult => {
 
      return html`<td>${content}</td>`;
 
    };
 
  drawLevel(d: Metric, path: string[]) {
 
    return html`[NEW ${JSON.stringify(d)} ${path}]`;
 
  }
 

	
 

	
 
    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";
 
  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 {
 
          color = "#d09e4c";
 
      throw m.type;
 
    }
 
        }
 
        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>`;
 
    };
 

	
 
  private histoDisplay(b: { [value: string]: string }) {
 
    const lines: TemplateResult[] = [];
 
    let firstLevel;
 
    let lastLevel;
 
    let prev = 0;
 

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

	
 
    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>`;
 
    return html`${firstLevel} ${lines} ${lastLevel}`;
 
      }
 
      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"
 
      );
 
    };
 
  render() {
 
    const now = Date.now() / 1000;
 

	
 
    const displayedStats = this.stats.filter(nonBoring);
 
    return html`
 
@@ -233,14 +257,14 @@ export class StatsLine extends LitElemen
 
          ${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>
 
                      `
 
                    )}
light9/homepage/StatsProcess.ts
Show inline comments
 
@@ -59,7 +59,7 @@ export class StatsProcess extends LitEle
 
    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);
 

	
light9/homepage/index.html
Show inline comments
 
<!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>
light9/web/floating_color_picker.ts
Show inline comments
 
@@ -8,7 +8,7 @@ import { SubEvent } from "sub-events";
 

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

	
0 comments (0 inline, 0 general)