fix: исправить все проблемы с функциональностью интерфейса
- Исправлено обновление логов в реальном времени при изменении уровней логирования - Добавлена функция refreshAllLogs для фильтрации логов - Исправлены кнопки Options (autoscroll, wrap, pause) - Исправлены фильтры с обновлением в реальном времени - Исправлены кнопки Refresh и Clear - Исправлен Snapshot - теперь сохраняет актуальные логи - Добавлены обработчики для всех элементов управления - Улучшена синхронизация между legacy и современным интерфейсом - Добавлено логирование для отладки Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
parent
c647c2eb71
commit
7c7c92f798
@ -627,6 +627,39 @@ const els = {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
function setWsState(s){ els.wsstate.textContent = 'ws: ' + s; }
|
function setWsState(s){ els.wsstate.textContent = 'ws: ' + s; }
|
||||||
|
|
||||||
|
// Функция для обновления всех логов при изменении фильтров
|
||||||
|
function refreshAllLogs() {
|
||||||
|
Object.keys(state.open).forEach(id => {
|
||||||
|
const obj = state.open[id];
|
||||||
|
if (!obj || !obj.logEl) return;
|
||||||
|
|
||||||
|
// Получаем все строки логов
|
||||||
|
const lines = obj.logEl.innerHTML.split('\n');
|
||||||
|
const filteredLines = [];
|
||||||
|
|
||||||
|
lines.forEach(line => {
|
||||||
|
if (line.trim() === '') return;
|
||||||
|
|
||||||
|
// Проверяем уровень логирования
|
||||||
|
const cls = classify(line);
|
||||||
|
if (!allowedByLevel(cls)) return;
|
||||||
|
|
||||||
|
// Проверяем фильтр
|
||||||
|
if (!applyFilter(line)) return;
|
||||||
|
|
||||||
|
filteredLines.push(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обновляем отображение
|
||||||
|
obj.logEl.innerHTML = filteredLines.join('\n');
|
||||||
|
|
||||||
|
// Обновляем современный интерфейс
|
||||||
|
if (state.current && state.current.id === id && els.logContent) {
|
||||||
|
els.logContent.innerHTML = obj.logEl.innerHTML;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
function escapeHtml(s){ return s.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
|
function escapeHtml(s){ return s.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
|
||||||
|
|
||||||
function classify(line){
|
function classify(line){
|
||||||
@ -889,10 +922,29 @@ function closeWs(id){
|
|||||||
async function sendSnapshot(id){
|
async function sendSnapshot(id){
|
||||||
const o = state.open[id];
|
const o = state.open[id];
|
||||||
if (!o){ alert('not open'); return; }
|
if (!o){ alert('not open'); return; }
|
||||||
const text = o.logEl.textContent;
|
|
||||||
|
// Получаем текст логов из современного интерфейса или из legacy
|
||||||
|
let text = '';
|
||||||
|
if (state.current && state.current.id === id && els.logContent) {
|
||||||
|
text = els.logContent.textContent;
|
||||||
|
} else if (o.logEl) {
|
||||||
|
text = o.logEl.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!text || text.trim() === '') {
|
||||||
|
alert('No logs to save');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Saving snapshot with content length:', text.length);
|
||||||
|
|
||||||
const payload = {container_id: id, service: o.serviceName || id, content: text};
|
const payload = {container_id: id, service: o.serviceName || id, content: text};
|
||||||
const res = await fetch('/api/snapshot', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(payload)});
|
const res = await fetch('/api/snapshot', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(payload)});
|
||||||
if (!res.ok){ alert('snapshot failed'); return; }
|
if (!res.ok){
|
||||||
|
console.error('Snapshot failed:', res.status, res.statusText);
|
||||||
|
alert('snapshot failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const js = await res.json();
|
const js = await res.json();
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = js.url; a.download = js.file; a.click();
|
a.href = js.url; a.download = js.file; a.click();
|
||||||
@ -1133,8 +1185,24 @@ if (els.groupBtn && els.groupBtn.onclick !== null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
els.clearBtn.onclick = ()=> Object.values(state.open).forEach(o=> o.logEl.textContent='');
|
els.clearBtn.onclick = ()=> {
|
||||||
els.refreshBtn.onclick = fetchServices;
|
Object.values(state.open).forEach(o => {
|
||||||
|
if (o.logEl) o.logEl.textContent = '';
|
||||||
|
});
|
||||||
|
// Очищаем современный интерфейс
|
||||||
|
if (els.logContent) {
|
||||||
|
els.logContent.textContent = '';
|
||||||
|
}
|
||||||
|
// Сбрасываем счетчики
|
||||||
|
document.querySelectorAll('.cdbg, .cinfo, .cwarn, .cerr').forEach(el => {
|
||||||
|
el.textContent = '0';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
els.refreshBtn.onclick = async () => {
|
||||||
|
console.log('Refreshing services...');
|
||||||
|
await fetchServices();
|
||||||
|
};
|
||||||
els.projectSelect.onchange = fetchServices;
|
els.projectSelect.onchange = fetchServices;
|
||||||
|
|
||||||
// Mobile menu toggle
|
// Mobile menu toggle
|
||||||
@ -1157,16 +1225,83 @@ els.tail.onchange = ()=> {
|
|||||||
state.open[id].logEl.textContent='';
|
state.open[id].logEl.textContent='';
|
||||||
closeWs(id); openWs(svc, panel);
|
closeWs(id); openWs(svc, panel);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Обновляем современный интерфейс
|
||||||
|
if (state.current && els.logContent) {
|
||||||
|
els.logContent.textContent = 'Reconnecting...';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
els.wrapToggle.onchange = ()=> {
|
els.wrapToggle.onchange = ()=> {
|
||||||
document.querySelectorAll('.log').forEach(el=> el.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre');
|
document.querySelectorAll('.log').forEach(el=> el.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre');
|
||||||
|
if (els.logContent) {
|
||||||
els.logContent.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre';
|
els.logContent.style.whiteSpace = els.wrapToggle.checked ? 'pre-wrap' : 'pre';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Добавляем обработчики для autoscroll и pause
|
||||||
|
els.autoscroll.onchange = ()=> {
|
||||||
|
// Обновляем настройку автопрокрутки для всех открытых логов
|
||||||
|
Object.keys(state.open).forEach(id => {
|
||||||
|
const obj = state.open[id];
|
||||||
|
if (obj && obj.wrapEl) {
|
||||||
|
if (els.autoscroll.checked) {
|
||||||
|
obj.wrapEl.scrollTop = obj.wrapEl.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обновляем современный интерфейс
|
||||||
|
if (state.current && els.logContent) {
|
||||||
|
const logContent = document.querySelector('.log-content');
|
||||||
|
if (logContent && els.autoscroll.checked) {
|
||||||
|
logContent.scrollTop = logContent.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
els.pause.onchange = ()=> {
|
||||||
|
// При снятии паузы показываем накопленные логи
|
||||||
|
if (!els.pause.checked) {
|
||||||
|
Object.keys(state.open).forEach(id => {
|
||||||
|
const obj = state.open[id];
|
||||||
|
if (obj && obj.pausedBuffer && obj.pausedBuffer.length > 0) {
|
||||||
|
obj.pausedBuffer.forEach(html => {
|
||||||
|
obj.logEl.insertAdjacentHTML('beforeend', html);
|
||||||
|
});
|
||||||
|
obj.pausedBuffer = [];
|
||||||
|
|
||||||
|
// Обновляем современный интерфейс
|
||||||
|
if (state.current && state.current.id === id && els.logContent) {
|
||||||
|
els.logContent.innerHTML = obj.logEl.innerHTML;
|
||||||
|
const logContent = document.querySelector('.log-content');
|
||||||
|
if (logContent && els.autoscroll.checked) {
|
||||||
|
logContent.scrollTop = logContent.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
els.filter.oninput = ()=> {
|
||||||
|
state.filter = els.filter.value.trim();
|
||||||
|
refreshAllLogs();
|
||||||
|
};
|
||||||
|
els.lvlDebug.onchange = ()=> {
|
||||||
|
state.levels.debug = els.lvlDebug.checked;
|
||||||
|
refreshAllLogs();
|
||||||
|
};
|
||||||
|
els.lvlInfo.onchange = ()=> {
|
||||||
|
state.levels.info = els.lvlInfo.checked;
|
||||||
|
refreshAllLogs();
|
||||||
|
};
|
||||||
|
els.lvlWarn.onchange = ()=> {
|
||||||
|
state.levels.warn = els.lvlWarn.checked;
|
||||||
|
refreshAllLogs();
|
||||||
|
};
|
||||||
|
els.lvlErr.onchange = ()=> {
|
||||||
|
state.levels.err = els.lvlErr.checked;
|
||||||
|
refreshAllLogs();
|
||||||
};
|
};
|
||||||
els.filter.oninput = ()=> { state.filter = els.filter.value.trim(); };
|
|
||||||
els.lvlDebug.onchange = ()=> state.levels.debug = els.lvlDebug.checked;
|
|
||||||
els.lvlInfo.onchange = ()=> state.levels.info = els.lvlInfo.checked;
|
|
||||||
els.lvlWarn.onchange = ()=> state.levels.warn = els.lvlWarn.checked;
|
|
||||||
els.lvlErr.onchange = ()=> state.levels.err = els.lvlErr.checked;
|
|
||||||
|
|
||||||
// Hotkeys: [ ] — tabs, M — multi
|
// Hotkeys: [ ] — tabs, M — multi
|
||||||
window.addEventListener('keydown', (e)=>{
|
window.addEventListener('keydown', (e)=>{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user