원격 로그 상태 진단 추가

This commit is contained in:
seo
2026-07-25 19:08:05 +09:00
parent c2099c21f0
commit 10c00b3e57
4 changed files with 292 additions and 2 deletions
+138
View File
@@ -37,6 +37,7 @@
statusBatteryRemaining: $('#statusBatteryRemaining'),
batteryRuntimeTooltip: $('#batteryRuntimeTooltip'),
spikeLogList: $('#spikeLogList'),
remoteLogList: $('#remoteLogList'),
customServiceList: $('#customServiceList'),
noticeBaseline: $('#noticeBaseline'),
rebootBtn: $('#rebootBtn'),
@@ -179,6 +180,24 @@
noNoticeHistory: 'No system notice history.',
customServices: 'Custom Services',
noCustomServices: 'No custom services.',
remoteLogs: 'Remote Logs',
remoteLogsLoading: 'Remote log status loading...',
remotePiToSynology: 'Raspberry -> Synology',
remoteSynologyToPi: 'Synology -> Raspberry',
remotePersistentJournal: 'Persistent Journal',
remoteCrashLogs: 'Crash Logs',
remoteOk: 'OK',
remoteWarn: 'Check',
remoteTarget: 'Target',
remoteTransport: 'Transport',
remoteQueue: 'Queue',
remoteListener: 'Listener',
remoteLatest: 'Latest',
remoteFiles: 'Files',
remoteUsage: 'Usage',
remoteBoots: 'Boots',
remoteSaved: 'Saved',
remotePending: 'Pending',
serviceLogs: 'Logs',
serviceEnabled: 'enabled',
servicePid: 'pid',
@@ -353,6 +372,24 @@
noNoticeHistory: '시스템 알림 이력이 없습니다.',
customServices: '사용자 서비스',
noCustomServices: '사용자 생성 서비스가 없습니다.',
remoteLogs: '원격 로그',
remoteLogsLoading: '원격 로그 상태 확인 중...',
remotePiToSynology: '라즈베리파이 -> 시놀로지',
remoteSynologyToPi: '시놀로지 -> 라즈베리파이',
remotePersistentJournal: '영구 저널',
remoteCrashLogs: '커널 크래시',
remoteOk: '정상',
remoteWarn: '확인',
remoteTarget: '대상',
remoteTransport: '전송',
remoteQueue: '대기열',
remoteListener: '수신',
remoteLatest: '최근',
remoteFiles: '파일',
remoteUsage: '사용량',
remoteBoots: '부팅 이력',
remoteSaved: '저장',
remotePending: '대기',
serviceLogs: '로그',
serviceEnabled: '활성화',
servicePid: 'PID',
@@ -1957,6 +1994,106 @@
: `<div class="spike-log-empty">${escapeHtml(t('noNoticeHistory'))}</div>`;
}
function remoteState(ok, warn = false) {
if (ok) {
return {
className: 'active',
label: t('remoteOk'),
};
}
return {
className: warn ? 'warning' : 'failed',
label: t('remoteWarn'),
};
}
function remoteMeta(items) {
return items
.filter(item => item.value !== null && item.value !== undefined && item.value !== '')
.map(item => `${item.label}: ${item.value}`)
.join(' · ') || '-';
}
function remoteAge(row) {
if (!row || row.age_seconds === null || row.age_seconds === undefined) {
return '-';
}
const formatted = formatDhms(Number(row.age_seconds));
return formatted ? `${formatted} ${t('ago')}` : '-';
}
function renderRemoteLogs(data = {}) {
if (!els.remoteLogList) return;
const piToSynology = data.raspberry_to_synology || {};
const synologyToPi = data.synology_to_raspberry || {};
const journal = data.journal || {};
const pstore = data.pstore || {};
const newest = synologyToPi.logs?.newest || null;
const queue = piToSynology.queue || {};
const rows = [
{
title: t('remotePiToSynology'),
desc: remoteMeta([
{ label: t('remoteTarget'), value: piToSynology.target || '-' },
{ label: t('remoteTransport'), value: piToSynology.transport || '-' },
]),
meta: remoteMeta([
{ label: 'rsyslog', value: `${piToSynology.rsyslog_active || '-'} / ${piToSynology.rsyslog_enabled || '-'}` },
{ label: t('remoteQueue'), value: queue.available === false ? '-' : `${Number(queue.file_count || 0).toLocaleString()} / ${queue.total_size || '-'}` },
]),
state: remoteState(Boolean(piToSynology.ok), true),
},
{
title: t('remoteSynologyToPi'),
desc: remoteMeta([
{ label: t('remoteListener'), value: synologyToPi.listener ? `:${synologyToPi.listen_port || 6514}` : '-' },
{ label: t('remoteTransport'), value: synologyToPi.transport || '-' },
]),
meta: remoteMeta([
{ label: t('remoteLatest'), value: newest ? `${newest.name || '-'} / ${remoteAge(newest)}` : '-' },
{ label: t('remoteFiles'), value: `${Number(synologyToPi.logs?.file_count || 0).toLocaleString()} / ${synologyToPi.logs?.total_size || '-'}` },
]),
state: remoteState(Boolean(synologyToPi.ok), true),
},
{
title: t('remotePersistentJournal'),
desc: journal.path || '-',
meta: remoteMeta([
{ label: t('remoteUsage'), value: journal.disk_usage || '-' },
{ label: t('remoteBoots'), value: journal.boot_count === null || journal.boot_count === undefined ? '-' : Number(journal.boot_count).toLocaleString() },
]),
state: remoteState(Boolean(journal.ok), true),
},
{
title: t('remoteCrashLogs'),
desc: pstore.saved_path || '-',
meta: remoteMeta([
{ label: 'pstore', value: pstore.mounted ? 'mounted' : '-' },
{ label: 'collector', value: pstore.collector_active || '-' },
{ label: t('remoteSaved'), value: Number(pstore.saved_files || 0).toLocaleString() },
{ label: t('remotePending'), value: Number(pstore.pending_files || 0).toLocaleString() },
]),
state: remoteState(Boolean(pstore.ok), true),
},
];
els.remoteLogList.innerHTML = rows.map(row => `
<div class="service-item">
<div class="service-head">
<div class="service-title">
<div class="service-name">${escapeHtml(row.title)}</div>
<div class="service-desc">${escapeHtml(row.desc)}</div>
</div>
<div class="service-state ${row.state.className}">${escapeHtml(row.state.label)}</div>
</div>
<div class="service-meta">${escapeHtml(row.meta)}</div>
</div>
`).join('');
}
function renderCustomServices(rows = []) {
if (!els.customServiceList) return;
if (Date.now() < state.customServiceScrollHoldUntil) return;
@@ -2011,6 +2148,7 @@
? `${baselineTemp.toFixed(1)}°C / ${Math.round(baselineRpm).toLocaleString()} RPM · ${stateText}`
: stateText;
renderSpikeHistory(data.fan_spike_history || []);
renderRemoteLogs(data.remote_logs || {});
renderCustomServices(data.custom_services || []);
setText(els.noticeBaseline, baselineText);