📚 Improved log file structure & organization in backend 🛠️

This commit is contained in:
2025-06-01 15:11:23 +02:00
parent 5287dbd1eb
commit 9e980cc01a
25 changed files with 920 additions and 169 deletions

File diff suppressed because one or more lines are too long

View File

@ -206,6 +206,23 @@ class AdminDashboard {
e.stopPropagation();
window.location.href = '/admin-dashboard?tab=logs';
});
// Logs-Funktionalität
this.addEventListenerSafe('#refresh-logs-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.loadLogs();
});
this.addEventListenerSafe('#export-logs-btn', 'click', (e) => {
e.preventDefault();
e.stopPropagation();
this.exportLogs();
});
this.addEventListenerSafe('#log-level-filter', 'change', (e) => {
this.loadLogs();
});
}
addEventListenerSafe(selector, event, handler) {
@ -242,6 +259,10 @@ class AdminDashboard {
async loadInitialData() {
await this.loadLiveStats();
await this.checkSystemHealth();
// Logs laden falls wir auf dem Logs-Tab sind
if (window.location.search.includes('tab=logs') || document.querySelector('.tabs [href*="logs"]')?.classList.contains('active')) {
await this.loadLogs();
}
}
async loadLiveStats() {
@ -856,6 +877,197 @@ class AdminDashboard {
}
}, 3000);
}
/**
* Logs-Management Funktionen
*/
async loadLogs(level = null) {
const logsContainer = document.getElementById('logs-container');
if (!logsContainer) return;
// Loading-Indikator anzeigen
logsContainer.innerHTML = `
<div class="flex justify-center items-center py-12">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 dark:border-blue-400"></div>
<span class="ml-3 text-slate-600 dark:text-slate-400">Logs werden geladen...</span>
</div>
`;
try {
const filter = level || document.getElementById('log-level-filter')?.value || 'all';
const url = `${this.apiBaseUrl}/api/admin/logs?level=${filter}&limit=100`;
const response = await fetch(url, {
headers: {
'X-CSRFToken': this.csrfToken
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
this.displayLogs(data.logs || []);
console.log('📋 Logs erfolgreich geladen');
} catch (error) {
console.error('Fehler beim Laden der Logs:', error);
logsContainer.innerHTML = `
<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl p-6 text-center">
<svg class="w-12 h-12 text-red-500 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.35 16.5c-.77.833.192 2.5 1.732 2.5z"/>
</svg>
<h3 class="text-lg font-semibold text-red-800 dark:text-red-200 mb-2">Fehler beim Laden der Logs</h3>
<p class="text-red-600 dark:text-red-400 mb-4">${error.message}</p>
<button onclick="adminDashboard.loadLogs()" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">
🔄 Erneut versuchen
</button>
</div>
`;
}
}
displayLogs(logs) {
const logsContainer = document.getElementById('logs-container');
if (!logsContainer) return;
if (!logs || logs.length === 0) {
logsContainer.innerHTML = `
<div class="text-center py-12">
<svg class="w-16 h-16 mx-auto text-slate-400 dark:text-slate-500 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
<h3 class="text-lg font-medium text-slate-900 dark:text-white mb-2">Keine Logs gefunden</h3>
<p class="text-slate-500 dark:text-slate-400">Es sind keine Logs für die ausgewählten Kriterien vorhanden.</p>
</div>
`;
return;
}
const logsHtml = logs.map(log => {
const levelColors = {
'error': 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-800 dark:text-red-200',
'warning': 'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800 text-yellow-800 dark:text-yellow-200',
'info': 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-800 dark:text-blue-200',
'debug': 'bg-gray-50 dark:bg-gray-900/20 border-gray-200 dark:border-gray-800 text-gray-800 dark:text-gray-200',
'critical': 'bg-red-100 dark:bg-red-900/40 border-red-300 dark:border-red-700 text-red-900 dark:text-red-100'
};
const levelIcons = {
'error': '❌',
'warning': '⚠️',
'info': '',
'debug': '🔍',
'critical': '🚨'
};
const levelClass = levelColors[log.level] || levelColors['info'];
const levelIcon = levelIcons[log.level] || '';
return `
<div class="border rounded-xl p-4 transition-all duration-200 hover:shadow-md ${levelClass}">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center space-x-2">
<span class="text-lg">${levelIcon}</span>
<span class="font-semibold text-sm uppercase tracking-wide">${log.level}</span>
<span class="text-xs opacity-75">${log.component || 'System'}</span>
</div>
<div class="text-xs opacity-75">
${this.formatLogTimestamp(log.timestamp)}
</div>
</div>
<div class="mb-2">
<p class="font-medium">${this.escapeHtml(log.message)}</p>
${log.details ? `<p class="text-sm opacity-75 mt-1">${this.escapeHtml(log.details)}</p>` : ''}
</div>
${log.user ? `
<div class="text-xs opacity-75">
<span class="font-medium">Benutzer:</span> ${this.escapeHtml(log.user)}
</div>
` : ''}
${log.ip_address ? `
<div class="text-xs opacity-75">
<span class="font-medium">IP:</span> ${this.escapeHtml(log.ip_address)}
</div>
` : ''}
${log.request_id ? `
<div class="text-xs opacity-75 mt-2 font-mono">
<span class="font-medium">Request-ID:</span> ${this.escapeHtml(log.request_id)}
</div>
` : ''}
</div>
`;
}).join('');
logsContainer.innerHTML = logsHtml;
}
formatLogTimestamp(timestamp) {
if (!timestamp) return 'Unbekannt';
try {
const date = new Date(timestamp);
return date.toLocaleString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
} catch (error) {
return timestamp;
}
}
escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
async exportLogs() {
try {
this.showNotification('📥 Logs werden exportiert...', 'info');
const filter = document.getElementById('log-level-filter')?.value || 'all';
const url = `${this.apiBaseUrl}/api/admin/logs/export?level=${filter}`;
const response = await fetch(url, {
headers: {
'X-CSRFToken': this.csrfToken
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Download als Datei
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = `system-logs-${new Date().toISOString().split('T')[0]}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(downloadUrl);
this.showNotification('✅ Logs erfolgreich exportiert!', 'success');
} catch (error) {
console.error('Fehler beim Exportieren der Logs:', error);
this.showNotification('❌ Fehler beim Exportieren der Logs: ' + error.message, 'error');
}
}
}
// Sichere Initialisierung - nur einmal ausführen