diff --git a/src/public/js/thresholds/logging.js b/src/public/js/thresholds/logging.js deleted file mode 100644 index 534423406..000000000 --- a/src/public/js/thresholds/logging.js +++ /dev/null @@ -1,456 +0,0 @@ -PulseApp.thresholds = PulseApp.thresholds || {}; - -PulseApp.thresholds.logging = (() => { - let clearAllLogsButton = null; - - function init() { - clearAllLogsButton = document.getElementById('clear-all-logs-button'); - - const startLogButton = document.getElementById('start-log-button'); - if (startLogButton) { - startLogButton.addEventListener('click', startThresholdLogging); - } else { - console.warn('#start-log-button not found.'); - } - - if (clearAllLogsButton) { - clearAllLogsButton.addEventListener('click', clearAllFinishedLogs); - } else { - console.warn('#clear-all-logs-button not found.'); - } - - updateClearAllButtonVisibility(); // Initial check - } - - function _getLogCriteriaSnapshot() { - const thresholdState = PulseApp.state.getThresholdState(); - const filterGuestType = PulseApp.state.get('filterGuestType'); - const filterStatus = PulseApp.state.get('filterStatus'); - const searchInput = document.getElementById('dashboard-search'); - - const snapshottedThresholds = {}; - let activeThresholdCount = 0; - let criteriaDescThresholds = []; - for (const type in thresholdState) { - const value = thresholdState[type].value; - if (value > 0) { - snapshottedThresholds[type] = value; - activeThresholdCount++; - criteriaDescThresholds.push(`${PulseApp.utils.getReadableThresholdName(type)}>=${PulseApp.utils.formatThresholdValue(type, value)}`); - } - } - - const snapshottedFilterGuestType = filterGuestType; - const snapshottedFilterStatus = filterStatus; - let criteriaDescFilters = []; - if (snapshottedFilterGuestType !== 'all') criteriaDescFilters.push(`Type: ${snapshottedFilterGuestType.toUpperCase()}`); - if (snapshottedFilterStatus !== 'all') criteriaDescFilters.push(`Status: ${snapshottedFilterStatus}`); - - const snapshottedRawSearch = searchInput ? searchInput.value.toLowerCase() : ''; - const snapshottedSearchTerms = snapshottedRawSearch.split(',').map(term => term.trim()).filter(term => term); - const criteriaDescSearch = snapshottedSearchTerms.length > 0 ? `Search: "${snapshottedSearchTerms.join(', ')}"` : null; - - const fullCriteriaDesc = [criteriaDescFilters.join('; '), criteriaDescSearch, criteriaDescThresholds.length > 0 ? `Thresholds: ${criteriaDescThresholds.join(', ')}`: null].filter(Boolean).join('; '); - - return { - snapshottedThresholds, - activeThresholdCount, - snapshottedFilterGuestType, - snapshottedFilterStatus, - snapshottedSearchTerms, - fullCriteriaDesc - }; - } - - function _validateLogCriteria(criteria) { - if (criteria.activeThresholdCount === 0 && criteria.snapshottedSearchTerms.length === 0 && (criteria.snapshottedFilterGuestType === 'all' && criteria.snapshottedFilterStatus === 'all')) { - alert("Please set at least one threshold, enter search terms, or select a Type/Status filter before starting the log."); - const toggleThresholdsButton = document.getElementById('toggle-thresholds-button'); - if (toggleThresholdsButton && !PulseApp.state.get('isThresholdRowVisible') && criteria.activeThresholdCount === 0) { - toggleThresholdsButton.click(); - } - return false; - } - return true; - } - - function _createLogPanelHeader(sessionId, sessionTitle) { - const header = document.createElement('div'); - header.className = 'log-session-header flex justify-between items-center p-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/50 rounded-t'; - - const titleSpan = document.createElement('span'); - titleSpan.className = 'text-sm font-medium text-gray-700 dark:text-gray-200 truncate'; - titleSpan.textContent = sessionTitle; - titleSpan.title = sessionTitle; - - const panelCloseButton = document.createElement('button'); - panelCloseButton.className = 'p-1 rounded text-gray-500 hover:bg-red-100 dark:text-gray-400 dark:hover:bg-red-800/50'; - panelCloseButton.innerHTML = ''; - panelCloseButton.title = 'Close Log Panel & Stop Log (if active)'; - - panelCloseButton.onclick = (event) => { - const panelToRemove = event.currentTarget.closest('.log-session-panel'); - if (panelToRemove) { - const panelSessionIdStr = panelToRemove.id.replace('log-session-panel-', ''); - PulseApp.ui.tabs.removeLogTabAndContent(panelSessionIdStr); - } - }; - - header.appendChild(titleSpan); - header.appendChild(panelCloseButton); - return header; - } - - function _createLogTableElement(sessionId) { - const tableContainer = document.createElement('div'); - tableContainer.className = 'log-table-container p-2 max-h-96 overflow-y-auto'; - - const table = document.createElement('table'); - table.id = `log-table-${sessionId}`; - table.className = 'min-w-full text-xs border-collapse'; - table.innerHTML = ` - - - Time - Guest - Node - CPU - Mem - Disk% - DRead - DWrite - NetIn - NetOut - - - - - - Logging started continuously... - - - - `; - tableContainer.appendChild(table); - return tableContainer; - } - - function startThresholdLogging() { - const criteria = _getLogCriteriaSnapshot(); - if (!_validateLogCriteria(criteria)) { - return; - } - - const sessionId = Date.now(); - const startTime = new Date(); - const sessionTitle = `Log @ ${startTime.toLocaleTimeString()}${criteria.fullCriteriaDesc ? ` (${criteria.fullCriteriaDesc})` : ''}`; - - const logContentContainer = PulseApp.ui.tabs.addLogTab(sessionId, sessionTitle, criteria.fullCriteriaDesc); - if (!logContentContainer) return; - - const panel = document.createElement('div'); - panel.id = `log-session-panel-${sessionId}`; - panel.className = 'log-session-panel border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 shadow-md'; - - const headerElement = _createLogPanelHeader(sessionId, sessionTitle); - const tableElement = _createLogTableElement(sessionId); - - panel.appendChild(headerElement); - panel.appendChild(tableElement); - logContentContainer.appendChild(panel); - - PulseApp.state.addActiveLogSession(sessionId, { - thresholds: criteria.snapshottedThresholds, - startTime: startTime, - entries: [], - element: panel, - searchTerms: criteria.snapshottedSearchTerms, - filterGuestType: criteria.snapshottedFilterGuestType, - filterStatus: criteria.snapshottedFilterStatus - }); - - console.log(`[Log Session ${sessionId}] Started. Thresholds:`, criteria.snapshottedThresholds); - updateClearAllButtonVisibility(); - } - - function stopThresholdLogging(sessionId, reason = 'manual') { - const session = PulseApp.state.getActiveLogSession(sessionId); - if (!session) { - console.warn(`Attempted to stop non-existent log session: ${sessionId}`); - return; - } - - console.log(`[Log Session ${sessionId}] Stopping. Reason: ${reason}`); - - if (session.element) { - const tableBody = session.element.querySelector(`#log-table-${sessionId} tbody`); - if (tableBody) { - const initialMsgRow = tableBody.querySelector('.initial-log-message'); - if (initialMsgRow) initialMsgRow.remove(); - - const finalStatusRow = tableBody.insertRow(-1); - finalStatusRow.className = 'final-log-message'; - const cell = finalStatusRow.insertCell(0); - cell.colSpan = 10; // Adjusted colspan - cell.className = 'p-1 px-2 text-center text-xs text-gray-500 dark:text-gray-400 italic border-t border-gray-200 dark:border-gray-700'; - const stopTime = new Date().toLocaleTimeString(); - if (reason === 'timer') { - cell.textContent = `Logging finished (timer expired) at ${stopTime}`; - } else { - cell.textContent = `Logging stopped manually at ${stopTime}`; - } - } - } - - PulseApp.state.removeActiveLogSession(sessionId); - updateClearAllButtonVisibility(); // Show the button as this log is now finished - } - - function addLogRow(sessionId, entry) { - const session = PulseApp.state.getActiveLogSession(sessionId); - if (!session || !session.element) return; - - const tableBody = session.element.querySelector(`#log-table-${sessionId} tbody`); - if (!tableBody) return; - - const initialMsgRow = tableBody.querySelector('.initial-log-message'); - if (initialMsgRow) initialMsgRow.remove(); - - const row = tableBody.insertRow(0); - row.className = 'bg-yellow-50 dark:bg-yellow-900/20 animate-pulse-once'; - - const cpuValueHTML = entry.activeThresholdKeys.includes('cpu') ? `${entry.cpuFormatted}` : entry.cpuFormatted; - const memValueHTML = entry.activeThresholdKeys.includes('memory') ? `${entry.memFormatted}` : entry.memFormatted; - const diskValueHTML = entry.activeThresholdKeys.includes('disk') ? `${entry.diskFormatted}` : entry.diskFormatted; - const diskReadValueHTML = entry.activeThresholdKeys.includes('diskread') ? `${entry.diskReadFormatted}` : entry.diskReadFormatted; - const diskWriteValueHTML = entry.activeThresholdKeys.includes('diskwrite') ? `${entry.diskWriteFormatted}` : entry.diskWriteFormatted; - const netInValueHTML = entry.activeThresholdKeys.includes('netin') ? `${entry.netInFormatted}` : entry.netInFormatted; - const netOutValueHTML = entry.activeThresholdKeys.includes('netout') ? `${entry.netOutFormatted}` : entry.netOutFormatted; - - const guestDisplayHTML = entry.guestMatchedSearch ? `${entry.guestName} (${entry.guestId})` : `${entry.guestName} (${entry.guestId})`; - const nodeDisplayHTML = entry.nodeMatchedSearch ? `${entry.node}` : entry.node; - - row.innerHTML = ` - ${entry.timestamp.toLocaleTimeString()} - ${guestDisplayHTML} - ${nodeDisplayHTML} - ${cpuValueHTML} - ${memValueHTML} - ${diskValueHTML} - ${diskReadValueHTML} - ${diskWriteValueHTML} - ${netInValueHTML} - ${netOutValueHTML} - `; - - tableBody.prepend(row); - - setTimeout(() => { - row.classList.remove('bg-yellow-50', 'dark:bg-yellow-900/20', 'animate-pulse-once'); - }, 1500); - - PulseApp.state.addLogEntry(sessionId, entry); - } - - function _guestMatchesSessionFilters(guest, session) { - const typeMatch = session.filterGuestType === 'all' || - (session.filterGuestType === 'vm' && guest.type === 'VM') || - (session.filterGuestType === 'lxc' && guest.type === 'CT'); - if (!typeMatch) return false; - - const statusMatch = session.filterStatus === 'all' || guest.status === session.filterStatus; - if (!statusMatch) return false; - - const snapshottedSearch = session.searchTerms || []; - if (snapshottedSearch.length > 0) { - let guestMatchedSearch = false; - let nodeMatchedSearch = false; - snapshottedSearch.forEach(term => { - if (!guestMatchedSearch && ( - (guest.name && guest.name.toLowerCase().includes(term)) || - (guest.vmid && guest.vmid.toString().includes(term)) || - (guest.uniqueId && guest.uniqueId.toString().includes(term)) - )) { - guestMatchedSearch = true; - } - if (!nodeMatchedSearch && ( - (guest.node && guest.node.toLowerCase().includes(term)) - )) { - nodeMatchedSearch = true; - } - }); - if (!(guestMatchedSearch || nodeMatchedSearch)) return false; - } - return true; - } - - function _getGuestMetricValue(guest, metricType) { - switch (metricType) { - case 'cpu': return guest.cpu * 100; - case 'memory': return guest.memory; - case 'disk': return guest.disk; - case 'diskread': return guest.diskread; - case 'diskwrite': return guest.diskwrite; - case 'netin': return guest.netin; - case 'netout': return guest.netout; - default: return undefined; - } - } - - function _guestMeetsAllThresholds(guest, sessionThresholds) { - for (const type in sessionThresholds) { - const thresholdValue = sessionThresholds[type]; - if (thresholdValue <= 0) continue; - - const guestValue = _getGuestMetricValue(guest, type); - if (guestValue === undefined || guestValue === null || guestValue === 'N/A' || isNaN(guestValue)) { - continue; // Skip if metric is not available for this guest or type - } - - if (guestValue < thresholdValue) { - return false; // Does not meet this threshold - } - } - return true; // Meets all active thresholds - } - - function _createLogEntryObject(guest, session, now) { - const metricsSnapshot = { - cpu: guest.cpu * 100, - mem: guest.memory, - disk: guest.disk, - diskRead: guest.diskread, - diskWrite: guest.diskwrite, - netIn: guest.netin, - netOut: guest.netout - }; - - let guestMatchedSearch = false; - let nodeMatchedSearch = false; - if (session.searchTerms && session.searchTerms.length > 0) { - session.searchTerms.forEach(term => { - if (!guestMatchedSearch && ( - (guest.name && guest.name.toLowerCase().includes(term)) || - (guest.vmid && guest.vmid.toString().includes(term)) || - (guest.uniqueId && guest.uniqueId.toString().includes(term)) - )) { - guestMatchedSearch = true; - } - if (!nodeMatchedSearch && ( - (guest.node && guest.node.toLowerCase().includes(term)) - )) { - nodeMatchedSearch = true; - } - }); - } - - return { - timestamp: now, - guestId: guest.vmid, - guestName: guest.name, - node: guest.node, - cpuFormatted: PulseApp.utils.formatThresholdValue('cpu', metricsSnapshot.cpu), - memFormatted: PulseApp.utils.formatThresholdValue('memory', metricsSnapshot.mem), - diskFormatted: PulseApp.utils.formatThresholdValue('disk', metricsSnapshot.disk), - diskReadFormatted: PulseApp.utils.formatThresholdValue('diskread', metricsSnapshot.diskRead), - diskWriteFormatted: PulseApp.utils.formatThresholdValue('diskwrite', metricsSnapshot.diskWrite), - netInFormatted: PulseApp.utils.formatThresholdValue('netin', metricsSnapshot.netIn), - netOutFormatted: PulseApp.utils.formatThresholdValue('netout', metricsSnapshot.netOut), - metricsRaw: metricsSnapshot, - activeThresholdKeys: Object.keys(session.thresholds), - guestMatchedSearch: guestMatchedSearch, - nodeMatchedSearch: nodeMatchedSearch - }; - } - - function _shouldAddLogEntry(session, newLogEntry, now) { - const lastEntry = session.entries.length > 0 ? session.entries[0] : null; - if (!lastEntry) return true; // Always add if it's the first entry - - // Debounce: Don't add if the same guest triggered within the last second - return !(lastEntry.guestId === newLogEntry.guestId && (now.getTime() - lastEntry.timestamp.getTime()) < 1000); - } - - function checkThresholdViolations() { - const activeSessions = PulseApp.state.getAllActiveLogSessions(); - if (Object.keys(activeSessions).length === 0) { - return; - } - - const now = new Date(); - const dashboardData = PulseApp.state.get('dashboardData') || []; - - dashboardData.forEach(guest => { - if (guest.status !== 'running') return; - - Object.keys(activeSessions).forEach(sessionId => { - const session = activeSessions[sessionId]; - if (!session) return; - - if (!_guestMatchesSessionFilters(guest, session)) { - return; - } - - if (_guestMeetsAllThresholds(guest, session.thresholds)) { - const logEntry = _createLogEntryObject(guest, session, now); - if (_shouldAddLogEntry(session, logEntry, now)) { - addLogRow(sessionId, logEntry); - } - } - }); - }); - } - - function updateClearAllButtonVisibility() { - if (!clearAllLogsButton) return; - const nestedTabsContainer = document.querySelector('.nested-tabs'); - if (!nestedTabsContainer) return; - - let hasFinishedLogs = false; - const activeSessions = PulseApp.state.getAllActiveLogSessions(); - nestedTabsContainer.querySelectorAll('.nested-tab[data-nested-tab^="log-session-"]').forEach(tab => { - const sessionId = tab.dataset.nestedTab.replace('log-session-', ''); - if (!activeSessions[sessionId]) { - hasFinishedLogs = true; - } - }); - - clearAllLogsButton.classList.toggle('hidden', !hasFinishedLogs); - } - - function clearAllFinishedLogs() { - const nestedTabsContainer = document.querySelector('.nested-tabs'); - const nestedTabContentContainer = document.querySelector('#log-content-area'); - const logSessionArea = document.getElementById('log-session-area'); - if (!nestedTabsContainer || !nestedTabContentContainer || !logSessionArea) return; - - const activeSessions = PulseApp.state.getAllActiveLogSessions(); - const tabsToRemove = []; - const contentsToRemove = []; - - nestedTabsContainer.querySelectorAll('.nested-tab[data-nested-tab^="log-session-"]').forEach(tab => { - const sessionIdFull = tab.dataset.nestedTab; - if (!activeSessions[sessionIdFull.replace('log-session-', '')]) { - tabsToRemove.push(tab); - const content = nestedTabContentContainer.querySelector(`#${sessionIdFull}`); - if (content) contentsToRemove.push(content); - } - }); - - tabsToRemove.forEach(tab => tab.remove()); - contentsToRemove.forEach(content => content.remove()); - - if (nestedTabsContainer.querySelectorAll('.nested-tab[data-nested-tab^="log-session-"]').length === 0) { - logSessionArea.classList.add('hidden'); - } - - if (clearAllLogsButton) clearAllLogsButton.classList.add('hidden'); - } - - return { - init, - stopThresholdLogging, - checkThresholdViolations, - updateClearAllButtonVisibility - }; -})();