mirror of
https://github.com/vladmandic/automatic
synced 2026-08-26 06:30:44 +02:00
8ce3313f9a
Signed-off-by: Vladimir Mandic <mandic00@live.com>
209 lines
8.2 KiB
TypeScript
209 lines
8.2 KiB
TypeScript
import { authFetch } from './authWrap';
|
|
import { log, error, xhrPost } from './logger';
|
|
import { timer } from './timers';
|
|
|
|
interface LogLine {
|
|
created: number;
|
|
level: string;
|
|
module: string;
|
|
facility: string;
|
|
msg: string;
|
|
}
|
|
|
|
let logMonitorEl: HTMLElement | null = null;
|
|
let logMonitorStatus = true;
|
|
let logWarnings = 0;
|
|
let logErrors = 0;
|
|
let logConnected = false;
|
|
|
|
function dateToStr(ts: number): string {
|
|
const dt = new Date(1000 * ts);
|
|
// const year = dt.getFullYear();
|
|
// const mo = String(dt.getMonth() + 1).padStart(2, '0');
|
|
// const day = String(dt.getDate()).padStart(2, '0');
|
|
const hour = String(dt.getHours()).padStart(2, '0');
|
|
const min = String(dt.getMinutes()).padStart(2, '0');
|
|
const sec = String(dt.getSeconds()).padStart(2, '0');
|
|
const ms = String(dt.getMilliseconds()).padStart(3, '0');
|
|
// const s = `${year}-${mo}-${day} ${hour}:${min}:${sec}.${ms}`;
|
|
const s = `${hour}:${min}:${sec}.${ms}`;
|
|
return s;
|
|
}
|
|
|
|
function htmlEscape(text: string): string {
|
|
return text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
}
|
|
|
|
function parseLogLine(line: string): LogLine {
|
|
let str = line.replaceAll('\n', ' ').replaceAll('\\', '\\\\');
|
|
const tracebackIndex = str.indexOf('Traceback');
|
|
if (tracebackIndex !== -1) str = str.substring(0, tracebackIndex);
|
|
const parsed = JSON.parse(str) as Partial<LogLine>;
|
|
return {
|
|
created: Number(parsed.created ?? Date.now()),
|
|
level: String(parsed.level ?? 'INFO'),
|
|
module: String(parsed.module ?? 'logMonitor'),
|
|
facility: String(parsed.facility ?? 'ui'),
|
|
msg: String(parsed.msg ?? ''),
|
|
};
|
|
}
|
|
|
|
async function clearErrors(): Promise<void> {
|
|
logWarnings = 0;
|
|
logErrors = 0;
|
|
log('clearErrors');
|
|
}
|
|
|
|
export async function initClearErrorsButton() {
|
|
const btnServerClear = document.getElementById('btn_console_log_server_clear');
|
|
if (btnServerClear) {
|
|
btnServerClear.onclick = async (evt) => {
|
|
evt.preventDefault();
|
|
evt.stopPropagation();
|
|
clearErrors();
|
|
};
|
|
}
|
|
}
|
|
|
|
async function logMonitor() {
|
|
const addLogLine = (line: string): void => {
|
|
if (!logMonitorEl) logMonitorEl = document.getElementById('logMonitorData');
|
|
if (!logMonitorEl) return;
|
|
try {
|
|
const l = parseLogLine(line);
|
|
const row = document.createElement('tr');
|
|
// row.style = 'padding: 10px; margin: 0;';
|
|
const level = `<td style="color: var(--color-${l.level.toLowerCase()})">${l.level}</td>`;
|
|
if (l.level === 'WARNING') logWarnings++;
|
|
if (l.level === 'ERROR') logErrors++;
|
|
const module = `<td style="color: #ffca68">${l.module}</td>`;
|
|
const facilityText = l.facility.length > 20 ? `${l.facility.substring(0, 20)}...` : l.facility;
|
|
const facility = l.facility !== 'sd' ? `<td style="color: #ffca68">${facilityText}</td>` : '<td></td>';
|
|
row.innerHTML = `<td>${dateToStr(l.created)}</td>${level}${facility}${module}<td>${htmlEscape(l.msg)}</td>`;
|
|
logMonitorEl.appendChild(row);
|
|
} catch (err) {
|
|
error('logMonitor', { error: String(err), line });
|
|
}
|
|
};
|
|
|
|
const cleanupLog = (atBottom: boolean): void => {
|
|
if (!logMonitorEl) return;
|
|
while (logMonitorEl.childElementCount > 100 && logMonitorEl.firstElementChild) {
|
|
logMonitorEl.removeChild(logMonitorEl.firstElementChild);
|
|
}
|
|
if (atBottom) logMonitorEl.scrollTop = logMonitorEl.scrollHeight;
|
|
else if (logMonitorEl.parentElement) logMonitorEl.parentElement.style.cssText = 'border-bottom: 2px solid var(--highlight-color);';
|
|
const elWarn = document.getElementById('logWarnings');
|
|
const elErr = document.getElementById('logErrors');
|
|
const modenUIBtn = document.getElementById('btn_console');
|
|
if (elWarn) elWarn.innerText = String(logWarnings);
|
|
if (elErr) elErr.innerText = String(logErrors);
|
|
if (modenUIBtn) {
|
|
modenUIBtn.setAttribute('error-count', logErrors > 0 ? String(logErrors) : '');
|
|
modenUIBtn.style.backgroundColor = logErrors > 0 ? 'var(--color-error)' : '';
|
|
modenUIBtn.title = `Log\nErrors ${logErrors}\nWarnings ${logWarnings}`;
|
|
}
|
|
};
|
|
|
|
const txtGallery = document.getElementById('txt2img_gallery');
|
|
if (txtGallery) txtGallery.style.height = window.opts.logmonitor_show ? '50vh' : '55vh';
|
|
const imgGallery = document.getElementById('img2img_gallery');
|
|
if (imgGallery) imgGallery.style.height = window.opts.logmonitor_show ? '50vh' : '55vh';
|
|
|
|
if (!window.opts.logmonitor_show) {
|
|
Array.from<any>(document.getElementsByClassName('log-monitor')).forEach((el) => {
|
|
if (el instanceof HTMLElement) el.style.display = 'none';
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (logMonitorStatus) setTimeout(logMonitor, window.opts.logmonitor_refresh_period);
|
|
else setTimeout(logMonitor, 10 * 1000); // on failure try to reconnect every 10sec
|
|
|
|
logMonitorStatus = false;
|
|
if (!logMonitorEl) {
|
|
logMonitorEl = document.getElementById('logMonitorData');
|
|
if (logMonitorEl) {
|
|
logMonitorEl.addEventListener('scroll', () => {
|
|
const atBottom = logMonitorEl.scrollHeight <= (logMonitorEl.scrollTop + logMonitorEl.clientHeight);
|
|
if (atBottom && logMonitorEl.parentElement) logMonitorEl.parentElement.style.cssText = '';
|
|
});
|
|
}
|
|
}
|
|
if (!logMonitorEl) return;
|
|
const atBottom = logMonitorEl.scrollHeight <= (logMonitorEl.scrollTop + logMonitorEl.clientHeight);
|
|
|
|
try {
|
|
const res = await authFetch(`${window.api}/log?clear=True`);
|
|
if (res?.ok) {
|
|
logMonitorStatus = true;
|
|
const lines = (await res.json()) as string[];
|
|
if (logMonitorEl && lines?.length > 0 && logMonitorEl.parentElement?.parentElement instanceof HTMLElement) {
|
|
logMonitorEl.parentElement.parentElement.style.display = window.opts.logmonitor_show ? 'block' : 'none';
|
|
}
|
|
if (!logConnected) {
|
|
logConnected = true;
|
|
xhrPost(`${window.api}/log`, { debug: 'connected' });
|
|
logErrors = 0; // reset error count on reconnect
|
|
}
|
|
for (const line of lines) addLogLine(line);
|
|
} else {
|
|
logConnected = false;
|
|
logErrors++;
|
|
if (res) addLogLine(`{ "created": ${Date.now()}, "level":"ERROR", "module":"logMonitor", "facility":"ui", "msg":"Failed to fetch log: ${res?.status} ${res?.statusText}" }`);
|
|
else addLogLine(`{ "created": ${Date.now()}, "level":"ERROR", "module":"logMonitor", "facility":"ui", "msg":"Server unreachable" }`);
|
|
}
|
|
cleanupLog(atBottom);
|
|
} catch {
|
|
logConnected = false;
|
|
logErrors++;
|
|
addLogLine(`{ "created": ${Date.now()}, "level":"ERROR", "module":"logMonitor", "facility":"ui", "msg":"Server unreachable" }`);
|
|
cleanupLog(atBottom);
|
|
}
|
|
}
|
|
|
|
export async function initLogMonitor() {
|
|
let el = document.getElementById('logMonitorPlaceholder');
|
|
const modernUi = Boolean(el);
|
|
if (!el) el = document.getElementsByTagName('footer')[0];
|
|
if (!el) return;
|
|
const t0 = performance.now();
|
|
el.classList.add('log-monitor');
|
|
const uiDisabled = Array.isArray(window.opts.ui_disabled) ? window.opts.ui_disabled : [];
|
|
if (uiDisabled.includes('logs')) return;
|
|
if (modernUi) {
|
|
el.style.overflow = 'auto';
|
|
el.innerHTML = `
|
|
<table id="logMonitor" style="width: 100%;">
|
|
<tbody id="logMonitorData" style="white-space: nowrap; display: block">
|
|
</tbody>
|
|
</table>
|
|
`;
|
|
} else {
|
|
el.innerHTML = `
|
|
<table id="logMonitor" style="width: 100%;">
|
|
<thead style="display: block; text-align: left; border-bottom: solid 1px var(--button-primary-border-color)">
|
|
<tr>
|
|
<th style="width: 144px">Time</th>
|
|
<th>Level</th>
|
|
<th style="width: 0"></th>
|
|
<th style="width: 154px">Module</th>
|
|
<th>Message</th>
|
|
<th style="position: absolute; right: 7em">Warnings <span id="logWarnings">0</span></th>
|
|
<th style="position: absolute; right: 1em">Errors <span id="logErrors">0</span></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="logMonitorData" style="white-space: nowrap; display: block; color: var(--neutral-400)">
|
|
</tbody>
|
|
</table>
|
|
`;
|
|
}
|
|
el.style.display = 'none';
|
|
authFetch(`${window.api}/start?agent=${encodeURI(navigator.userAgent)}`);
|
|
logMonitor();
|
|
initClearErrorsButton();
|
|
const t1 = performance.now();
|
|
log('initLogMonitor', { show: window.opts.logmonitor_show, time: Math.round(t1 - t0) });
|
|
timer('initLogMonitor', t1 - t0);
|
|
}
|