fix: reduce console warnings and errors in web UI (#183)

- Add UILogger utility for conditional debug logging
- Replace verbose console.log/warn with UILogger.debug in charts.js
- Suppress non-critical cache and preference warnings in data-store.js
- Use debug level for log stream reconnection messages
- Add ?debug=true URL param or localStorage ag_debug=true to enable debug output

Closes #183
This commit is contained in:
quocthai0404
2026-01-24 14:43:01 +07:00
parent 71b9b001fd
commit cd44b2bc9d
6 changed files with 171 additions and 25 deletions

View File

@@ -18,7 +18,7 @@
<body <body
class="bg-space-950 text-gray-300 font-sans antialiased min-h-screen overflow-hidden selection:bg-neon-purple selection:text-white" class="bg-space-950 text-gray-300 font-sans antialiased min-h-screen overflow-hidden selection:bg-neon-purple selection:text-white"
x-cloak x-data="app" x-init="console.log('App initialized')"> x-cloak x-data="app" x-init="if(window.UILogger) window.UILogger.debug('App initialized')">
<!-- Toast Notification --> <!-- Toast Notification -->
<div class="fixed top-4 right-4 z-[100] flex flex-col gap-2 pointer-events-none"> <div class="fixed top-4 right-4 z-[100] flex flex-col gap-2 pointer-events-none">
@@ -389,6 +389,7 @@
<!-- Scripts - Loading Order Matters! --> <!-- Scripts - Loading Order Matters! -->
<!-- 1. Config & Utils (global helpers) --> <!-- 1. Config & Utils (global helpers) -->
<script src="js/config/constants.js"></script> <script src="js/config/constants.js"></script>
<script src="js/utils/ui-logger.js"></script><!-- Issue #183: Conditional logging utility -->
<script src="js/utils.js"></script> <script src="js/utils.js"></script>
<script src="js/utils/error-handler.js"></script> <script src="js/utils/error-handler.js"></script>
<script src="js/utils/account-actions.js"></script> <script src="js/utils/account-actions.js"></script>

View File

@@ -106,7 +106,7 @@ window.DashboardCharts.createDataset = function (label, data, color, canvas) {
} }
} }
} catch (e) { } 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; gradient = null;
} }
@@ -149,7 +149,7 @@ window.DashboardCharts.updateCharts = function (component) {
console.debug("Destroying existing quota chart from canvas property"); console.debug("Destroying existing quota chart from canvas property");
try { try {
canvas._chartInstance.destroy(); canvas._chartInstance.destroy();
} catch(e) { console.warn(e); } } catch(e) { if (window.UILogger) window.UILogger.debug(e); }
canvas._chartInstance = null; canvas._chartInstance = null;
} }
@@ -170,11 +170,11 @@ window.DashboardCharts.updateCharts = function (component) {
} }
if (typeof Chart === "undefined") { if (typeof Chart === "undefined") {
console.warn("Chart.js not loaded"); if (window.UILogger) window.UILogger.warn("Chart.js not loaded");
return; return;
} }
if (!isCanvasReady(canvas)) { if (!isCanvasReady(canvas)) {
console.debug("quotaChart canvas not ready, skipping update"); if (window.UILogger) window.UILogger.debug("quotaChart canvas not ready, skipping update");
return; return;
} }
@@ -319,12 +319,13 @@ window.DashboardCharts.updateCharts = function (component) {
window.DashboardCharts.updateTrendChart = function (component) { window.DashboardCharts.updateTrendChart = function (component) {
// Prevent concurrent updates (fixes race condition on rapid toggling) // Prevent concurrent updates (fixes race condition on rapid toggling)
if (_trendChartUpdateLock) { if (_trendChartUpdateLock) {
console.log("[updateTrendChart] Update already in progress, skipping"); if (window.UILogger) window.UILogger.debug("[updateTrendChart] Update already in progress, skipping");
return; return;
} }
_trendChartUpdateLock = true; _trendChartUpdateLock = true;
console.log("[updateTrendChart] Starting update..."); const logger = window.UILogger || console;
logger.debug("[updateTrendChart] Starting update...");
const canvas = document.getElementById("usageTrendChart"); const canvas = document.getElementById("usageTrendChart");
@@ -335,7 +336,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
try { try {
canvas._chartInstance.stop(); canvas._chartInstance.stop();
canvas._chartInstance.destroy(); canvas._chartInstance.destroy();
} catch(e) { console.warn(e); } } catch(e) { if (window.UILogger) window.UILogger.debug(e); }
canvas._chartInstance = null; canvas._chartInstance = null;
} }
@@ -359,17 +360,17 @@ window.DashboardCharts.updateTrendChart = function (component) {
// Safety checks // Safety checks
if (!canvas) { if (!canvas) {
console.error("[updateTrendChart] Canvas not found in DOM!"); if (window.UILogger) window.UILogger.debug("[updateTrendChart] Canvas not found in DOM");
_trendChartUpdateLock = false; // Release lock! _trendChartUpdateLock = false;
return; return;
} }
if (typeof Chart === "undefined") { if (typeof Chart === "undefined") {
console.error("[updateTrendChart] Chart.js not loaded"); if (window.UILogger) window.UILogger.warn("[updateTrendChart] Chart.js not loaded");
_trendChartUpdateLock = false; // Release lock! _trendChartUpdateLock = false;
return; return;
} }
console.log("[updateTrendChart] Canvas element:", { if (window.UILogger) window.UILogger.debug("[updateTrendChart] Canvas element:", {
exists: !!canvas, exists: !!canvas,
isConnected: canvas.isConnected, isConnected: canvas.isConnected,
width: canvas.offsetWidth, width: canvas.offsetWidth,
@@ -378,7 +379,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
}); });
if (!isCanvasReady(canvas)) { if (!isCanvasReady(canvas)) {
console.error("[updateTrendChart] Canvas not ready!", { if (window.UILogger) window.UILogger.debug("[updateTrendChart] Canvas not ready", {
isConnected: canvas.isConnected, isConnected: canvas.isConnected,
width: canvas.offsetWidth, width: canvas.offsetWidth,
height: canvas.offsetHeight, height: canvas.offsetHeight,
@@ -394,17 +395,17 @@ window.DashboardCharts.updateTrendChart = function (component) {
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
} }
} catch (e) { } 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" "[updateTrendChart] Canvas is ready, proceeding with chart creation"
); );
// Use filtered history data based on time range // Use filtered history data based on time range
const history = window.DashboardFilters.getFilteredHistoryData(component); const history = window.DashboardFilters.getFilteredHistoryData(component);
if (!history || Object.keys(history).length === 0) { 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; component.hasFilteredTrendData = false;
_trendChartUpdateLock = false; _trendChartUpdateLock = false;
return; return;

View File

@@ -50,7 +50,7 @@ window.DashboardFilters.loadPreferences = function(component) {
component.selectedModels = prefs.selectedModels || {}; component.selectedModels = prefs.selectedModels || {};
} }
} catch (e) { } 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 selectedModels: component.selectedModels
})); }));
} catch (e) { } catch (e) {
console.error('Failed to save dashboard preferences:', e); if (window.UILogger) window.UILogger.debug('Failed to save dashboard preferences:', e.message);
} }
}; };

