/** * HAProxy Logs Management * Pagination, filtering, and proper formatting */ document.addEventListener('DOMContentLoaded', function() { let currentPage = 1; let perPage = 50; let totalLogs = parseInt(document.getElementById('total_count').textContent); let allLoadedLogs = []; let excludePhrases = []; const logsContainer = document.getElementById('logs_container'); const searchFilter = document.getElementById('search_filter'); const excludeFilter = document.getElementById('exclude_filter'); const excludeBtn = document.getElementById('exclude_btn'); const perPageSelect = document.getElementById('logs_per_page'); const refreshBtn = document.getElementById('refresh_logs_btn'); const prevBtn = document.getElementById('prev_btn'); const nextBtn = document.getElementById('next_btn'); const loadAllBtn = document.getElementById('load_all_btn'); const clearFilterBtn = document.getElementById('clear_filter_btn'); const loadedSpan = document.getElementById('loaded_count'); const matchSpan = document.getElementById('match_count'); const currentPageSpan = document.getElementById('current_page'); const totalPagesSpan = document.getElementById('total_pages'); // Event Listeners searchFilter.addEventListener('keyup', debounce(function() { currentPage = 1; applyFilters(); }, 300)); excludeBtn.addEventListener('click', function() { const phrase = excludeFilter.value.trim(); if (phrase) { if (!excludePhrases.includes(phrase)) { excludePhrases.push(phrase); updateExcludeUI(); applyFilters(); } excludeFilter.value = ''; } }); excludeFilter.addEventListener('keypress', function(e) { if (e.key === 'Enter') excludeBtn.click(); }); clearFilterBtn.addEventListener('click', function() { searchFilter.value = ''; excludePhrases = []; excludeFilter.value = ''; updateExcludeUI(); currentPage = 1; applyFilters(); }); perPageSelect.addEventListener('change', function() { perPage = parseInt(this.value); currentPage = 1; applyFilters(); }); refreshBtn.addEventListener('click', function() { searchFilter.value = ''; excludePhrases = []; excludeFilter.value = ''; updateExcludeUI(); currentPage = 1; loadLogs(); }); prevBtn.addEventListener('click', function() { if (currentPage > 1) { currentPage--; applyFilters(); } }); nextBtn.addEventListener('click', function() { const filtered = getFilteredLogs(); const totalPages = Math.ceil(filtered.length / perPage); if (currentPage < totalPages) { currentPage++; applyFilters(); } }); loadAllBtn.addEventListener('click', function() { perPage = totalLogs; currentPage = 1; perPageSelect.value = totalLogs; applyFilters(); }); /** * Debounce function */ function debounce(func, wait) { let timeout; return function() { clearTimeout(timeout); timeout = setTimeout(func, wait); }; } /** * Load logs from API */ function loadLogs() { logsContainer.innerHTML = 'Loading logs...'; fetch('/api/logs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ page: currentPage, per_page: perPage }) }) .then(r => r.json()) .then(data => { if (data.success) { allLoadedLogs = data.logs; loadedSpan.textContent = data.logs.length; applyFilters(); } else { showError(data.error); } }) .catch(e => { console.error('Error:', e); showError('Failed to load logs'); }); } /** * Get filtered logs */ function getFilteredLogs() { let filtered = allLoadedLogs; // Apply search filter if (searchFilter.value.trim()) { const query = searchFilter.value.toLowerCase(); filtered = filtered.filter(log => { const text = `${log.timestamp || ''} ${log.source || ''} ${log.message || ''}`.toLowerCase(); return text.includes(query); }); } // Apply exclude phrases if (excludePhrases.length > 0) { filtered = filtered.filter(log => { const text = `${log.timestamp || ''} ${log.source || ''} ${log.message || ''}`; return !excludePhrases.some(phrase => text.includes(phrase)); }); } return filtered; } /** * Apply all filters and render */ function applyFilters() { const filtered = getFilteredLogs(); matchSpan.textContent = filtered.length; const totalPages = Math.ceil(filtered.length / perPage) || 1; totalPagesSpan.textContent = totalPages; currentPageSpan.textContent = currentPage; const offset = (currentPage - 1) * perPage; const paginated = filtered.slice(offset, offset + perPage); renderLogs(paginated); prevBtn.disabled = currentPage === 1; nextBtn.disabled = offset + perPage >= filtered.length; } /** * Render logs as table rows */ function renderLogs(logs) { if (!logs || logs.length === 0) { logsContainer.innerHTML = 'No logs matching criteria'; return; } logsContainer.innerHTML = logs.map((entry, idx) => { const timestamp = entry.timestamp || '-'; const source = entry.source || '-'; const message = entry.message || '-'; // Color code by status code if present let statusClass = ''; if (message.includes('200')) statusClass = 'table-success'; else if (message.includes('404')) statusClass = 'table-warning'; else if (message.includes('500')) statusClass = 'table-danger'; return ` ${escapeHtml(timestamp)}
${escapeHtml(source)}
${escapeHtml(message)} `; }).join(''); } /** * Update exclude UI to show active filters */ function updateExcludeUI() { if (excludePhrases.length > 0) { const tags = excludePhrases.map((phrase, idx) => ` ${escapeHtml(phrase)} `).join(''); const container = document.createElement('div'); container.className = 'small mt-2'; container.innerHTML = `Hiding: ${tags}`; const existing = document.getElementById('exclude_ui'); if (existing) existing.remove(); container.id = 'exclude_ui'; excludeFilter.parentElement.parentElement.after(container); } else { const existing = document.getElementById('exclude_ui'); if (existing) existing.remove(); } } /** * Global function to remove exclude phrase */ window.removeExcludePhrase = function(idx) { excludePhrases.splice(idx, 1); updateExcludeUI(); applyFilters(); }; /** * Show error */ function showError(msg) { logsContainer.innerHTML = `${escapeHtml(msg)}`; } /** * Escape HTML */ function escapeHtml(text) { const map = {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}; return (text || '').replace(/[&<>"']/g, m => map[m]); } loadLogs(); });