More like a library now
This commit is contained in:
parent
141a5bf888
commit
1fe69cd88f
|
@ -3,10 +3,16 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Webstat</title>
|
<title>Webstat</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script src="webstat.mjs" type="module"></script>
|
<script src="main.mjs" type="module"></script>
|
||||||
|
<style>
|
||||||
|
#pie {
|
||||||
|
max-width: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div><canvas id="stats"></canvas></div>
|
<div id="pie"></div>
|
||||||
|
<div id="chart"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import * as WebStat from "./webstat.mjs"
|
||||||
|
|
||||||
|
const Millisecond = 1
|
||||||
|
const Second = 1000 * Millisecond
|
||||||
|
const Minute = 60 * Second
|
||||||
|
|
||||||
|
class App {
|
||||||
|
constructor() {
|
||||||
|
this.stat = new WebStat.Stat()
|
||||||
|
this.areaChart = new WebStat.AreaChart(document.querySelector("#chart"))
|
||||||
|
this.pieChart = new WebStat.PieChart(document.querySelector("#pie"))
|
||||||
|
|
||||||
|
setInterval(()=>this.update(), 2 * Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
this.stat.update()
|
||||||
|
this.areaChart.update(this.stat)
|
||||||
|
this.pieChart.update(this.stat)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
new App()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", init)
|
||||||
|
} else {
|
||||||
|
init()
|
||||||
|
}
|
148
web/webstat.mjs
148
web/webstat.mjs
|
@ -1,10 +1,6 @@
|
||||||
//import Chart from "https://esm.run/chart.js@4.2.1/auto"
|
//import Chart from "https://esm.run/chart.js@4.2.1/auto"
|
||||||
import Chart from "https://cdn.jsdelivr.net/npm/chart.js@4.2.1/auto/+esm"
|
import Chart from "https://cdn.jsdelivr.net/npm/chart.js@4.2.1/auto/+esm"
|
||||||
|
|
||||||
const Millisecond = 1
|
|
||||||
const Second = 1000 * Millisecond
|
|
||||||
const Minute = 60 * Second
|
|
||||||
|
|
||||||
function qpush(arr, val, len) {
|
function qpush(arr, val, len) {
|
||||||
arr.push(val)
|
arr.push(val)
|
||||||
if (arr.length > len) {
|
if (arr.length > len) {
|
||||||
|
@ -13,36 +9,53 @@ function qpush(arr, val, len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Stat {
|
class Stat {
|
||||||
constructor(text) {
|
constructor() {
|
||||||
let lines = text.split("\n")
|
this.Stat = {}
|
||||||
|
this.LastStat = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
let resp = await fetch("proc/stat")
|
||||||
|
let stext = await resp.text()
|
||||||
|
let lines = stext.split("\n")
|
||||||
|
this.Date = new Date(resp.headers.get("Date"))
|
||||||
|
this.LastStat = this.Stat
|
||||||
|
this.Stat = {}
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
let parts = line.split(/\s+/)
|
let parts = line.split(/\s+/)
|
||||||
let key = parts.shift()
|
let key = parts.shift()
|
||||||
let vals = parts.map(Number)
|
let vals = parts.map(Number)
|
||||||
if (key.startsWith("cpu")) {
|
this.Stat[key] = vals
|
||||||
this[key] = {
|
|
||||||
user: vals[0],
|
|
||||||
nice: vals[1],
|
|
||||||
sys: vals[2],
|
|
||||||
idle: vals[3],
|
|
||||||
wait: vals[4],
|
|
||||||
irq: vals[5],
|
|
||||||
softirq: vals[6],
|
|
||||||
steal: vals[7],
|
|
||||||
guest: vals[8],
|
|
||||||
guestNice: vals[9],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this[key] = vals
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu(n="") {
|
||||||
|
let key = `cpu${n}`
|
||||||
|
let vals = this.Stat[key]
|
||||||
|
let prev = this.LastStat[key]
|
||||||
|
if (!vals || !prev) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
let total = vals.reduce((a,b)=>a+b) - prev.reduce((a,b)=>a+b)
|
||||||
|
return {
|
||||||
|
user: (vals[0] - prev[0]) / total,
|
||||||
|
nice: (vals[1] - prev[1]) / total,
|
||||||
|
sys: (vals[2] - prev[2]) / total,
|
||||||
|
idle: (vals[3] - prev[3]) / total,
|
||||||
|
wait: (vals[4] - prev[4]) / total,
|
||||||
|
irq: (vals[5] - prev[5]) / total,
|
||||||
|
softirq: (vals[6] - prev[6]) / total,
|
||||||
|
steal: (vals[7] - prev[7]) / total,
|
||||||
|
guest: (vals[8] - prev[8]) / total,
|
||||||
|
guestNice: (vals[9] - prev[9]) / total,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatUpdater {
|
class AreaChart {
|
||||||
constructor(interval, width=60) {
|
constructor(element, width=60) {
|
||||||
this.width = width
|
this.width = width
|
||||||
this.canvas = document.querySelector("#stats")
|
this.canvas = element.appendChild(document.createElement("canvas"))
|
||||||
this.data ={
|
this.data ={
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: []
|
datasets: []
|
||||||
|
@ -78,44 +91,63 @@ class StatUpdater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
setInterval(() => this.update(), interval)
|
|
||||||
this.update()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async update() {
|
async update(stat) {
|
||||||
let now = Date.now()
|
let now = stat.Date
|
||||||
let resp = await fetch("proc/stat")
|
let cpu = stat.cpu()
|
||||||
let stext = await resp.text()
|
qpush(this.data.labels, now, this.width)
|
||||||
let stat = new Stat(stext)
|
qpush(this.datasets.user, cpu.user, this.width)
|
||||||
if (this.last) {
|
qpush(this.datasets.nice, cpu.nice, this.width)
|
||||||
let user = stat.cpu.user - this.last.cpu.user
|
qpush(this.datasets.sys, cpu.sys, this.width)
|
||||||
let nice = stat.cpu.nice - this.last.cpu.nice
|
qpush(this.datasets.idle, cpu.idle, this.width)
|
||||||
let sys = stat.cpu.sys - this.last.cpu.sys
|
qpush(this.datasets.wait, cpu.wait, this.width)
|
||||||
let idle = stat.cpu.idle - this.last.cpu.idle
|
|
||||||
let wait = stat.cpu.wait - this.last.cpu.wait
|
|
||||||
let total = user + nice + sys + idle + wait
|
|
||||||
|
|
||||||
qpush(this.data.labels, now, this.width)
|
|
||||||
qpush(this.datasets.user, user/total, this.width)
|
|
||||||
qpush(this.datasets.nice, nice/total, this.width)
|
|
||||||
qpush(this.datasets.sys, sys/total, this.width)
|
|
||||||
qpush(this.datasets.idle, idle/total, this.width)
|
|
||||||
qpush(this.datasets.wait, wait/total, this.width)
|
|
||||||
|
|
||||||
//this.data.datasets[0].label= `user: ${user/total*100}`
|
|
||||||
}
|
|
||||||
this.last = stat
|
|
||||||
this.chart.update()
|
this.chart.update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
class PieChart {
|
||||||
new StatUpdater(2*Second)
|
constructor(element) {
|
||||||
|
this.canvas = element.appendChild(document.createElement("canvas"))
|
||||||
|
this.data ={
|
||||||
|
labels: ["user", "nice", "sys", "idle", "wait"],
|
||||||
|
datasets: []
|
||||||
|
}
|
||||||
|
this.chart = new Chart(
|
||||||
|
this.canvas,
|
||||||
|
{
|
||||||
|
type: "pie",
|
||||||
|
data: this.data,
|
||||||
|
options: {
|
||||||
|
animation: false,
|
||||||
|
borderWidth: 0,
|
||||||
|
backgroundColor: [
|
||||||
|
"red",
|
||||||
|
"green",
|
||||||
|
"orange",
|
||||||
|
"rgba(0, 64, 0, 0.2)",
|
||||||
|
"magenta",
|
||||||
|
],
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(stat) {
|
||||||
|
let cpu = stat.cpu()
|
||||||
|
this.data.datasets = [{
|
||||||
|
label: "Current",
|
||||||
|
data: [cpu.user, cpu.nice, cpu.sys, cpu.idle, cpu.wait],
|
||||||
|
}]
|
||||||
|
this.chart.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
export {
|
||||||
document.addEventListener("DOMContentLoaded", init)
|
Stat,
|
||||||
} else {
|
AreaChart,
|
||||||
init()
|
PieChart,
|
||||||
}
|
}
|
Loading…
Reference in New Issue