BE+FE: System info -> Performance section

This commit is contained in:
jokob-sk committed 2026-09-28 15:11:24 +10:00
1 parent 4aa2a0dddb
commit 66d13bd55c
3 files changed
+76 -24

No files matched your search

+23 -7
View File
@@ -91,7 +91,17 @@ function deleteCookie (cookie) {
// -----------------------------------------------------------------------------
// DateTime utilities
// -----------------------------------------------------------------------------
function localizeTimestamp(input) {
/**
* Parses a timestamp in any of several common formats (unix seconds/ms,
* DD/MM/YYYY, MM/DD/YYYY, ISO, RFC2822, DD-MM-YYYY) and formats it in the
* user's configured TIMEZONE/UI_LOCALE settings.
* @param {string|number} input - The timestamp to localize.
* @param {Intl.DateTimeFormatOptions} [dtfOptions] - Full Intl.DateTimeFormat
* options object, replacing (not merging with) the default year/month/day/
* hour/minute/second/hour12 shape - e.g. for a shorter chart-axis label.
* @returns {string} The localized, formatted timestamp.
*/
function localizeTimestamp(input, dtfOptions) {
// Read fresh on every call (not a module-level const): getSetting() reads
// a localStorage cache that clearCache() wipes before reloading, so a
@@ -99,6 +109,16 @@ function localizeTimestamp(input) {
const tz = getSetting("TIMEZONE") || 'Europe/Berlin';
const LOCALE = getSetting('UI_LOCALE') || 'en-GB';
// Optional second arg: full Intl.DateTimeFormat options object, replacing
// (not merging with) the historical year+seconds default below - e.g. a
// chart axis label that wants month/day/hour/minute only. Omit it for the
// original full-precision behavior every existing caller relies on.
const options = dtfOptions || {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
};
input = String(input || '').trim();
// 1. Unix timestamps (10 or 13 digits)
@@ -106,9 +126,7 @@ function localizeTimestamp(input) {
const ms = input.length === 10 ? parseInt(input, 10) * 1000 : parseInt(input, 10);
return new Intl.DateTimeFormat('default', {
timeZone: tz,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
...options
}).format(new Date(ms));
}
@@ -198,9 +216,7 @@ function localizeTimestamp(input) {
return new Intl.DateTimeFormat(LOCALE, {
// Convert from UTC to user's configured timezone
timeZone: tz,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
...options
}).format(date);
}
}
+48 -12
View File
@@ -2,6 +2,23 @@
// building a new one on the same canvas (Chart.js throws otherwise).
var resourceHistoryChartInstances = {};
// Chart axis labels: time only - date/year are dropped entirely to keep the
// axis readable; the full date (including year) is still available on
// hover via each chart's tooltip title callback below. Passed as
// localizeTimestamp()'s options override so parsing/timezone/locale logic
// stays in one shared place (common.js).
var CHART_TIMESTAMP_OPTIONS_AXIS = {
hour: '2-digit', minute: '2-digit',
hour12: false
};
// Full timestamp (including year) shown in the tooltip title on hover.
var CHART_TIMESTAMP_OPTIONS_TOOLTIP = {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
};
/**
* Fetches Resource_History data for the given range ('hour'/'day'/'week'/'month')
* and renders the four Performance-tab charts, or shows the disabled-state
@@ -21,6 +38,7 @@ function initResourceHistoryGraphs(range) {
$('#resourceHistoryCharts').removeClass('myhidden');
var labels = [];
var fullLabels = [];
var cpuData = [];
var rssData = [];
var ioReadData = [];
@@ -36,7 +54,8 @@ function initResourceHistoryGraphs(range) {
// hour/day rows carry resDateTime (raw); week/month rows carry bucket
// (hourly rollup) instead - see const.py's sql_resource_history_* queries.
var ts = entry.resDateTime || entry.bucket;
labels.push(localizeTimestamp(ts).slice(0, 16));
labels.push(localizeTimestamp(ts, CHART_TIMESTAMP_OPTIONS_AXIS));
fullLabels.push(localizeTimestamp(ts, CHART_TIMESTAMP_OPTIONS_TOOLTIP));
cpuData.push(round2(entry.resCpuPercent));
rssData.push(round2(entry.resRssMb));
// Bytes -> MB and ms -> s: raw units from the DB aren't a readable axis scale.
@@ -46,7 +65,7 @@ function initResourceHistoryGraphs(range) {
tickFailed.push(entry.resTickFailed == 1);
});
renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWriteData, durationData, tickFailed);
renderResourceHistoryCharts(labels, fullLabels, cpuData, rssData, ioReadData, ioWriteData, durationData, tickFailed);
}).fail(function () {
console.error('Error fetching resource history data.');
});
@@ -57,9 +76,11 @@ function initResourceHistoryGraphs(range) {
* memory usage MB, IO read+write MB, scan duration seconds) on the same time
* axis. Rows written from the tick-failure `finally` path (resTickFailed = 1)
* are rendered as visually distinct points rather than plain data, since
* their numbers may reflect a truncated, crash-adjacent sample.
* their numbers may reflect a truncated, crash-adjacent sample. `labels`
* (time-only, used for the axis) and `fullLabels` (full date+year, used for
* the hover tooltip title) are parallel arrays over the same rows.
*/
function renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWriteData, durationData, tickFailed) {
function renderResourceHistoryCharts(labels, fullLabels, cpuData, rssData, ioReadData, ioWriteData, durationData, tickFailed) {
var normalColor = "rgba(0, 166, 89, .8)";
var failedColor = "#dd4b39";
@@ -75,15 +96,30 @@ function renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWri
var commonScales = {
yAxes: [{
ticks: { beginAtZero: true, fontColor: '#A0A0A0' },
gridLines: { color: "rgba(0, 0, 0, 0)" },
// maxTicksLimit caps how many labels Chart.js draws - without it, a
// short/fixed-height chart crams in enough ticks that adjacent labels
// visually overlap.
ticks: { beginAtZero: true, fontColor: '#A0A0A0', maxTicksLimit: 5 },
// Faint gridlines only at the (few, capped) main ticks - easier to
// read a value off the chart without making it visually noisy.
gridLines: { color: "rgba(160, 160, 160, 0.15)", zeroLineColor: "rgba(160, 160, 160, 0.3)" },
}],
xAxes: [{
ticks: { fontColor: '#A0A0A0' },
gridLines: { color: "rgba(0, 0, 0, 0)" },
ticks: { fontColor: '#A0A0A0', maxTicksLimit: 10, autoSkip: true },
gridLines: { color: "rgba(160, 160, 160, 0.15)", zeroLineColor: "rgba(160, 160, 160, 0.3)" },
}],
};
// Axis labels are time-only (CHART_TIMESTAMP_OPTIONS_AXIS); show the full
// date (including year) on hover instead, via fullLabels.
var commonTooltips = {
callbacks: {
title: function (tooltipItems) {
return fullLabels[tooltipItems[0].index];
},
},
};
destroyIfExists('cpu');
resourceHistoryChartInstances.cpu = new Chart("ResourceCpuChart", {
type: "line",
@@ -98,7 +134,7 @@ function renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWri
fill: true,
}],
},
options: { legend: { display: true }, scales: commonScales, maintainAspectRatio: false, responsive: true },
options: { legend: { display: true }, scales: commonScales, tooltips: commonTooltips, maintainAspectRatio: false, responsive: true },
});
destroyIfExists('rss');
@@ -115,7 +151,7 @@ function renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWri
fill: true,
}],
},
options: { legend: { display: true }, scales: commonScales, maintainAspectRatio: false, responsive: true },
options: { legend: { display: true }, scales: commonScales, tooltips: commonTooltips, maintainAspectRatio: false, responsive: true },
});
destroyIfExists('io');
@@ -140,7 +176,7 @@ function renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWri
},
],
},
options: { legend: { display: true }, scales: commonScales, maintainAspectRatio: false, responsive: true },
options: { legend: { display: true }, scales: commonScales, tooltips: commonTooltips, maintainAspectRatio: false, responsive: true },
});
destroyIfExists('duration');
@@ -157,6 +193,6 @@ function renderResourceHistoryCharts(labels, cpuData, rssData, ioReadData, ioWri
fill: true,
}],
},
options: { legend: { display: true }, scales: commonScales, maintainAspectRatio: false, responsive: true },
options: { legend: { display: true }, scales: commonScales, tooltips: commonTooltips, maintainAspectRatio: false, responsive: true },
});
}
+5 -5
View File
@@ -11,7 +11,7 @@
<div class="box box-solid">
<div class="box-header">
<h3 class="box-title sysinfo_headline"><i class="fa fa-chart-line"></i> <?= lang('Systeminfo_Performance');?>
<a href="https://docs.netalertx.com/PERFORMANCE" target="_blank"><i class="fa fa-circle-question"></i></a>
<a href="https://docs.netalertx.com/PERFORMANCE" target="_blank"><i class="fa fa-circle-question" style="font-size: 14px;"></i></a>
</h3>
</div>
<div class="box-body">
@@ -28,17 +28,17 @@
</div>
<div id="resourceHistoryCharts">
<div style="position: relative; height: 150px; width: 100%; margin-bottom: 25px;">
<div style="position: relative; height: 220px; width: 100%; margin-bottom: 25px;">
<canvas id="ResourceCpuChart"></canvas>
</div>
<div style="position: relative; height: 150px; width: 100%; margin-bottom: 25px;">
<div style="position: relative; height: 220px; width: 100%; margin-bottom: 25px;">
<canvas id="ResourceRssChart"></canvas>
</div>
<div style="position: relative; height: 150px; width: 100%; margin-bottom: 5px;">
<div style="position: relative; height: 220px; width: 100%; margin-bottom: 5px;">
<canvas id="ResourceIoChart"></canvas>
</div>
<p class="text-muted" style="font-size: 12px; margin-bottom: 25px;"><?= lang('Systeminfo_Performance_IO_Caption');?></p>
<div style="position: relative; height: 150px; width: 100%; margin-bottom: 15px;">
<div style="position: relative; height: 220px; width: 100%; margin-bottom: 15px;">
<canvas id="ResourceDurationChart"></canvas>
</div>
</div>