51 lines
1.7 KiB
Python
51 lines
1.7 KiB
Python
import asyncio
|
|
import time
|
|
|
|
from app.collect import cpu, disks, gpu, mem, power
|
|
from app.sample import Sample
|
|
from app.state import HistoryStore
|
|
|
|
|
|
def _collect() -> Sample:
|
|
"""Fill a fresh Sample with one synchronous collector pass.
|
|
|
|
Runs in a worker thread (see sampler_loop) because the collectors hit
|
|
sysfs and psutil. Disk read/write rates are intentionally not set here:
|
|
they need the delta between two samples, which sampler_loop keeps.
|
|
|
|
Returns:
|
|
A Sample with cpu, load, memory, swap, GPU, and power fields filled.
|
|
"""
|
|
sample = Sample()
|
|
cpu.fill(sample)
|
|
mem.fill(sample)
|
|
gpu.fill(sample)
|
|
power.fill(sample)
|
|
return sample
|
|
|
|
|
|
async def sampler_loop(store: HistoryStore, sample_interval: float) -> None:
|
|
"""Sample the system into the store every `sample_interval` seconds, forever.
|
|
|
|
Before the first sample it primes `psutil.cpu_percent` (its first call
|
|
always returns 0) and takes a baseline disk-counter reading, so the
|
|
first stored sample already carries valid CPU and disk rates. Each loop
|
|
collects in a worker thread, then computes per-disk byte deltas divided
|
|
by the elapsed time as the aggregate io_read / io_write rates.
|
|
|
|
Args:
|
|
store: ring buffer that receives each sample.
|
|
sample_interval: seconds between samples.
|
|
"""
|
|
cpu.prime()
|
|
prev_disk = disks.counters()
|
|
prev_t = time.monotonic()
|
|
while True:
|
|
await asyncio.sleep(sample_interval)
|
|
sample = await asyncio.to_thread(_collect)
|
|
now = time.monotonic()
|
|
dt = now - prev_t
|
|
sample.io_read, sample.io_write = disks.rates(prev_disk, dt)
|
|
prev_disk = disks.counters()
|
|
prev_t = now
|
|
store.record(sample)
|