View File

@@ -79,12 +79,12 @@ window.Components.logsViewer = () => ({
this.$nextTick(() => this.scrollToBottom()); this.$nextTick(() => this.scrollToBottom());
} }
} catch (e) { } catch (e) {
console.error('Log parse error:', e); if (window.UILogger) window.UILogger.debug('Log parse error:', e.message);
} }
}; };
this.eventSource.onerror = () => { this.eventSource.onerror = () => {
console.warn('Log stream disconnected, reconnecting...'); if (window.UILogger) window.UILogger.debug('Log stream disconnected, reconnecting...');
setTimeout(() => this.startLogStream(), 3000); setTimeout(() => this.startLogStream(), 3000);
}; };
}, },

View File

@@ -55,7 +55,7 @@ document.addEventListener('alpine:init', () => {
// Check TTL // Check TTL
if (data.timestamp && (Date.now() - data.timestamp > CACHE_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'); localStorage.removeItem('ag_data_cache');
return; return;
} }
@@ -70,11 +70,11 @@ document.addEventListener('alpine:init', () => {
// Don't show loading on initial load if we have cache // Don't show loading on initial load if we have cache
this.initialLoad = false; this.initialLoad = false;
this.computeQuotaRows(); this.computeQuotaRows();
console.log('Restored data from cache'); if (window.UILogger) window.UILogger.debug('Restored data from cache');
} }
} }
} catch (e) { } 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)); localStorage.setItem('ag_data_cache', JSON.stringify(cacheData));
} catch (e) { } 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(); this.lastUpdated = new Date().toLocaleTimeString();
} catch (error) { } catch (error) {
// Keep error logging for actual fetch failures
console.error('Fetch error:', error); console.error('Fetch error:', error);
const store = Alpine.store('global'); const store = Alpine.store('global');
store.showToast(store.t('connectionLost'), 'error'); store.showToast(store.t('connectionLost'), 'error');

View File

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