📚 Improved log file structure & organization in backend 🛠️
This commit is contained in:
2
backend/static/css/tailwind.min.css
vendored
2
backend/static/css/tailwind.min.css
vendored
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
Reference in New Issue
Block a user