diff --git a/public/index.html b/public/index.html index 6958518..ea8cf41 100644 --- a/public/index.html +++ b/public/index.html @@ -18,7 +18,7 @@ + x-cloak x-data="app" x-init="if(window.UILogger) window.UILogger.debug('App initialized')">
@@ -389,6 +389,7 @@ + diff --git a/public/js/components/dashboard/charts.js b/public/js/components/dashboard/charts.js index 3641042..44e27c2 100644 --- a/public/js/components/dashboard/charts.js +++ b/public/js/components/dashboard/charts.js @@ -106,7 +106,7 @@ window.DashboardCharts.createDataset = function (label, data, color, canvas) { } } } catch (e) { - console.warn("Failed to create gradient, using solid color fallback:", e); + if (window.UILogger) window.UILogger.debug("Gradient fallback:", e.message); gradient = null; } @@ -149,7 +149,7 @@ window.DashboardCharts.updateCharts = function (component) { console.debug("Destroying existing quota chart from canvas property"); try { canvas._chartInstance.destroy(); - } catch(e) { console.warn(e); } + } catch(e) { if (window.UILogger) window.UILogger.debug(e); } canvas._chartInstance = null; } @@ -170,11 +170,11 @@ window.DashboardCharts.updateCharts = function (component) { } if (typeof Chart === "undefined") { - console.warn("Chart.js not loaded"); + if (window.UILogger) window.UILogger.warn("Chart.js not loaded"); return; } if (!isCanvasReady(canvas)) { - console.debug("quotaChart canvas not ready, skipping update"); + if (window.UILogger) window.UILogger.debug("quotaChart canvas not ready, skipping update"); return; } @@ -319,12 +319,13 @@ window.DashboardCharts.updateCharts = function (component) { window.DashboardCharts.updateTrendChart = function (component) { // Prevent concurrent updates (fixes race condition on rapid toggling) if (_trendChartUpdateLock) { - console.log("[updateTrendChart] Update already in progress, skipping"); + if (window.UILogger) window.UILogger.debug("[updateTrendChart] Update already in progress, skipping"); return; } _trendChartUpdateLock = true; - console.log("[updateTrendChart] Starting update..."); + const logger = window.UILogger || console; + logger.debug("[updateTrendChart] Starting update..."); const canvas = document.getElementById("usageTrendChart"); @@ -335,7 +336,7 @@ window.DashboardCharts.updateTrendChart = function (component) { try { canvas._chartInstance.stop(); canvas._chartInstance.destroy(); - } catch(e) { console.warn(e); } + } catch(e) { if (window.UILogger) window.UILogger.debug(e); } canvas._chartInstance = null; } @@ -359,17 +360,17 @@ window.DashboardCharts.updateTrendChart = function (component) { // Safety checks if (!canvas) { - console.error("[updateTrendChart] Canvas not found in DOM!"); - _trendChartUpdateLock = false; // Release lock! + if (window.UILogger) window.UILogger.debug("[updateTrendChart] Canvas not found in DOM"); + _trendChartUpdateLock = false; return; } if (typeof Chart === "undefined") { - console.error("[updateTrendChart] Chart.js not loaded"); - _trendChartUpdateLock = false; // Release lock! + if (window.UILogger) window.UILogger.warn("[updateTrendChart] Chart.js not loaded"); + _trendChartUpdateLock = false; return; } - console.log("[updateTrendChart] Canvas element:", { + if (window.UILogger) window.UILogger.debug("[updateTrendChart] Canvas element:", { exists: !!canvas, isConnected: canvas.isConnected, width: canvas.offsetWidth, @@ -378,7 +379,7 @@ window.DashboardCharts.updateTrendChart = function (component) { }); if (!isCanvasReady(canvas)) { - console.error("[updateTrendChart] Canvas not ready!", { + if (window.UILogger) window.UILogger.debug("[updateTrendChart] Canvas not ready", { isConnected: canvas.isConnected, width: canvas.offsetWidth, height: canvas.offsetHeight, @@ -394,17 +395,17 @@ window.DashboardCharts.updateTrendChart = function (component) { ctx.clearRect(0, 0, canvas.width, canvas.height); } } catch (e) { - console.warn("[updateTrendChart] Failed to clear canvas:", e); + if (window.UILogger) window.UILogger.debug("[updateTrendChart] Failed to clear canvas:", e.message); } - console.log( + if (window.UILogger) window.UILogger.debug( "[updateTrendChart] Canvas is ready, proceeding with chart creation" ); // Use filtered history data based on time range const history = window.DashboardFilters.getFilteredHistoryData(component); if (!history || Object.keys(history).length === 0) { - console.warn("No history data available for trend chart (after filtering)"); + if (window.UILogger) window.UILogger.debug("No history data available for trend chart (after filtering)"); component.hasFilteredTrendData = false; _trendChartUpdateLock = false; return; diff --git a/public/js/components/dashboard/filters.js b/public/js/components/dashboard/filters.js index 44d09fc..1ed31d8 100644 --- a/public/js/components/dashboard/filters.js +++ b/public/js/components/dashboard/filters.js @@ -50,7 +50,7 @@ window.DashboardFilters.loadPreferences = function(component) { component.selectedModels = prefs.selectedModels || {}; } } catch (e) { - console.error('Failed to load dashboard preferences:', e); + if (window.UILogger) window.UILogger.debug('Failed to load dashboard preferences:', e.message); } }; @@ -67,7 +67,7 @@ window.DashboardFilters.savePreferences = function(component) { selectedModels: component.selectedModels })); } catch (e) { - console.error('Failed to save dashboard preferences:', e); + if (window.UILogger) window.UILogger.debug('Failed to save dashboard preferences:', e.message); } }; diff --git a/public/js/components/logs-viewer.js b/public/js/components/logs-viewer.js index 969032f..b926ae8 100644 --- a/public/js/components/logs-viewer.js +++ b/public/js/components/logs-viewer.js @@ -79,12 +79,12 @@ window.Components.logsViewer = () => ({ this.$nextTick(() => this.scrollToBottom()); } } catch (e) { - console.error('Log parse error:', e); + if (window.UILogger) window.UILogger.debug('Log parse error:', e.message); } }; this.eventSource.onerror = () => { - console.warn('Log stream disconnected, reconnecting...'); + if (window.UILogger) window.UILogger.debug('Log stream disconnected, reconnecting...'); setTimeout(() => this.startLogStream(), 3000); }; }, diff --git a/public/js/data-store.js b/public/js/data-store.js index 4c7df16..1dfa696 100644 --- a/public/js/data-store.js +++ b/public/js/data-store.js @@ -55,7 +55,7 @@ document.addEventListener('alpine:init', () => { // Check TTL if (data.timestamp && (Date.now() - data.timestamp > CACHE_TTL)) { - console.log('Cache expired, skipping restoration'); + if (window.UILogger) window.UILogger.debug('Cache expired, skipping restoration'); localStorage.removeItem('ag_data_cache'); return; } @@ -70,11 +70,11 @@ document.addEventListener('alpine:init', () => { // Don't show loading on initial load if we have cache this.initialLoad = false; this.computeQuotaRows(); - console.log('Restored data from cache'); + if (window.UILogger) window.UILogger.debug('Restored data from cache'); } } } catch (e) { - console.warn('Failed to load cache', e); + if (window.UILogger) window.UILogger.debug('Failed to load cache', e.message); } }, @@ -89,7 +89,7 @@ document.addEventListener('alpine:init', () => { }; localStorage.setItem('ag_data_cache', JSON.stringify(cacheData)); } catch (e) { - console.warn('Failed to save cache', e); + if (window.UILogger) window.UILogger.debug('Failed to save cache', e.message); } }, @@ -127,6 +127,7 @@ document.addEventListener('alpine:init', () => { this.lastUpdated = new Date().toLocaleTimeString(); } catch (error) { + // Keep error logging for actual fetch failures console.error('Fetch error:', error); const store = Alpine.store('global'); store.showToast(store.t('connectionLost'), 'error'); diff --git a/public/js/utils/ui-logger.js b/public/js/utils/ui-logger.js new file mode 100644 index 0000000..153a49d --- /dev/null +++ b/public/js/utils/ui-logger.js @@ -0,0 +1,143 @@ +/** + * UI Logger Utility + * Provides conditional logging for the web UI to reduce console spam in production. + * Wraps console methods and only outputs when debug mode is enabled. + * + * Usage: + * window.UILogger.debug('message') - Only logs if debug mode enabled + * window.UILogger.info('message') - Only logs if debug mode enabled + * window.UILogger.warn('message') - Always logs (important warnings) + * window.UILogger.error('message') - Always logs (errors should always be visible) + * + * Enable debug mode: + * - Set localStorage.setItem('ag_debug', 'true') in browser console + * - Or pass ?debug=true in URL + */ + +(function() { + 'use strict'; + + // Check if debug mode is enabled + function isDebugEnabled() { + // Check URL parameter + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.get('debug') === 'true') { + return true; + } + + // Check localStorage + try { + return localStorage.getItem('ag_debug') === 'true'; + } catch (e) { + return false; + } + } + + // Cache debug state (can be refreshed) + let debugEnabled = isDebugEnabled(); + + window.UILogger = { + /** + * Refresh debug state (call after changing localStorage) + */ + refresh() { + debugEnabled = isDebugEnabled(); + }, + + /** + * Enable debug mode + */ + enableDebug() { + try { + localStorage.setItem('ag_debug', 'true'); + debugEnabled = true; + console.info('[UILogger] Debug mode enabled. Refresh page to see all logs.'); + } catch (e) { + console.warn('[UILogger] Could not save debug preference'); + } + }, + + /** + * Disable debug mode + */ + disableDebug() { + try { + localStorage.removeItem('ag_debug'); + debugEnabled = false; + console.info('[UILogger] Debug mode disabled.'); + } catch (e) { + // Ignore + } + }, + + /** + * Check if debug mode is enabled + * @returns {boolean} + */ + isDebug() { + return debugEnabled; + }, + + /** + * Debug level - only logs if debug mode enabled + * Use for verbose debugging info (chart updates, cache operations, etc.) + */ + debug(...args) { + if (debugEnabled) { + console.log('[DEBUG]', ...args); + } + }, + + /** + * Info level - only logs if debug mode enabled + * Use for informational messages that aren't errors + */ + info(...args) { + if (debugEnabled) { + console.info('[INFO]', ...args); + } + }, + + /** + * Log level - alias for debug + */ + log(...args) { + if (debugEnabled) { + console.log(...args); + } + }, + + /** + * Warn level - always logs + * Use for important warnings that users should see + * But suppress noisy/expected warnings unless in debug mode + */ + warn(...args) { + // In production, only show critical warnings + // In debug mode, show all warnings + if (debugEnabled) { + console.warn(...args); + } + }, + + /** + * Warn level that always shows (for critical warnings) + */ + warnAlways(...args) { + console.warn(...args); + }, + + /** + * Error level - always logs + * Errors should always be visible for debugging + */ + error(...args) { + console.error(...args); + } + }; + + // Log initial state (only in debug mode) + if (debugEnabled) { + console.info('[UILogger] Debug mode is ON. Set localStorage ag_debug=false to disable.'); + } +})();