This commit is contained in:
devdatt 2026-02-18 03:41:10 +05:30
parent ef2e104172
commit db29cc1600
2 changed files with 233 additions and 222 deletions

View File

@ -9,6 +9,7 @@ include 'header.php'; ?>
<div class="containerindex"> <div class="containerindex">
<div class="grid"> <div class="grid">
<div class="card"> <div class="card">
<h3>CPU (%)</h3> <h3>CPU (%)</h3>
<div class="chart-wrap"><canvas id="cpuChart"></canvas></div> <div class="chart-wrap"><canvas id="cpuChart"></canvas></div>
@ -19,6 +20,11 @@ include 'header.php'; ?>
<div class="chart-wrap"><canvas id="ramChart"></canvas></div> <div class="chart-wrap"><canvas id="ramChart"></canvas></div>
</div> </div>
<div class="card">
<h3>Intel iGPU Engines (%)</h3>
<div class="chart-wrap"><canvas id="gpuChart"></canvas></div>
</div>
<div class="card"> <div class="card">
<h3>Network (KB/s)</h3> <h3>Network (KB/s)</h3>
<div class="chart-wrap"><canvas id="netChart"></canvas></div> <div class="chart-wrap"><canvas id="netChart"></canvas></div>
@ -28,10 +34,6 @@ include 'header.php'; ?>
<h3>Disk I/O (KB/s) & Disk %</h3> <h3>Disk I/O (KB/s) & Disk %</h3>
<div class="chart-wrap"><canvas id="diskChart"></canvas></div> <div class="chart-wrap"><canvas id="diskChart"></canvas></div>
</div> </div>
<div class="card">
<h3>Intel iGPU (%)</h3>
<div class="chart-wrap"><canvas id="gpuChart"></canvas></div>
</div>
</div> </div>
@ -45,6 +47,7 @@ include 'header.php'; ?>
Out: <span id="lastOut"></span>KB/s Out: <span id="lastOut"></span>KB/s
</div> </div>
</div> </div>
<br><br><br><br> <br><br><br><br>
</div> </div>
@ -56,69 +59,34 @@ include 'header.php'; ?>
/* CPU */ /* CPU */
const cpuChart = new Chart(document.getElementById('cpuChart'),{ const cpuChart = new Chart(document.getElementById('cpuChart'),{
type:'line', type:'line',
data: { data:{labels:[],datasets:[{label:'CPU %',data:[],tension:0.2}]},
labels: [], options:{responsive:true,maintainAspectRatio:false,scales:{y:{min:0,max:100}}}
datasets: [{
label: 'CPU %',
data: [],
tension: 0.2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
min: 0,
max: 100
}
}
}
}); });
/* RAM */ /* RAM */
const ramChart = new Chart(document.getElementById('ramChart'),{ const ramChart = new Chart(document.getElementById('ramChart'),{
type:'line', type:'line',
data: { data:{labels:[],datasets:[{label:'RAM %',data:[],tension:0.2}]},
labels: [], options:{responsive:true,maintainAspectRatio:false,scales:{y:{min:0,max:100}}}
datasets: [{
label: 'RAM %',
data: [],
tension: 0.2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
min: 0,
max: 100
}
}
}
}); });
/* GPU */ /* GPU multi-engine */
const gpuChart = new Chart(document.getElementById('gpuChart'),{ const gpuChart = new Chart(document.getElementById('gpuChart'),{
type:'line', type:'line',
data:{ data:{
labels:[], labels:[],
datasets: [{ datasets:[
label: 'iGPU %', {label:'Total',data:[],tension:0.2},
data: [], {label:'Video',data:[],tension:0.2},
tension: 0.2 {label:'Render',data:[],tension:0.2},
}] {label:'Blitter',data:[],tension:0.2},
{label:'Enhance',data:[],tension:0.2}
]
}, },
options:{ options:{
responsive:true, responsive:true,
maintainAspectRatio:false, maintainAspectRatio:false,
scales: { scales:{y:{min:0,max:100}}
y: {
min: 0,
max: 100
}
}
} }
}); });
@ -127,27 +95,12 @@ include 'header.php'; ?>
type:'line', type:'line',
data:{ data:{
labels:[], labels:[],
datasets: [{ datasets:[
label: 'Net In (KB/s)', {label:'Net In (KB/s)',data:[],tension:0.2},
data: [], {label:'Net Out (KB/s)',data:[],tension:0.2}
tension: 0.2
},
{
label: 'Net Out (KB/s)',
data: [],
tension: 0.2
}
] ]
}, },
options: { options:{responsive:true,maintainAspectRatio:false,scales:{y:{beginAtZero:true}}}
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
}
}); });
/* Disk */ /* Disk */
@ -155,42 +108,23 @@ include 'header.php'; ?>
type:'line', type:'line',
data:{ data:{
labels:[], labels:[],
datasets: [{ datasets:[
label: 'Disk Read (KB/s)', {label:'Disk Read (KB/s)',data:[],tension:0.2},
data: [], {label:'Disk Write (KB/s)',data:[],tension:0.2},
tension: 0.2 {label:'Disk %',data:[],yAxisID:'percent',tension:0.2}
},
{
label: 'Disk Write (KB/s)',
data: [],
tension: 0.2
},
{
label: 'Disk %',
data: [],
yAxisID: 'percent',
tension: 0.2
}
] ]
}, },
options:{ options:{
responsive:true, responsive:true,
maintainAspectRatio:false, maintainAspectRatio:false,
scales:{ scales:{
y: { y:{position:'left',beginAtZero:true},
position: 'left',
beginAtZero: true
},
percent:{ percent:{
position:'right', position:'right',
min:0, min:0,
max:100, max:100,
grid: { grid:{display:false},
display: false ticks:{callback:v=>v+'%'}
},
ticks: {
callback: v => v + '%'
}
} }
} }
} }
@ -198,27 +132,34 @@ include 'header.php'; ?>
async function update(){ async function update(){
try{ try{
const res = await fetch(JSON_URL + "?_=" + Date.now(), { const res = await fetch(JSON_URL+"?_="+Date.now(),{cache:'no-store'});
cache: 'no-store'
});
if(!res.ok) throw new Error(res.status); if(!res.ok) throw new Error(res.status);
const j = await res.json(); const j = await res.json();
const labels = j.timestamps.map(t=>new Date(t).toLocaleTimeString()); const labels = j.timestamps.map(t=>new Date(t).toLocaleTimeString());
/* CPU */
cpuChart.data.labels=labels; cpuChart.data.labels=labels;
cpuChart.data.datasets[0].data=j.cpu_percent; cpuChart.data.datasets[0].data=j.cpu_percent;
/* RAM */
ramChart.data.labels=labels; ramChart.data.labels=labels;
ramChart.data.datasets[0].data=j.ram_percent; ramChart.data.datasets[0].data=j.ram_percent;
/* GPU */
gpuChart.data.labels=labels; gpuChart.data.labels=labels;
gpuChart.data.datasets[0].data = j.igpu_percent; gpuChart.data.datasets[0].data=j.gpu_total;
gpuChart.data.datasets[1].data=j.gpu_video;
gpuChart.data.datasets[2].data=j.gpu_render;
gpuChart.data.datasets[3].data=j.gpu_blitter;
gpuChart.data.datasets[4].data=j.gpu_videoenhance;
/* NET */
netChart.data.labels=labels; netChart.data.labels=labels;
netChart.data.datasets[0].data=j.net_in_Bps.map(toKB); netChart.data.datasets[0].data=j.net_in_Bps.map(toKB);
netChart.data.datasets[1].data=j.net_out_Bps.map(toKB); netChart.data.datasets[1].data=j.net_out_Bps.map(toKB);
/* DISK */
diskChart.data.labels=labels; diskChart.data.labels=labels;
diskChart.data.datasets[0].data=j.disk_read_Bps.map(toKB); diskChart.data.datasets[0].data=j.disk_read_Bps.map(toKB);
diskChart.data.datasets[1].data=j.disk_write_Bps.map(toKB); diskChart.data.datasets[1].data=j.disk_write_Bps.map(toKB);
@ -235,14 +176,16 @@ include 'header.php'; ?>
lastUpdate.textContent=labels[last]; lastUpdate.textContent=labels[last];
lastCpu.textContent=j.cpu_percent[last]; lastCpu.textContent=j.cpu_percent[last];
lastRam.textContent=j.ram_percent[last]; lastRam.textContent=j.ram_percent[last];
lastGpu.textContent = j.igpu_percent[last]; lastGpu.textContent=j.gpu_total[last];
lastIn.textContent=toKB(j.net_in_Bps[last]); lastIn.textContent=toKB(j.net_in_Bps[last]);
lastOut.textContent=toKB(j.net_out_Bps[last]); lastOut.textContent=toKB(j.net_out_Bps[last]);
} }
}catch(e){ }catch(e){
console.error(e); console.error("metrics fetch error",e);
} }
} }
setInterval(update,POLL_MS); setInterval(update,POLL_MS);
update(); update();
</script> </script>

