네트워크 오류 드롭 이력 추가

This commit is contained in:
seo
2026-07-25 23:15:18 +09:00
parent e500eb2876
commit 7f9a5d1a81
5 changed files with 201 additions and 3 deletions
+51 -1
View File
@@ -1048,6 +1048,10 @@ function add_network_usage_log_for_interface(array $iface): void
$txBytes = isset($iface['tx_bytes']) ? (int)$iface['tx_bytes'] : -1;
$rxPackets = isset($iface['rx_packets']) ? (int)$iface['rx_packets'] : -1;
$txPackets = isset($iface['tx_packets']) ? (int)$iface['tx_packets'] : -1;
$rxErrors = isset($iface['rx_errors']) ? (int)$iface['rx_errors'] : 0;
$txErrors = isset($iface['tx_errors']) ? (int)$iface['tx_errors'] : 0;
$rxDropped = isset($iface['rx_dropped']) ? (int)$iface['rx_dropped'] : 0;
$txDropped = isset($iface['tx_dropped']) ? (int)$iface['tx_dropped'] : 0;
if ($name === '' || $rxBytes < 0 || $txBytes < 0 || $rxPackets < 0 || $txPackets < 0) {
return;
}
@@ -1060,6 +1064,10 @@ function add_network_usage_log_for_interface(array $iface): void
tx_bytes,
rx_packets,
tx_packets,
rx_errors,
tx_errors,
rx_dropped,
tx_dropped,
TIMESTAMPDIFF(MICROSECOND, recorded_at, NOW(3)) AS elapsed_us
FROM network_usage_logs
WHERE iface = :iface
@@ -1073,6 +1081,10 @@ function add_network_usage_log_for_interface(array $iface): void
$txMbps = null;
$rxPps = null;
$txPps = null;
$rxErrorsPerSec = null;
$txErrorsPerSec = null;
$rxDroppedPerSec = null;
$txDroppedPerSec = null;
if ($previous) {
$seconds = max(0.0, (float)($previous['elapsed_us'] ?? 0) / 1000000);
if ($seconds < 0.8) {
@@ -1083,12 +1095,22 @@ function add_network_usage_log_for_interface(array $iface): void
$txDelta = $txBytes - (int)$previous['tx_bytes'];
$rxPacketDelta = $rxPackets - (int)$previous['rx_packets'];
$txPacketDelta = $txPackets - (int)$previous['tx_packets'];
$rxErrorDelta = $rxErrors - (int)$previous['rx_errors'];
$txErrorDelta = $txErrors - (int)$previous['tx_errors'];
$rxDroppedDelta = $rxDropped - (int)$previous['rx_dropped'];
$txDroppedDelta = $txDropped - (int)$previous['tx_dropped'];
if ($seconds > 0 && $seconds <= 60 && $rxDelta >= 0 && $txDelta >= 0 && $rxPacketDelta >= 0 && $txPacketDelta >= 0) {
$rxMbps = round(($rxDelta * 8) / $seconds / 1000000, 4);
$txMbps = round(($txDelta * 8) / $seconds / 1000000, 4);
$rxPps = round($rxPacketDelta / $seconds, 2);
$txPps = round($txPacketDelta / $seconds, 2);
}
if ($seconds > 0 && $seconds <= 60 && $rxErrorDelta >= 0 && $txErrorDelta >= 0 && $rxDroppedDelta >= 0 && $txDroppedDelta >= 0) {
$rxErrorsPerSec = round($rxErrorDelta / $seconds, 2);
$txErrorsPerSec = round($txErrorDelta / $seconds, 2);
$rxDroppedPerSec = round($rxDroppedDelta / $seconds, 2);
$txDroppedPerSec = round($txDroppedDelta / $seconds, 2);
}
}
$stmt = $pdo->prepare("
@@ -1099,10 +1121,18 @@ function add_network_usage_log_for_interface(array $iface): void
tx_bytes,
rx_packets,
tx_packets,
rx_errors,
tx_errors,
rx_dropped,
tx_dropped,
rx_mbps,
tx_mbps,
rx_pps,
tx_pps,
rx_errors_per_sec,
tx_errors_per_sec,
rx_dropped_per_sec,
tx_dropped_per_sec,
create_ip
)
VALUES
@@ -1112,10 +1142,18 @@ function add_network_usage_log_for_interface(array $iface): void
:tx_bytes,
:rx_packets,
:tx_packets,
:rx_errors,
:tx_errors,
:rx_dropped,
:tx_dropped,
:rx_mbps,
:tx_mbps,
:rx_pps,
:tx_pps,
:rx_errors_per_sec,
:tx_errors_per_sec,
:rx_dropped_per_sec,
:tx_dropped_per_sec,
:create_ip
)
");
@@ -1125,10 +1163,18 @@ function add_network_usage_log_for_interface(array $iface): void
':tx_bytes' => $txBytes,
':rx_packets' => $rxPackets,
':tx_packets' => $txPackets,
':rx_errors' => $rxErrors,
':tx_errors' => $txErrors,
':rx_dropped' => $rxDropped,
':tx_dropped' => $txDropped,
':rx_mbps' => $rxMbps,
':tx_mbps' => $txMbps,
':rx_pps' => $rxPps,
':tx_pps' => $txPps,
':rx_errors_per_sec' => $rxErrorsPerSec,
':tx_errors_per_sec' => $txErrorsPerSec,
':rx_dropped_per_sec' => $rxDroppedPerSec,
':tx_dropped_per_sec' => $txDroppedPerSec,
':create_ip' => $_SERVER['REMOTE_ADDR'] ?? 'cli',
]);
}
@@ -1152,7 +1198,11 @@ function network_usage_history(int $limit = 240): array
ROUND(rx_mbps / 8, 4) AS rx_mbytes,
ROUND(tx_mbps / 8, 4) AS tx_mbytes,
rx_pps,
tx_pps
tx_pps,
rx_errors_per_sec,
tx_errors_per_sec,
rx_dropped_per_sec,
tx_dropped_per_sec
FROM network_usage_logs
WHERE rx_mbps IS NOT NULL
AND tx_mbps IS NOT NULL
+124
View File
@@ -167,10 +167,14 @@
wifi5History: '5G WiFi History',
downloadRxPackets: 'Download / RX Packets',
uploadTxPackets: 'Upload / TX Packets',
rxErrorsDrops: 'RX Errors / Drops',
txErrorsDrops: 'TX Errors / Drops',
downloadLine: 'Download',
uploadLine: 'Upload',
rxPacketsLine: 'RX Packets',
txPacketsLine: 'TX Packets',
errorsLine: 'Errors',
dropsLine: 'Drops',
remaining: 'Remaining',
batteryVoltage: 'Battery Voltage',
wifiClients: 'DHCP Clients',
@@ -373,10 +377,14 @@
wifi5History: '5G WiFi 이력',
downloadRxPackets: '다운로드 / RX 패킷',
uploadTxPackets: '업로드 / TX 패킷',
rxErrorsDrops: 'RX 오류 / 드롭',
txErrorsDrops: 'TX 오류 / 드롭',
downloadLine: '다운로드',
uploadLine: '업로드',
rxPacketsLine: 'RX 패킷',
txPacketsLine: 'TX 패킷',
errorsLine: '오류',
dropsLine: '드롭',
remaining: '잔여 시간',
batteryVoltage: '배터리 전압',
wifiClients: 'DHCP 클라이언트',
@@ -2626,6 +2634,114 @@
c.update('none');
}
function networkQualityChart(canvasId, rows, errorKey, dropKey) {
const canvas = $('#' + canvasId);
if (!canvas || typeof Chart === 'undefined') return;
const { labels, xTickCallback, xGridColor } = timeAxis(rows);
const errorValues = rows.map(row => {
const value = Number(row[errorKey]);
return Number.isFinite(value) ? value : null;
});
const dropValues = rows.map(row => {
const value = Number(row[dropKey]);
return Number.isFinite(value) ? value : null;
});
const scale = dynamicScaleForValues([...errorValues, ...dropValues], { minSpan: 1, padding: 0.2 });
const datasets = [
{
label: t('errorsLine'),
data: errorValues,
borderColor: '#ef4444',
backgroundColor: 'transparent',
borderWidth: 2.1,
pointRadius: 0,
tension: 0.2,
fill: false,
},
{
label: t('dropsLine'),
data: dropValues,
borderColor: '#f59e0b',
backgroundColor: 'transparent',
borderWidth: 2,
borderDash: [5, 4],
pointRadius: 0,
tension: 0.2,
fill: false,
},
];
const options = {
responsive: true,
maintainAspectRatio: false,
animation: false,
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
legend: {
display: true,
position: 'bottom',
labels: {
boxWidth: 18,
boxHeight: 2,
color: '#8d98aa',
padding: 10,
},
},
tooltip: {
displayColors: true,
padding: 8,
callbacks: {
label: item => `${item.dataset.label}: ${formatChartNumber(item.parsed.y, '/s')}`,
},
},
},
scales: {
x: {
ticks: {
color: '#64748b',
maxRotation: 0,
autoSkip: false,
callback: xTickCallback,
},
grid: {
color: xGridColor,
drawTicks: false,
},
border: { display: false },
},
y: {
min: scale.min,
max: scale.max,
ticks: {
color: '#8d98aa',
maxTicksLimit: 4,
callback: value => formatChartNumber(value, '/s'),
},
grid: { color: 'rgba(255,255,255,.045)', drawTicks: false },
border: { display: false },
},
},
};
if (!state.charts[canvasId]) {
state.charts[canvasId] = new Chart(canvas, {
type: 'line',
data: { labels, datasets },
options,
});
return;
}
const c = state.charts[canvasId];
c.data.labels = labels;
c.data.datasets = datasets;
c.options = options;
c.update('none');
}
function dynamicScaleForValues(values, options = {}) {
const floor = Number(options.floor ?? 0);
const ceiling = Number.isFinite(Number(options.ceiling)) ? Number(options.ceiling) : null;
@@ -2690,12 +2806,20 @@
const wifi5Rows = networkRowsForIface(networkRows, 'wlan1');
networkChart('netWanRxChart', wanRows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
networkChart('netWanTxChart', wanRows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
networkQualityChart('netWanRxQualityChart', wanRows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
networkQualityChart('netWanTxQualityChart', wanRows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
networkChart('netLanRxChart', lanRows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
networkChart('netLanTxChart', lanRows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
networkQualityChart('netLanRxQualityChart', lanRows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
networkQualityChart('netLanTxQualityChart', lanRows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
networkChart('netWifi24RxChart', wifi24Rows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
networkChart('netWifi24TxChart', wifi24Rows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
networkQualityChart('netWifi24RxQualityChart', wifi24Rows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
networkQualityChart('netWifi24TxQualityChart', wifi24Rows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
networkChart('netWifi5RxChart', wifi5Rows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
networkChart('netWifi5TxChart', wifi5Rows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
networkQualityChart('netWifi5RxQualityChart', wifi5Rows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
networkQualityChart('netWifi5TxQualityChart', wifi5Rows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
}
function resizeChartsSoon() {
+8
View File
@@ -289,6 +289,8 @@ html[data-theme="light"] .details-panel{background:#edf3fa;border-color:#b8c7da}
<div class="chart-grid">
<div class="chart-box wide"><h3 data-i18n="downloadRxPackets">Download / RX Packets</h3><div class="chart-canvas"><canvas id="netWanRxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="uploadTxPackets">Upload / TX Packets</h3><div class="chart-canvas"><canvas id="netWanTxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="rxErrorsDrops">RX Errors / Drops</h3><div class="chart-canvas"><canvas id="netWanRxQualityChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="txErrorsDrops">TX Errors / Drops</h3><div class="chart-canvas"><canvas id="netWanTxQualityChart"></canvas></div></div>
</div>
</div>
@@ -297,6 +299,8 @@ html[data-theme="light"] .details-panel{background:#edf3fa;border-color:#b8c7da}
<div class="chart-grid">
<div class="chart-box wide"><h3 data-i18n="downloadRxPackets">Download / RX Packets</h3><div class="chart-canvas"><canvas id="netLanRxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="uploadTxPackets">Upload / TX Packets</h3><div class="chart-canvas"><canvas id="netLanTxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="rxErrorsDrops">RX Errors / Drops</h3><div class="chart-canvas"><canvas id="netLanRxQualityChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="txErrorsDrops">TX Errors / Drops</h3><div class="chart-canvas"><canvas id="netLanTxQualityChart"></canvas></div></div>
</div>
</div>
@@ -305,6 +309,8 @@ html[data-theme="light"] .details-panel{background:#edf3fa;border-color:#b8c7da}
<div class="chart-grid">
<div class="chart-box wide"><h3 data-i18n="downloadRxPackets">Download / RX Packets</h3><div class="chart-canvas"><canvas id="netWifi24RxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="uploadTxPackets">Upload / TX Packets</h3><div class="chart-canvas"><canvas id="netWifi24TxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="rxErrorsDrops">RX Errors / Drops</h3><div class="chart-canvas"><canvas id="netWifi24RxQualityChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="txErrorsDrops">TX Errors / Drops</h3><div class="chart-canvas"><canvas id="netWifi24TxQualityChart"></canvas></div></div>
</div>
</div>
@@ -313,6 +319,8 @@ html[data-theme="light"] .details-panel{background:#edf3fa;border-color:#b8c7da}
<div class="chart-grid">
<div class="chart-box wide"><h3 data-i18n="downloadRxPackets">Download / RX Packets</h3><div class="chart-canvas"><canvas id="netWifi5RxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="uploadTxPackets">Upload / TX Packets</h3><div class="chart-canvas"><canvas id="netWifi5TxChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="rxErrorsDrops">RX Errors / Drops</h3><div class="chart-canvas"><canvas id="netWifi5RxQualityChart"></canvas></div></div>
<div class="chart-box wide"><h3 data-i18n="txErrorsDrops">TX Errors / Drops</h3><div class="chart-canvas"><canvas id="netWifi5TxQualityChart"></canvas></div></div>
</div>
</div>