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
- };
-})();