Files
haproxy-dashboard/static/js/logs.js
Mateusz Gruszczyński da1af612ef fixes
2025-11-04 08:43:42 +01:00

226 lines
7.1 KiB
JavaScript

/**
* HAProxy Logs Management
* Pagination, filtering, and formatting of logs
*/
document.addEventListener('DOMContentLoaded', function() {
let currentPage = 1;
let perPage = 50;
let totalLogs = parseInt(document.getElementById('total_count').textContent);
let filterRegex = null;
let wrapEnabled = false;
let allLoadedLogs = [];
const logsContainer = document.getElementById('logs_container');
const searchFilter = document.getElementById('search_filter');
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 toggleWrapBtn = document.getElementById('toggle_wrap_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');
const logsWrapper = document.getElementById('logs_container_wrapper');
// Event Listeners
searchFilter.addEventListener('keyup', function() {
currentPage = 1;
try {
filterRegex = new RegExp(this.value, 'gi');
} catch(e) {
filterRegex = null;
}
applyFilter();
});
clearFilterBtn.addEventListener('click', function() {
searchFilter.value = '';
filterRegex = null;
currentPage = 1;
applyFilter();
});
toggleWrapBtn.addEventListener('click', function() {
wrapEnabled = !wrapEnabled;
const pre = document.getElementById('logs_container');
pre.style.whiteSpace = wrapEnabled ? 'pre-wrap' : 'pre';
toggleWrapBtn.classList.toggle('active', wrapEnabled);
});
perPageSelect.addEventListener('change', function(e) {
perPage = parseInt(e.target.value);
currentPage = 1;
applyFilter();
});
refreshBtn.addEventListener('click', function() {
currentPage = 1;
filterRegex = null;
searchFilter.value = '';
loadLogs();
});
prevBtn.addEventListener('click', function() {
if (currentPage > 1) {
currentPage--;
applyFilter();
}
});
nextBtn.addEventListener('click', function() {
const filtered = filterRegex ? allLoadedLogs.filter(log => filterRegex.test(log)) : allLoadedLogs;
const totalPages = Math.ceil(filtered.length / perPage);
if (currentPage < totalPages) {
currentPage++;
applyFilter();
}
});
loadAllBtn.addEventListener('click', function() {
perPage = totalLogs;
currentPage = 1;
perPageSelect.value = totalLogs;
applyFilter();
});
/**
* Load logs from API
*/
function loadLogs() {
console.log(`[Logs] Loading page ${currentPage} with ${perPage} per page`);
fetch('/api/logs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
page: currentPage,
per_page: perPage
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
allLoadedLogs = data.logs;
loadedSpan.textContent = data.logs.length;
updatePagination(data);
applyFilter();
console.log(`[Logs] Successfully loaded page ${data.page}/${Math.ceil(data.total / data.per_page)}`);
} else {
showError(data.error || 'Unknown error');
}
})
.catch(error => {
console.error('[Logs] Error loading logs:', error);
showError('Failed to load logs. Please try again.');
});
}
/**
* Apply filter and display logs
*/
function applyFilter() {
let filtered = allLoadedLogs;
if (filterRegex) {
filtered = allLoadedLogs.filter(log => {
const fullLog = `${log.timestamp || ''} ${log.source || ''} ${log.message || ''}`;
return filterRegex.test(fullLog);
});
filterRegex.lastIndex = 0; // Reset regex for next test
}
matchSpan.textContent = filtered.length;
const totalPages = Math.ceil(filtered.length / perPage) || 1;
totalPagesSpan.textContent = totalPages;
const offset = (currentPage - 1) * perPage;
const paginated = filtered.slice(offset, offset + perPage);
renderLogs(paginated, filtered);
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = offset + perPage >= filtered.length;
}
/**
* Render logs with syntax highlighting
*/
function renderLogs(logs, allFiltered) {
if (!logs || logs.length === 0) {
logsContainer.textContent = '(No logs available)';
return;
}
const output = logs.map((entry, idx) => {
const timestamp = entry.timestamp || 'N/A';
const source = entry.source || 'N/A';
const message = entry.message || 'N/A';
// Format with colors
let formatted = '';
// Timestamp (blue)
formatted += `\x1b[36m${timestamp}\x1b[0m `;
// Source IP (yellow)
formatted += `\x1b[33m${source}\x1b[0m `;
// Message (white)
formatted += `${message}`;
return `${idx + 1}. ${formatted}`;
}).join('\n');
// Convert ANSI-like colors to HTML-like (for better compatibility)
logsContainer.textContent = output;
// Highlight search matches if filter is active
if (filterRegex) {
const text = logsContainer.textContent;
const highlighted = text.replace(filterRegex, match => `>>> ${match} <<<`);
logsContainer.textContent = highlighted;
filterRegex.lastIndex = 0;
}
}
/**
* Update pagination info
*/
function updatePagination(data) {
const totalPages = Math.ceil(data.total / data.per_page);
currentPageSpan.textContent = data.page;
totalPagesSpan.textContent = totalPages;
}
/**
* Show error message
*/
function showError(message) {
logsContainer.textContent = `ERROR: ${escapeHtml(message)}`;
}
/**
* Escape HTML
*/
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// Initial load
loadLogs();
});