/
firstAndres
/
LogView.js
Обзор
Документация
Войти
/
firstAndres
/
LogView.js
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
index.html
310 строк
11 KB
firstAndres
update index.html
20 янв 2025, 06:51
20 янв 2025, 06:51
9af06a0
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="en"> <head> <script> // Configuration const URL_TO_LOG_FILE = '/LogView.js/log.txt'; const AUTO_REFRESH_ENABLED_DEFAULT = false; // Change to true to enable auto-refresh by default const IS_REVERSE_LOGS = false; </script> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Log Viewer</title> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> <style> body { font-family: Arial, sans-serif; background-color: #f8f9fa; padding: 20px; padding-bottom: 100px; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; } textarea { width: 100%; height: 150px; margin-bottom: 10px; font-family: monospace; } .controls { margin-bottom: 20px; } .controls { background: #e6e6e6; padding: 11px 16px 7px 16px; border-radius: 4px; } .log-entry { border-bottom: 1px solid #ddd; padding: 10px 16px; white-space: pre-wrap; background-color: #fff; border-radius: 5px; display: flex; } .log-level { font-weight: bold; margin-right: 5px; } .DEBUG { color: #607d8b; } /* Цвет для Debug из Material (blue-grey) */ .INFO { color: #17a2b8; } /* Голубой для информационных сообщений */ .WARNING { color: #ff9800; } /* Цвет для Warning из Material (amber) */ .ERROR { color: #f44336; } /* Цвет для Error из Material (red) */ .json_in_log { background-color: #f4f4f4; border: 1px solid #ddd; border-radius: 5px; padding: 5px; font-family: monospace; display: inline-block; white-space: pre-wrap; cursor: pointer; } .json_beautified { background-color: #e8f7e8; display: block; } .checkbox-group { display: flex; gap: 10px; } .log-level { min-width: 110px; } .log-entry:hover { background: #f6fdf6; } .btn.btn-light { border: 1px solid silver; } .btns_box.reverse { position: fixed; bottom: 0; left: 0; width: 100%; background: #fff; padding: 16px 16px; border-top: 1px solid silver; } </style> </head> <body> <div class="container"> <h1 class="mb-4">Log Viewer</h1> <div class="mb-4"> <textarea id="log-input" class="form-control" placeholder="Paste your logs here"></textarea> </div> <div class="controls mb-4"> <div class="mb-3"> <label for="filter-substring" class="form-label">Filter by substring:</label> <input type="text" id="filter-substring" class="form-control" placeholder="Enter substring"> </div> <div class="mb-3"> <label for="filter-levels" class="form-label">Filter by levels:</label> <div class="checkbox-group" id="filter-levels"> <label><input type="checkbox" value="DEBUG" checked0> DEBUG</label> <label><input type="checkbox" value="INFO" checked> INFO</label> <label><input type="checkbox" value="WARNING" checked> WARNING</label> <label><input type="checkbox" value="ERROR" checked> ERROR</label> </div> </div> <div class="btns_box reverse"> <button id="toggle-autorefresh" class="btn btn-light">Play Auto-Refresh</button> <button id="" class="btn btn-light js_refresh_from_file">Refresh from file</button> </div> </div> <div id="log-output" class="mt-4"></div> </div> <script> const logInput = document.getElementById('log-input'); const filterSubstring = document.getElementById('filter-substring'); const filterLevels = document.getElementById('filter-levels'); const logOutput = document.getElementById('log-output'); const toggleAutoRefreshButton = document.getElementById('toggle-autorefresh'); const refreshButton = document.querySelector('.js_refresh_from_file'); let autoRefresh = true; let autoRefreshInterval; // Default log file path var rand = Math.random(); const baseUrl = URL_TO_LOG_FILE; // Проверяем, есть ли уже параметры в URL const separator = baseUrl.includes('?') ? '&' : '?'; // Формируем финальный URL const defaultLogPath = baseUrl + separator + 'random=' + rand; // Function to fetch logs from a file async function loadDefaultLogs() { try { var rand = Math.random(); const response = await fetch(defaultLogPath + rand); if (!response.ok) throw new Error('Failed to load default logs'); const text = await response.text(); logInput.value = text; applyFilters(); // Automatically apply filters after loading } catch (error) { console.error(error); logInput.value = 'Failed to load default logs.'; } } // Function to parse and render logs function renderLogs(logs) { logOutput.innerHTML = ''; if (IS_REVERSE_LOGS) { logs.reverse(); } logs.forEach(log => { const logDiv = document.createElement('div'); logDiv.className = 'log-entry'; const levelSpan = document.createElement('span'); levelSpan.className = `log-level ${log.level}`; levelSpan.textContent = `[${log.level}]`; const messageSpan = document.createElement('span'); messageSpan.innerHTML = highlightJsonInText(log.message); logDiv.appendChild(levelSpan); logDiv.appendChild(messageSpan); logOutput.appendChild(logDiv); }); addJsonToggleBehavior(); } // Parse log input function parseLogs(rawLogs) { const logLines = rawLogs.split('\n').filter(line => line.trim() !== ''); return logLines.map(line => { const match = line.match(/^\[(.*?)\] (\w+): (.+)$/); if (!match) return null; return { timestamp: match[1], level: match[2], message: match[3] }; }).filter(log => log !== null); } // Highlight JSON in a string function highlightJsonInText(text) { const jsonMatch = text.match(/\{.*\}|\[.*\]/); if (jsonMatch) { try { JSON.parse(jsonMatch[0]); // Check if valid JSON return text.replace( jsonMatch[0], `<span class='json_in_log' data-original='${jsonMatch[0]}'>${jsonMatch[0]}</span>` ); } catch (e) { return text; // If JSON is invalid, return the original text } } return text; } // Add toggle behavior for JSON elements function addJsonToggleBehavior() { const jsonElements = document.querySelectorAll('.json_in_log'); jsonElements.forEach(el => { el.addEventListener('click', () => { const originalJson = el.getAttribute('data-original'); if (!el.dataset.expanded) { try { const parsedJson = JSON.parse(originalJson); el.textContent = JSON.stringify(parsedJson, null, 4); el.classList.add('json_beautified'); el.dataset.expanded = true; } catch (e) { console.error('Invalid JSON:', originalJson); } } else { el.textContent = originalJson; el.classList.remove('json_beautified'); delete el.dataset.expanded; } }); }); } // Apply filters to logs function applyFilters() { const rawLogs = logInput.value; const substring = filterSubstring.value.toLowerCase(); const selectedLevels = Array.from(filterLevels.querySelectorAll('input:checked')).map( checkbox => checkbox.value ); const parsedLogs = parseLogs(rawLogs); const filteredLogs = parsedLogs.filter(log => { const matchesSubstring = log.message.toLowerCase().includes(substring); const matchesLevel = selectedLevels.includes(log.level); return matchesSubstring && matchesLevel; }); renderLogs(filteredLogs); } // Toggle auto-refresh function toggleAutoRefresh() { autoRefresh = !autoRefresh; toggleAutoRefreshButton.textContent = autoRefresh ? 'Pause Auto-Refresh' : 'Play Auto-Refresh'; if (autoRefresh) { autoRefreshInterval = setInterval(loadDefaultLogs, 5000); } else { clearInterval(autoRefreshInterval); } } // Event listeners for real-time filtering and auto-refresh toggle filterLevels.addEventListener('change', applyFilters); filterSubstring.addEventListener('input', applyFilters); toggleAutoRefreshButton.addEventListener('click', toggleAutoRefresh); refreshButton.addEventListener('click', loadDefaultLogs); // Start auto-refresh on page load loadDefaultLogs(); autoRefreshInterval = setInterval(loadDefaultLogs, 5000); // Получаем элемент с классом .toggle-autorefresh const autoRefreshButton = document.querySelector('#toggle-autorefresh'); if (!AUTO_REFRESH_ENABLED_DEFAULT) { if (autoRefreshButton) { autoRefreshButton.click(); } } </script> <!-- Bootstrap 5 JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> </body> </html>