- Fix WebSocket server to properly broadcast logs to all connected clients - Integrate professional logging system with real-time WebSocket interface - Add network status indicator with DigitalOcean Spaces connectivity - Implement AWS Signature V4 authentication for private bucket access - Add JSON content loader with backward compatibility to JS modules - Restore navigation breadcrumb system with comprehensive logging - Add multiple content formats: JSON + JS with automatic discovery - Enhance top bar with logger toggle and network status indicator - Remove deprecated temp-games module and clean up unused files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
355 lines
11 KiB
JavaScript
355 lines
11 KiB
JavaScript
// ========================================
|
||
// FICHIER: js/core/browser-logger.js - VERSION NAVIGATEUR
|
||
// Système de logging adapté du système ErrorReporting.js pour le navigateur
|
||
// ========================================
|
||
|
||
class BrowserLogger {
|
||
constructor() {
|
||
this.logs = [];
|
||
this.maxLogs = 1000; // Limite pour éviter l'explosion mémoire
|
||
this.logContainer = null;
|
||
this.isVisible = false;
|
||
|
||
// Niveaux de log avec couleurs
|
||
this.levels = {
|
||
TRACE: { value: 10, color: '#6c757d', emoji: '🔍' },
|
||
DEBUG: { value: 20, color: '#007bff', emoji: '🐛' },
|
||
INFO: { value: 30, color: '#28a745', emoji: 'ℹ️' },
|
||
WARN: { value: 40, color: '#ffc107', emoji: '⚠️' },
|
||
ERROR: { value: 50, color: '#dc3545', emoji: '❌' }
|
||
};
|
||
|
||
this.minLevel = 'DEBUG'; // Niveau minimum affiché
|
||
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// Créer l'interface de log
|
||
this.createLogUI();
|
||
|
||
// Intercepter les erreurs globales
|
||
window.addEventListener('error', (event) => {
|
||
this.logSh(`Erreur JavaScript: ${event.error.message}`, 'ERROR');
|
||
});
|
||
|
||
window.addEventListener('unhandledrejection', (event) => {
|
||
this.logSh(`Promise rejetée: ${event.reason}`, 'ERROR');
|
||
});
|
||
|
||
logSh('🚀 BrowserLogger initialisé', 'INFO');
|
||
}
|
||
|
||
createLogUI() {
|
||
// Créer le conteneur de log
|
||
const logContainer = document.createElement('div');
|
||
logContainer.id = 'browser-logger';
|
||
logContainer.innerHTML = `
|
||
<div class="logger-header">
|
||
<h3>📋 System Logs</h3>
|
||
<div class="logger-controls">
|
||
<select id="log-level-filter">
|
||
<option value="TRACE">TRACE</option>
|
||
<option value="DEBUG" selected>DEBUG</option>
|
||
<option value="INFO">INFO</option>
|
||
<option value="WARN">WARN</option>
|
||
<option value="ERROR">ERROR</option>
|
||
</select>
|
||
<button id="clear-logs">🗑️</button>
|
||
<button id="toggle-logger">❌</button>
|
||
</div>
|
||
</div>
|
||
<div class="logger-content" id="logger-content">
|
||
<div class="log-entry info">
|
||
<span class="log-time">${this.formatTime(new Date())}</span>
|
||
<span class="log-level">INFO</span>
|
||
<span class="log-message">Logger initialisé</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Ajouter les styles CSS
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
#browser-logger {
|
||
position: fixed;
|
||
top: 80px;
|
||
right: 10px;
|
||
width: 400px;
|
||
max-height: 500px;
|
||
background: #fff;
|
||
border: 2px solid #007bff;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||
font-family: 'Consolas', 'Monaco', monospace;
|
||
font-size: 12px;
|
||
z-index: 10000;
|
||
display: none;
|
||
}
|
||
|
||
#browser-logger.visible {
|
||
display: block;
|
||
}
|
||
|
||
.logger-header {
|
||
background: #007bff;
|
||
color: white;
|
||
padding: 8px 12px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-radius: 6px 6px 0 0;
|
||
}
|
||
|
||
.logger-header h3 {
|
||
margin: 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.logger-controls {
|
||
display: flex;
|
||
gap: 5px;
|
||
align-items: center;
|
||
}
|
||
|
||
.logger-controls select {
|
||
padding: 2px 4px;
|
||
font-size: 11px;
|
||
border: none;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.logger-controls button {
|
||
background: rgba(255,255,255,0.2);
|
||
border: none;
|
||
color: white;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.logger-controls button:hover {
|
||
background: rgba(255,255,255,0.3);
|
||
}
|
||
|
||
.logger-content {
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
padding: 0;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.log-entry {
|
||
padding: 4px 8px;
|
||
border-bottom: 1px solid #e9ecef;
|
||
display: flex;
|
||
gap: 8px;
|
||
font-size: 11px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.log-entry:hover {
|
||
background: #e9ecef;
|
||
}
|
||
|
||
.log-time {
|
||
color: #6c757d;
|
||
min-width: 60px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.log-level {
|
||
min-width: 50px;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
border-radius: 3px;
|
||
padding: 1px 4px;
|
||
}
|
||
|
||
.log-level.trace { background: #f8f9fa; color: #6c757d; }
|
||
.log-level.debug { background: #cce5ff; color: #007bff; }
|
||
.log-level.info { background: #d4edda; color: #28a745; }
|
||
.log-level.warn { background: #fff3cd; color: #856404; }
|
||
.log-level.error { background: #f8d7da; color: #721c24; }
|
||
|
||
.log-message {
|
||
flex: 1;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.log-message.trace { color: #6c757d; }
|
||
.log-message.network { color: #17a2b8; font-weight: bold; }
|
||
.log-message.auth { color: #6610f2; font-weight: bold; }
|
||
`;
|
||
|
||
document.head.appendChild(style);
|
||
document.body.appendChild(logContainer);
|
||
|
||
this.logContainer = logContainer;
|
||
this.logContent = logContainer.querySelector('#logger-content');
|
||
|
||
// Event listeners
|
||
document.getElementById('toggle-logger').addEventListener('click', () => {
|
||
this.toggle();
|
||
});
|
||
|
||
document.getElementById('clear-logs').addEventListener('click', () => {
|
||
this.clear();
|
||
});
|
||
|
||
document.getElementById('log-level-filter').addEventListener('change', (e) => {
|
||
this.minLevel = e.target.value;
|
||
this.refreshDisplay();
|
||
});
|
||
|
||
// Raccourci clavier pour toggle
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.ctrlKey && e.shiftKey && e.key === 'L') {
|
||
this.toggle();
|
||
}
|
||
});
|
||
}
|
||
|
||
formatTime(date) {
|
||
return date.toLocaleTimeString('fr-FR', {
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit'
|
||
});
|
||
}
|
||
|
||
logSh(message, level = 'INFO') {
|
||
const timestamp = new Date();
|
||
const logEntry = {
|
||
timestamp,
|
||
level: level.toUpperCase(),
|
||
message,
|
||
id: Date.now() + Math.random()
|
||
};
|
||
|
||
// Ajouter à la liste des logs
|
||
this.logs.push(logEntry);
|
||
|
||
// Limiter le nombre de logs en mémoire
|
||
if (this.logs.length > this.maxLogs) {
|
||
this.logs.shift();
|
||
}
|
||
|
||
// Log aussi dans la console navigateur (gardé pour le debugging)
|
||
if (level !== 'TRACE' && level !== 'DEBUG') { // Éviter le spam en console
|
||
const consoleMethod = level.toLowerCase() === 'error' ? 'error' :
|
||
level.toLowerCase() === 'warn' ? 'warn' : 'log';
|
||
console[consoleMethod](`[${this.formatTime(timestamp)}] ${level}: ${message}`);
|
||
}
|
||
|
||
// Mettre à jour l'affichage si visible
|
||
if (this.isVisible) {
|
||
this.addLogToDisplay(logEntry);
|
||
}
|
||
|
||
return logEntry;
|
||
}
|
||
|
||
addLogToDisplay(logEntry) {
|
||
const levelInfo = this.levels[logEntry.level] || this.levels.INFO;
|
||
|
||
// Vérifier si on doit afficher ce niveau
|
||
if (levelInfo.value < this.levels[this.minLevel].value) {
|
||
return;
|
||
}
|
||
|
||
const logElement = document.createElement('div');
|
||
logElement.className = `log-entry ${logEntry.level.toLowerCase()}`;
|
||
logElement.innerHTML = `
|
||
<span class="log-time">${this.formatTime(logEntry.timestamp)}</span>
|
||
<span class="log-level ${logEntry.level.toLowerCase()}">${logEntry.level}</span>
|
||
<span class="log-message ${this.getMessageClass(logEntry.message)}">${this.formatMessage(logEntry.message)}</span>
|
||
`;
|
||
|
||
this.logContent.appendChild(logElement);
|
||
|
||
// Auto-scroll vers le bas
|
||
this.logContent.scrollTop = this.logContent.scrollHeight;
|
||
|
||
// Limiter les éléments DOM affichés
|
||
const entries = this.logContent.querySelectorAll('.log-entry');
|
||
if (entries.length > 200) {
|
||
entries[0].remove();
|
||
}
|
||
}
|
||
|
||
getMessageClass(message) {
|
||
if (message.includes('🌐') || message.includes('📡') || message.includes('connexion')) {
|
||
return 'network';
|
||
}
|
||
if (message.includes('🔐') || message.includes('auth') || message.includes('Headers')) {
|
||
return 'auth';
|
||
}
|
||
return '';
|
||
}
|
||
|
||
formatMessage(message) {
|
||
// Échapper le HTML mais garder les emojis
|
||
return message.replace(/</g, '<').replace(/>/g, '>');
|
||
}
|
||
|
||
refreshDisplay() {
|
||
if (!this.isVisible) return;
|
||
|
||
this.logContent.innerHTML = '';
|
||
|
||
this.logs.forEach(logEntry => {
|
||
this.addLogToDisplay(logEntry);
|
||
});
|
||
}
|
||
|
||
show() {
|
||
this.isVisible = true;
|
||
this.logContainer.classList.add('visible');
|
||
this.refreshDisplay();
|
||
this.logSh('Logger affiché', 'DEBUG');
|
||
}
|
||
|
||
hide() {
|
||
this.isVisible = false;
|
||
this.logContainer.classList.remove('visible');
|
||
this.logSh('Logger masqué', 'DEBUG');
|
||
}
|
||
|
||
toggle() {
|
||
if (this.isVisible) {
|
||
this.hide();
|
||
} else {
|
||
this.show();
|
||
}
|
||
}
|
||
|
||
clear() {
|
||
this.logs = [];
|
||
if (this.logContent) {
|
||
this.logContent.innerHTML = '';
|
||
}
|
||
this.logSh('Logs effacés', 'INFO');
|
||
}
|
||
|
||
// Méthodes de convenance
|
||
trace(message) { return this.logSh(message, 'TRACE'); }
|
||
debug(message) { return this.logSh(message, 'DEBUG'); }
|
||
info(message) { return this.logSh(message, 'INFO'); }
|
||
warn(message) { return this.logSh(message, 'WARN'); }
|
||
error(message) { return this.logSh(message, 'ERROR'); }
|
||
}
|
||
|
||
// Instance globale
|
||
window.logger = new BrowserLogger();
|
||
|
||
// Fonction globale compatible avec le système original
|
||
window.logSh = (message, level = 'INFO') => {
|
||
return window.logger.logSh(message, level);
|
||
};
|
||
|
||
// Export pour compatibilité
|
||
if (typeof module !== 'undefined' && module.exports) {
|
||
module.exports = { BrowserLogger };
|
||
} |