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 7967b50..02fd23a 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.');
+ }
+})();