i dont know what happend

This commit is contained in:
2026-10-20 19:41:43 +02:00
parent 95af3a84af
commit 81b56228d7
7 changed files with 258 additions and 3 deletions
+133
View File
@@ -0,0 +1,133 @@
# Simple statserver: CPU usage + CPU temp, chart on localhost:8080
#
# Every sample is stored in SQLite (stats.db, next to this file). Rows older
# than 7 days are purged, so the database always holds the last 7 days. A
# background thread samples once per second, independent of web clients.
import os
import sqlite3
import threading
import time
from flask import Flask, request, jsonify, Response
app = Flask(__name__)
DB_PATH = os.path.join(os.path.dirname(__file__), "stats.db")
SEVEN_DAYS = 7 * 24 * 3600
# name -> (span in seconds, bucket size in seconds); day/week rows are
# averaged into buckets so the payload stays small.
RANGES = {
"minute": (60, 1),
"hour": (3600, 10),
"day": (86400, 60),
"week": (SEVEN_DAYS, 600),
}
_prev_cpu = None
_last = {"usage": 0.0, "temp": None}
_db = sqlite3.connect(DB_PATH, check_same_thread=False)
_db_lock = threading.Lock()
_db.execute(
"CREATE TABLE IF NOT EXISTS samples ("
"ts REAL PRIMARY KEY, usage REAL, temp REAL)"
)
_db.commit()
def _read_temp():
"""Read CPU temperature from sysfs, preferring the CPU package sensor.
Returns degrees Celsius, or None if nothing readable is found.
"""
fallback = None
base = "/sys/class/thermal"
for zone in sorted(x for x in os.listdir(base) if x.startswith("thermal_zone")):
try:
with open(f"{base}/{zone}/type") as f:
ztype = f.read().strip().lower()
with open(f"{base}/{zone}/temp") as f:
temp = int(f.read().strip())
except OSError:
continue
if ztype == "x86_pkg_temp": # package temp = best guess for CPU temp
return temp / 1000
if "cpu" in ztype or "pkg" in ztype or "core" in ztype:
fallback = fallback if fallback is not None else temp / 1000
return fallback
def _read_usage():
"""CPU usage since the previous call, from /proc/stat. Returns percent."""
with open("/proc/stat") as f:
cpu_line = next(line for line in f if line.startswith("cpu "))
fields = [int(x) for x in cpu_line.split()[1:] if x.isdigit()]
idle, total = fields[3], sum(fields)
global _prev_cpu
prev = _prev_cpu
_prev_cpu = (idle, total)
if prev is None:
return None
didle = idle - prev[0]
dtotal = total - prev[1]
return 100.0 * (1 - didle / dtotal) if dtotal > 0 else 0.0
def sample():
usage = _read_usage()
if usage is not None:
_last["usage"] = usage
_last["temp"] = _read_temp()
def _sampler():
"""Sample once per second and store it; purge >7-day-old rows every minute."""
n = 0
while True:
sample()
with _db_lock:
_db.execute(
"INSERT OR REPLACE INTO samples VALUES (?, ?, ?)",
(time.time(), _last["usage"], _last["temp"]),
)
# commit each insert so other connections (and restarts) see the
# data; a 1/sec commit is trivial load for SQLite
n += 1
if n % 60 == 0:
_db.execute("DELETE FROM samples WHERE ts < ?",
(time.time() - SEVEN_DAYS,))
_db.commit()
time.sleep(1)
@app.get("/api/stats")
def stats():
return jsonify(usage=_last["usage"], temp=_last["temp"])
@app.get("/api/history")
def history():
rng = request.args.get("range", "minute")
span, bucket = RANGES.get(rng, RANGES["minute"])
since = time.time() - span
with _db_lock:
rows = _db.execute(
"SELECT CAST(ts / ? AS INTEGER) * ? AS t, AVG(usage), AVG(temp) "
"FROM samples WHERE ts >= ? GROUP BY t ORDER BY t",
(bucket, bucket, since),
).fetchall()
return jsonify(points=[
{"t": round(r[0]), "u": r[1], "c": r[2]} for r in rows
])
@app.get("/")
def index():
with open(os.path.join(os.path.dirname(__file__), "index.html"), "rb") as f:
return Response(f.read(), mimetype="text/html")
if __name__ == "__main__":
threading.Thread(target=_sampler, daemon=True).start()
app.run(host="0.0.0.0", port=8080, threaded=True, use_reloader=False)