feat: объединить все температурные метрики (temp_*) в один график с разными цветами, вернуть сетевые графики

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
mirivlad 2026-04-14 03:13:03 +08:00
parent dbd71e3485
commit ce577c5d51
1 changed files with 171 additions and 38 deletions

View File

@ -123,7 +123,7 @@
<div class="row"> <div class="row">
{% for metricName, metricData in metrics %} {% for metricName, metricData in metrics %}
{% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" %} {% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" and not (metricName starts with "temp_") %}
<div class="col-12 mb-4"> <div class="col-12 mb-4">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
@ -160,10 +160,36 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
<!-- Температуры --> <!-- Графики сетевых интерфейсов -->
{% set has_temps = false %} {% set net_interfaces = [] %}
{% for m in metrics %}{% if m starts with 'temp_' %}{% set has_temps = true %}{% endif %}{% endfor %} {% for metricName in metrics|keys %}
{% if has_temps %} {% if metricName starts with 'net_in_' %}
{% set iface = metricName|replace({'net_in_': ''}) %}
{% set net_interfaces = net_interfaces|merge([iface]) %}
{% endif %}
{% endfor %}
{% for iface in net_interfaces %}
{% if metrics['net_in_' ~ iface] is defined and metrics['net_out_' ~ iface] is defined %}
<div class="row">
<div class="col-12 mb-4">
<div class="card">
<div class="card-header">
<h6 class="mb-0"><i class="fas fa-network-wired"></i> Сеть: {{ iface }}</h6>
</div>
<div class="card-body">
<canvas id="chart-net-{{ iface }}" width="100%" height="200"></canvas>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<!-- Температуры: один общий график -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card">
@ -171,12 +197,11 @@
<h6 class="mb-0"><i class="fas fa-thermometer-half"></i> Температуры</h6> <h6 class="mb-0"><i class="fas fa-thermometer-half"></i> Температуры</h6>
</div> </div>
<div class="card-body"> <div class="card-body">
<canvas id="chart-temperatures" width="100%" height="200"></canvas> <canvas id="chart-temperatures" width="100%" height="300"></canvas>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endif %}
<!-- Диски: Doughnut графики --> <!-- Диски: Doughnut графики -->
<div class="row mb-3"> <div class="row mb-3">
@ -552,7 +577,7 @@ var diskTotalGB = {
// Графики метрик // Графики метрик
{% for metricName, metricData in metrics %} {% for metricName, metricData in metrics %}
{% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" %} {% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" and not (metricName starts with "temp_") %}
const ctx{{ metricName|replace({'-': '_', '.': '_'}) }} = document.getElementById('chart-{{ metricName }}').getContext('2d'); const ctx{{ metricName|replace({'-': '_', '.': '_'}) }} = document.getElementById('chart-{{ metricName }}').getContext('2d');
// Подготовка данных для графика // Подготовка данных для графика
@ -726,7 +751,7 @@ chart{{ metricName|replace({'-': '_', '.': '_'}) }}.canvas.addEventListener('mou
// Глобальный обработчик для скрытия тултипов при уходе курсора за пределы canvas // Глобальный обработчик для скрытия тултипов при уходе курсора за пределы canvas
document.addEventListener('mousemove', function(e) { document.addEventListener('mousemove', function(e) {
{% for metricName, metricData in metrics %} {% for metricName, metricData in metrics %}
{% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" %} {% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" and not (metricName starts with "temp_") %}
(function() { (function() {
var canvas = chart{{ metricName|replace({'-': '_', '.': '_'}) }}.canvas; var canvas = chart{{ metricName|replace({'-': '_', '.': '_'}) }}.canvas;
var rect = canvas.getBoundingClientRect(); var rect = canvas.getBoundingClientRect();
@ -748,40 +773,61 @@ document.addEventListener('mousemove', function(e) {
// График температур // Графики сетевых интерфейсов (две линии: In зелёная, Out красная)
{% set temp_metrics = [] %} {% set net_interfaces = [] %}
{% for m in metrics %}{% if m starts with 'temp_' %}{% set temp_metrics = temp_metrics|merge([m]) %}{% endif %}{% endfor %} {% for metricName in metrics|keys %}
{% if temp_metrics %} {% if metricName starts with 'net_in_' %}
{% set net_interfaces = net_interfaces|merge([metricName|replace({'net_in_': ''})]) %}
{% endif %}
{% endfor %}
{% for iface in net_interfaces %}
{% if metrics['net_in_' ~ iface] is defined and metrics['net_out_' ~ iface] is defined %}
(function() { (function() {
var ctx = document.getElementById('chart-temperatures'); var ctx = document.getElementById('chart-net-{{ iface }}');
if (!ctx) return; if (!ctx) return;
var labels = []; var labels = [];
{% if metrics[temp_metrics[0]] is defined %} var inData = [];
{% for p in metrics[temp_metrics[0]]|slice(-100) %} var outData = [];
labels.push('{{ p.time_bucket|default(p.created_at)|date("d.m H:i") }}');
{% endfor %}
{% endif %}
var datasets = []; {% for m in metrics['net_in_' ~ iface]|slice(0, 500)|reverse %}
{% for m in temp_metrics %} labels.push('{{ m.created_at|date("d.m H:i") }}');
var data_{{ m }} = { inData.push({{ m.value }});
label: '{{ m|replace({'temp_': '', '_': ' '})|title }}',
data: [],
fill: false,
tension: 0.1,
pointRadius: 1,
borderWidth: 1
};
{% for p in metrics[m]|slice(-100) %}
data_{{ m }}.data.push({{ p.value }});
{% endfor %} {% endfor %}
datasets.push(data_{{ m }});
{% set outMetrics = metrics['net_out_' ~ iface]|slice(0, 500)|reverse %}
{% for m in outMetrics %}
outData.push({{ m.value }});
{% endfor %} {% endfor %}
new Chart(ctx.getContext('2d'), { new Chart(ctx.getContext('2d'), {
type: 'line', type: 'line',
data: { labels: labels, datasets: datasets }, data: {
labels: labels,
datasets: [
{
label: 'Входящий (In)',
data: inData,
borderColor: 'rgba(25, 135, 84, 1)',
backgroundColor: 'rgba(25, 135, 84, 0.1)',
fill: true,
tension: 0.1,
pointRadius: 0,
borderWidth: 1.5
},
{
label: 'Исходящий (Out)',
data: outData,
borderColor: 'rgba(220, 53, 69, 1)',
backgroundColor: 'rgba(220, 53, 69, 0.1)',
fill: true,
tension: 0.1,
pointRadius: 0,
borderWidth: 1.5
}
]
},
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
@ -789,13 +835,97 @@ document.addEventListener('mousemove', function(e) {
plugins: { plugins: {
legend: { display: true, position: 'top' }, legend: { display: true, position: 'top' },
tooltip: { enabled: true, mode: 'index', intersect: false }, tooltip: { enabled: true, mode: 'index', intersect: false },
zoom: { zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' }, pan: { enabled: true, mode: 'x' } } zoom: {
zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' },
pan: { enabled: true, mode: 'x' }
}
}, },
scales: { y: { beginAtZero: false, ticks: { callback: v => v + '°C' } } } scales: {
y: { beginAtZero: true, ticks: { callback: function(v) { return v + '%'; } } }
}
} }
}); });
})(); })();
{% endif %} {% endif %}
{% endfor %}
// График температур — ВСЕ temp_* на одном графике
(function() {
var ctx = document.getElementById('chart-temperatures');
if (!ctx) return;
var tempColors = [
{ border: 'rgba(255, 99, 132, 1)', bg: 'rgba(255, 99, 132, 0.1)' },
{ border: 'rgba(75, 192, 192, 1)', bg: 'rgba(75, 192, 192, 0.1)' },
{ border: 'rgba(255, 159, 64, 1)', bg: 'rgba(255, 159, 64, 0.1)' },
{ border: 'rgba(255, 205, 86, 1)', bg: 'rgba(255, 205, 86, 0.1)' },
{ border: 'rgba(201, 203, 207, 1)', bg: 'rgba(201, 203, 207, 0.1)' },
{ border: 'rgba(54, 162, 235, 1)', bg: 'rgba(54, 162, 235, 0.1)' },
{ border: 'rgba(153, 102, 255, 1)', bg: 'rgba(153, 102, 255, 0.1)' },
{ border: 'rgba(255, 99, 255, 1)', bg: 'rgba(255, 99, 255, 0.1)' },
{ border: 'rgba(100, 200, 100, 1)', bg: 'rgba(100, 200, 100, 0.1)' },
{ border: 'rgba(200, 150, 50, 1)', bg: 'rgba(200, 150, 50, 0.1)' }
];
var labels = [];
var labelsFilled = false;
var datasets = [];
var colorIdx = 0;
{% for mn, md in metrics %}
{% if mn starts with 'temp_' %}
if (!labelsFilled) {
{% for p in md|slice(-1000) %}
labels.push('{{ p.time_bucket|default(p.created_at)|date("d.m H:i") }}');
{% endfor %}
labelsFilled = true;
}
(function() {
var data = [];
{% for p in md|slice(-1000) %}
data.push({{ p.value }});
{% endfor %}
var color = tempColors[colorIdx % tempColors.length];
colorIdx++;
datasets.push({
label: '{{ mn|replace({'temp_': '', '_': ' '})|title }}',
data: data,
borderColor: color.border,
backgroundColor: color.bg,
fill: false,
tension: 0.1,
pointRadius: 1,
borderWidth: 2
});
})();
{% endif %}
{% endfor %}
var chart = new Chart(ctx.getContext('2d'), {
type: 'line',
data: { labels: labels, datasets: datasets },
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { display: true, position: 'top', labels: { boxWidth: 12, font: { size: 11 } } },
tooltip: { enabled: true, mode: 'index', intersect: false },
zoom: {
zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' },
pan: { enabled: true, mode: 'x' }
}
},
scales: { y: { beginAtZero: false, ticks: { callback: function(v) { return v + '°C'; } } } }
}
});
window.chartTemperatures = chart;
})();
// Doughnut графики для разделов дисков // Doughnut графики для разделов дисков
{% for metricName, metricData in metrics %} {% for metricName, metricData in metrics %}
@ -842,12 +972,15 @@ document.addEventListener('mousemove', function(e) {
// Сбросить зум на всех графиках // Сбросить зум на всех графиках
function resetAllZoom() { function resetAllZoom() {
{% for metricName, metricData in metrics %} {% for metricName, metricData in metrics %}
{% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" %} {% if metricName!="top_cpu_proc" and metricName!="top_ram_proc" and metricName!="disk_used" and not (metricName starts with "disk_used_") and not (metricName starts with "disk_total_gb_") and metricName!="ram_total_gb" and not (metricName starts with "net_in_") and not (metricName starts with "net_out_") and metricName!="network_rx" and metricName!="network_tx" and not (metricName starts with "temp_") %}
if (typeof chart{{ metricName|replace({'-': '_', '.': '_'}) }} !== 'undefined') { if (typeof chart{{ metricName|replace({'-': '_', '.': '_'}) }} !== 'undefined') {
chart{{ metricName|replace({'-': '_', '.': '_'}) }}.resetZoom(); chart{{ metricName|replace({'-': '_', '.': '_'}) }}.resetZoom();
} }
{% endif %} {% endif %}
{% endfor %} {% endfor %}
if (typeof window.chartTemperatures !== 'undefined') {
window.chartTemperatures.resetZoom();
}
} }
</script> </script>