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

@@ -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;

View File

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

View File

@@ -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);
};
},

View File

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

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