118
setup.sh
View File

@ -176,7 +176,7 @@ mount -a
cat > /usr/local/bin/nginx_system_monitor_sampler.py<< 'EOL' cat > /usr/local/bin/nginx_system_monitor_sampler.py<< 'EOL'
#!/usr/bin/env python3 #!/usr/bin/env python3
import time, json, os, subprocess import time, json, os, subprocess, threading
from collections import deque from collections import deque
from datetime import datetime from datetime import datetime
import psutil import psutil
@ -187,10 +187,17 @@ SAMPLE_INTERVAL=10.0
HISTORY_SECONDS=15*60 HISTORY_SECONDS=15*60
MAX_SAMPLES=int(HISTORY_SECONDS/SAMPLE_INTERVAL) MAX_SAMPLES=int(HISTORY_SECONDS/SAMPLE_INTERVAL)
# history buffers
timestamps=deque(maxlen=MAX_SAMPLES) timestamps=deque(maxlen=MAX_SAMPLES)
cpu_hist=deque(maxlen=MAX_SAMPLES) cpu_hist=deque(maxlen=MAX_SAMPLES)
ram_hist=deque(maxlen=MAX_SAMPLES) ram_hist=deque(maxlen=MAX_SAMPLES)
gpu_hist=deque(maxlen=MAX_SAMPLES)
gpu_total_hist=deque(maxlen=MAX_SAMPLES)
gpu_render_hist=deque(maxlen=MAX_SAMPLES)
gpu_video_hist=deque(maxlen=MAX_SAMPLES)
gpu_blitter_hist=deque(maxlen=MAX_SAMPLES)
gpu_ve_hist=deque(maxlen=MAX_SAMPLES)
net_in_hist=deque(maxlen=MAX_SAMPLES) net_in_hist=deque(maxlen=MAX_SAMPLES)
net_out_hist=deque(maxlen=MAX_SAMPLES) net_out_hist=deque(maxlen=MAX_SAMPLES)
disk_read_hist=deque(maxlen=MAX_SAMPLES) disk_read_hist=deque(maxlen=MAX_SAMPLES)
@ -201,30 +208,74 @@ _prev_net=psutil.net_io_counters()
_prev_disk=psutil.disk_io_counters() _prev_disk=psutil.disk_io_counters()
_prev_time=time.time() _prev_time=time.time()
def igpu_percent(): # shared gpu values
try: gpu_data={
out = subprocess.check_output( "total":0.0,
["intel_gpu_top","-J","-s","200","-o","-"], "Render/3D":0.0,
stderr=subprocess.DEVNULL, "Video":0.0,
timeout=2 "Blitter":0.0,
) "VideoEnhance":0.0
line = out.decode().splitlines()[0] }
data = json.loads(line) gpu_lock=threading.Lock()
engines = data.get("engines",{})
if not engines:
return 0.0
return round(max(v.get("busy",0) for v in engines.values()),2)
except Exception:
return 0.0
# ---------- persistent GPU monitor ----------
def gpu_monitor():
global gpu_data
while True:
try:
p=subprocess.Popen(
["intel_gpu_top","-J","-s","1000","-o","-"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
bufsize=1
)
for line in p.stdout:
if '"engines"' not in line:
continue
try:
j=json.loads(line.rstrip(",\n"))
engines=j.get("engines",{})
if not engines:
continue
with gpu_lock:
for k in gpu_data:
gpu_data[k]=0.0
for name,val in engines.items():
gpu_data[name]=float(val.get("busy",0))
gpu_data["total"]=max(
float(v.get("busy",0))
for v in engines.values()
)
except:
pass
except:
pass
time.sleep(2) # restart delay if intel_gpu_top exits
threading.Thread(target=gpu_monitor,daemon=True).start()
# ---------- sampling ----------
def sample_once(): def sample_once():
global _prev_net,_prev_disk,_prev_time global _prev_net,_prev_disk,_prev_time
now=time.time() now=time.time()
iso=datetime.fromtimestamp(now).isoformat(timespec='seconds') iso=datetime.fromtimestamp(now).isoformat(timespec='seconds')
cpu=psutil.cpu_percent(interval=None) cpu=psutil.cpu_percent(interval=None)
ram=psutil.virtual_memory().percent ram=psutil.virtual_memory().percent
gpu=igpu_percent()
with gpu_lock:
gtot=gpu_data["total"]
gr=gpu_data["Render/3D"]
gv=gpu_data["Video"]
gb=gpu_data["Blitter"]
ge=gpu_data["VideoEnhance"]
net=psutil.net_io_counters() net=psutil.net_io_counters()
disk=psutil.disk_io_counters() disk=psutil.disk_io_counters()
@ -237,16 +288,21 @@ def sample_once():
elapsed=now-_prev_time if _prev_time else SAMPLE_INTERVAL elapsed=now-_prev_time if _prev_time else SAMPLE_INTERVAL
if elapsed<=0: elapsed=SAMPLE_INTERVAL if elapsed<=0: elapsed=SAMPLE_INTERVAL
in_rate=int(((net.bytes_recv-_prev_net.bytes_recv)/elapsed)) in_rate=(net.bytes_recv-_prev_net.bytes_recv)/elapsed
out_rate=int(((net.bytes_sent-_prev_net.bytes_sent)/elapsed)) out_rate=(net.bytes_sent-_prev_net.bytes_sent)/elapsed
read_rate=(disk.read_bytes-_prev_disk.read_bytes)/elapsed read_rate=(disk.read_bytes-_prev_disk.read_bytes)/elapsed
write_rate=(disk.write_bytes-_prev_disk.write_bytes)/elapsed write_rate=(disk.write_bytes-_prev_disk.write_bytes)/elapsed
timestamps.append(iso) timestamps.append(iso)
cpu_hist.append(round(cpu,2)) cpu_hist.append(round(cpu,2))
ram_hist.append(round(ram,2)) ram_hist.append(round(ram,2))
gpu_hist.append(round(gpu,2))
gpu_total_hist.append(round(gtot,2))
gpu_render_hist.append(round(gr,2))
gpu_video_hist.append(round(gv,2))
gpu_blitter_hist.append(round(gb,2))
gpu_ve_hist.append(round(ge,2))
net_in_hist.append(int(in_rate)) net_in_hist.append(int(in_rate))
net_out_hist.append(int(out_rate)) net_out_hist.append(int(out_rate))
disk_read_hist.append(int(read_rate)) disk_read_hist.append(int(read_rate))
@ -257,29 +313,40 @@ def sample_once():
_prev_disk=disk _prev_disk=disk
_prev_time=now _prev_time=now
# ---------- write ----------
def write_json_atomic(): def write_json_atomic():
payload={ payload={
"timestamps":list(timestamps), "timestamps":list(timestamps),
"cpu_percent":list(cpu_hist), "cpu_percent":list(cpu_hist),
"ram_percent":list(ram_hist), "ram_percent":list(ram_hist),
"igpu_percent":list(gpu_hist),
"gpu_total":list(gpu_total_hist),
"gpu_render":list(gpu_render_hist),
"gpu_video":list(gpu_video_hist),
"gpu_blitter":list(gpu_blitter_hist),
"gpu_videoenhance":list(gpu_ve_hist),
"net_in_Bps":list(net_in_hist), "net_in_Bps":list(net_in_hist),
"net_out_Bps":list(net_out_hist), "net_out_Bps":list(net_out_hist),
"disk_read_Bps":list(disk_read_hist), "disk_read_Bps":list(disk_read_hist),
"disk_write_Bps":list(disk_write_hist), "disk_write_Bps":list(disk_write_hist),
"disk_percent":list(disk_percent_hist), "disk_percent":list(disk_percent_hist),
"sample_interval":SAMPLE_INTERVAL, "sample_interval":SAMPLE_INTERVAL,
"generated_at":datetime.utcnow().isoformat(timespec='seconds')+"Z" "generated_at":datetime.utcnow().isoformat(timespec='seconds')+"Z"
} }
with open(TMP_FILE,"w") as f: json.dump(payload,f)
with open(TMP_FILE,"w") as f:
json.dump(payload,f)
os.replace(TMP_FILE,OUT_FILE) os.replace(TMP_FILE,OUT_FILE)
# ---------- main ----------
def main(): def main():
global _prev_net,_prev_disk,_prev_time global _prev_net,_prev_disk,_prev_time
_prev_net=psutil.net_io_counters() _prev_net=psutil.net_io_counters()
_prev_disk=psutil.disk_io_counters() _prev_disk=psutil.disk_io_counters()
_prev_time=time.time() _prev_time=time.time()
time.sleep(0.2)
while True: while True:
try: try:
@ -287,6 +354,7 @@ def main():
write_json_atomic() write_json_atomic()
except Exception as e: except Exception as e:
print("Sampler error:",e) print("Sampler error:",e)
time.sleep(SAMPLE_INTERVAL) time.sleep(SAMPLE_INTERVAL)
if __name__=="__main__": if __name__=="__main__":