"feat: Update database configuration and documentation for monitoring system"
This commit is contained in:
parent
d2a9a42651
commit
8b77ded3af
Binary file not shown.
Binary file not shown.
272
backend/app/docs/ERROR_MONITORING_SYSTEM_DOCUMENTATION.md
Normal file
272
backend/app/docs/ERROR_MONITORING_SYSTEM_DOCUMENTATION.md
Normal file
@ -0,0 +1,272 @@
|
||||
# MYP Error-Monitoring System - Dokumentation
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Error-Monitoring System ist eine umfassende Lösung zur automatischen Erkennung, Meldung und Behebung kritischer Systemfehler im MYP (Mercedes-Benz Your Platform) System. Es wurde entwickelt, um Administratoren sofortige Benachrichtigungen über Datenbankfehler, Schema-Probleme und andere kritische Systemprobleme zu geben.
|
||||
|
||||
## Problemstellung
|
||||
|
||||
**Ursprünglicher Fehler:**
|
||||
```
|
||||
sqlite3.OperationalError: no such column: guest_requests.duration_minutes
|
||||
```
|
||||
|
||||
Dieser Fehler trat auf, weil das Datenmodell `GuestRequest` sowohl `duration_min` als auch `duration_minutes` definierte, aber die Datenbank nur die `duration_min` Spalte enthielt. Solche Schema-Inkonsistenzen führten zu Anwendungsfehlern und waren für Admins nicht sichtbar.
|
||||
|
||||
## Lösung
|
||||
|
||||
### 1. Automatische Datenbank-Migration ⚡
|
||||
|
||||
**Datei:** `utils/database_schema_migration.py`
|
||||
|
||||
**Erweiterte Funktionalität:**
|
||||
- Vollständige Schema-Überprüfung für alle Tabellen
|
||||
- Automatisches Hinzufügen fehlender Spalten
|
||||
- Backup-Erstellung vor jeder Migration
|
||||
- Datenmigration (kopiert `duration_min` → `duration_minutes`)
|
||||
|
||||
**Neue Spalten hinzugefügt:**
|
||||
```python
|
||||
required_columns = {
|
||||
'duration_minutes': 'INTEGER', # ← Lösung für ursprünglichen Fehler
|
||||
'file_name': 'VARCHAR(255)',
|
||||
'file_path': 'VARCHAR(500)',
|
||||
'copies': 'INTEGER DEFAULT 1',
|
||||
'updated_at': 'DATETIME DEFAULT CURRENT_TIMESTAMP',
|
||||
'approved_at': 'DATETIME',
|
||||
'rejected_at': 'DATETIME',
|
||||
'approved_by': 'INTEGER',
|
||||
'rejected_by': 'INTEGER',
|
||||
'otp_expires_at': 'DATETIME',
|
||||
'assigned_printer_id': 'INTEGER'
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Real-Time Error-Monitoring Dashboard 📊
|
||||
|
||||
**Datei:** `templates/admin.html`
|
||||
|
||||
**Neue Komponenten:**
|
||||
- **Critical Errors Alert System**: Rote Warnmeldungen für kritische Fehler
|
||||
- **Database Health Status**: Echtzeit-Überwachung der Datenbankgesundheit
|
||||
- **Automatic Fix Button**: Ein-Klick-Reparatur für häufige Probleme
|
||||
|
||||
**Features:**
|
||||
- 🚨 Sofortige Benachrichtigungen bei kritischen Fehlern
|
||||
- 🗄️ Datenbank-Gesundheitsstatus mit Live-Indikatoren
|
||||
- 🔧 Automatische Reparatur-Buttons
|
||||
- 📊 System-Metriken (CPU, RAM, Festplatte)
|
||||
|
||||
### 3. Comprehensive Health Check API 🔍
|
||||
|
||||
**Datei:** `app.py` - Neue Endpoints:
|
||||
|
||||
#### `/api/admin/system-health` (GET)
|
||||
|
||||
**Funktionalität:**
|
||||
```python
|
||||
def api_admin_system_health():
|
||||
# 1. Datenbank-Schema-Integrität prüfen
|
||||
# 2. Kritische Spalten in wichtigen Tabellen überprüfen
|
||||
# 3. Log-Dateien nach wiederkehrenden Fehlern durchsuchen
|
||||
# 4. Drucker-Konnektivität überprüfen
|
||||
# 5. System-Performance-Metriken sammeln
|
||||
# 6. Letzte Migration-Informationen abrufen
|
||||
```
|
||||
|
||||
**Response-Format:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"health_status": "healthy|warning|critical",
|
||||
"critical_errors": [
|
||||
{
|
||||
"type": "database_schema",
|
||||
"message": "Datenbank-Schema-Fehler erkannt",
|
||||
"severity": "critical",
|
||||
"suggested_fix": "Datenbank-Migration ausführen",
|
||||
"timestamp": "2025-05-29T18:22:03"
|
||||
}
|
||||
],
|
||||
"warnings": [...],
|
||||
"schema_integrity": "OK|FEHLER",
|
||||
"last_migration": "20250529_182203",
|
||||
"recent_errors_count": 0,
|
||||
"system_metrics": {
|
||||
"cpu_usage": 15.2,
|
||||
"memory_usage": 42.1,
|
||||
"disk_usage": 68.9
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `/api/admin/fix-errors` (POST)
|
||||
|
||||
**Funktionalität:**
|
||||
- Führt automatische Datenbank-Migration aus
|
||||
- Erstellt Backup vor Reparatur
|
||||
- Protokolliert alle Aktionen
|
||||
- Gibt detaillierte Ergebnis-Informationen zurück
|
||||
|
||||
### 4. Live JavaScript Error-Monitor 🔄
|
||||
|
||||
**Datei:** `static/js/admin-live.js`
|
||||
|
||||
**Neue Klassen-Methoden:**
|
||||
- `initErrorMonitoring()`: Startet das Monitoring-System
|
||||
- `checkSystemHealth()`: Prüft System alle 30 Sekunden
|
||||
- `updateHealthDisplay()`: Aktualisiert UI-Indikatoren
|
||||
- `updateErrorAlerts()`: Zeigt/versteckt Error-Alerts
|
||||
- `fixErrors()`: Führt automatische Reparatur aus
|
||||
- `showNotification()`: Toast-Benachrichtigungen
|
||||
|
||||
**Live-Features:**
|
||||
- ⏱️ Automatische Überprüfung alle 30 Sekunden
|
||||
- 🔴 Rote Indikatoren bei kritischen Fehlern
|
||||
- 🟡 Gelbe Indikatoren bei Warnungen
|
||||
- 🟢 Grüne Indikatoren bei gesundem System
|
||||
- 📱 Toast-Benachrichtigungen für Aktionen
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Schema-Migration-Prozess
|
||||
|
||||
1. **Backup-Erstellung:**
|
||||
```sql
|
||||
VACUUM INTO 'database/myp.db.backup_YYYYMMDD_HHMMSS'
|
||||
```
|
||||
|
||||
2. **Spalten-Überprüfung:**
|
||||
```python
|
||||
cursor.execute("PRAGMA table_info(guest_requests)")
|
||||
existing_columns = {row[1]: row[2] for row in cursor.fetchall()}
|
||||
```
|
||||
|
||||
3. **Automatisches Hinzufügen:**
|
||||
```sql
|
||||
ALTER TABLE guest_requests ADD COLUMN duration_minutes INTEGER
|
||||
UPDATE guest_requests SET duration_minutes = duration_min WHERE duration_minutes IS NULL
|
||||
```
|
||||
|
||||
### Error-Detection-Algorithmus
|
||||
|
||||
1. **Schema-Integrität:** Testet kritische Spalten mit `SELECT ... LIMIT 1`
|
||||
2. **Log-Analyse:** Durchsucht letzte 100 Log-Zeilen nach "OperationalError"
|
||||
3. **Performance-Monitoring:** Nutzt `psutil` für System-Metriken
|
||||
4. **Drucker-Status:** Überprüft offline/online Status
|
||||
5. **Migration-Historie:** Analysiert Backup-Dateien für letzte Änderungen
|
||||
|
||||
## Admin-Interface
|
||||
|
||||
### Darstellung im Dashboard
|
||||
|
||||
```html
|
||||
<!-- Critical Error Alert -->
|
||||
🚨 Kritische Systemfehler erkannt
|
||||
├── Datenbank-Schema-Fehler: no such column: duration_minutes
|
||||
│ 💡 Suggested Fix: Datenbank-Migration ausführen
|
||||
│ 📅 29.05.2025, 18:22:03
|
||||
│ 🔧 [Automatisch reparieren] ❌ [Verwerfen] 📊 [Details]
|
||||
|
||||
<!-- Database Health Status -->
|
||||
🗄️ Datenbank-Gesundheitsstatus 🟢 Gesund
|
||||
├── Letzte Migration: 20250529_182203
|
||||
├── Schema-Integrität: OK
|
||||
└── Letzte Fehler: 0
|
||||
```
|
||||
|
||||
### Benutzerinteraktion
|
||||
|
||||
1. **Fehler erkannt** → Alert wird automatisch angezeigt
|
||||
2. **Admin klickt "Automatisch reparieren"** → Migration wird ausgeführt
|
||||
3. **Erfolgsmeldung** → ✅ Grüne Toast-Benachrichtigung
|
||||
4. **System aktualisiert sich** → Health-Check läuft erneut
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Monitoring-Intervalle
|
||||
|
||||
```javascript
|
||||
// System Health Check alle 30 Sekunden
|
||||
setInterval(() => this.checkSystemHealth(), 30000);
|
||||
|
||||
// Toast-Notifications verschwinden nach 5 Sekunden
|
||||
setTimeout(() => notification.remove(), 5000);
|
||||
```
|
||||
|
||||
### Schwellenwerte
|
||||
|
||||
```python
|
||||
# Performance-Warnungen
|
||||
cpu_usage > 90% # Warnung bei hoher CPU-Last
|
||||
memory_usage > 85% # Warnung bei hohem RAM-Verbrauch
|
||||
recent_db_errors > 5 # Kritisch bei vielen DB-Fehlern
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Automatische Aktivierung
|
||||
|
||||
Das Error-Monitoring System ist automatisch aktiv sobald:
|
||||
1. Ein Administrator das Admin-Dashboard öffnet
|
||||
2. Das JavaScript `admin-live.js` geladen wird
|
||||
3. Die Health-Check-APIs verfügbar sind
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
```python
|
||||
# Python-Dependencies
|
||||
import psutil # Für System-Metriken
|
||||
import subprocess # Für automatische Migration
|
||||
import os # Für Log-Datei-Zugriff
|
||||
```
|
||||
|
||||
## Logging und Dokumentation
|
||||
|
||||
### Error-Logging
|
||||
|
||||
```python
|
||||
app_logger.error(f"Datenbank-Transaktion fehlgeschlagen: {str(e)}")
|
||||
app_logger.info(f"Automatische Migration erfolgreich ausgeführt von Admin {current_user.email}")
|
||||
```
|
||||
|
||||
### Admin-Aktionen
|
||||
|
||||
Alle Admin-Aktionen werden protokolliert:
|
||||
- Wer hat welche Reparatur ausgeführt
|
||||
- Zeitstempel aller Aktionen
|
||||
- Erfolg/Fehlschlag-Status
|
||||
- Detaillierte Fehlermeldungen
|
||||
|
||||
## Wartung
|
||||
|
||||
### Regelmäßige Aufgaben
|
||||
|
||||
1. **Log-Rotation:** Alte Log-Dateien archivieren
|
||||
2. **Backup-Cleanup:** Alte Backup-Dateien löschen
|
||||
3. **Performance-Monitoring:** System-Metriken überwachen
|
||||
4. **Schema-Updates:** Neue Migrations bei Model-Änderungen
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Problem:** Error-Monitor zeigt nichts an
|
||||
**Lösung:**
|
||||
1. Browser-Konsole überprüfen
|
||||
2. `/api/admin/system-health` manuell testen
|
||||
3. Admin-Berechtigung überprüfen
|
||||
|
||||
**Problem:** Automatische Reparatur schlägt fehl
|
||||
**Lösung:**
|
||||
1. Manuelle Migration: `python utils/database_schema_migration.py`
|
||||
2. Log-Dateien überprüfen
|
||||
3. Datenbank-Berechtigungen prüfen
|
||||
|
||||
## Ergebnis
|
||||
|
||||
✅ **Problem gelöst:** Der ursprüngliche `duration_minutes` Fehler wurde behoben
|
||||
✅ **Proaktiv:** Zukünftige Schema-Probleme werden automatisch erkannt
|
||||
✅ **Benutzerfreundlich:** Admins sehen Probleme sofort und können sie mit einem Klick beheben
|
||||
✅ **Umfassend:** Monitoring von DB, Performance, Logs und System-Gesundheit
|
||||
✅ **Automatisiert:** Selbst-reparierendes System für häufige Probleme
|
||||
|
||||
Das Error-Monitoring System stellt sicher, dass kritische Systemfehler nicht unbemerkt bleiben und Administratoren die Werkzeuge haben, um schnell und effektiv zu reagieren.
|
@ -49,6 +49,9 @@ class AdminLiveDashboard {
|
||||
|
||||
// Initial Load
|
||||
this.loadLiveStats();
|
||||
|
||||
// Error Monitoring System
|
||||
this.initErrorMonitoring();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
@ -366,6 +369,211 @@ class AdminLiveDashboard {
|
||||
const meta = document.querySelector('meta[name="csrf-token"]');
|
||||
return meta ? meta.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
// Error Monitoring System
|
||||
initErrorMonitoring() {
|
||||
// Check system health every 30 seconds
|
||||
this.checkSystemHealth();
|
||||
setInterval(() => this.checkSystemHealth(), 30000);
|
||||
|
||||
// Setup error alert event handlers
|
||||
this.setupErrorAlertHandlers();
|
||||
}
|
||||
|
||||
async checkSystemHealth() {
|
||||
try {
|
||||
const response = await fetch('/api/admin/system-health');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.updateHealthDisplay(data);
|
||||
this.updateErrorAlerts(data);
|
||||
} else {
|
||||
console.error('System health check failed:', data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking system health:', error);
|
||||
}
|
||||
}
|
||||
|
||||
updateHealthDisplay(data) {
|
||||
// Update database health status
|
||||
const statusIndicator = document.getElementById('db-status-indicator');
|
||||
const statusText = document.getElementById('db-status-text');
|
||||
const lastMigration = document.getElementById('last-migration');
|
||||
const schemaIntegrity = document.getElementById('schema-integrity');
|
||||
const recentErrorsCount = document.getElementById('recent-errors-count');
|
||||
|
||||
if (statusIndicator && statusText) {
|
||||
if (data.health_status === 'critical') {
|
||||
statusIndicator.className = 'w-3 h-3 bg-red-500 rounded-full animate-pulse';
|
||||
statusText.textContent = 'Kritisch';
|
||||
statusText.className = 'text-sm font-medium text-red-600 dark:text-red-400';
|
||||
} else if (data.health_status === 'warning') {
|
||||
statusIndicator.className = 'w-3 h-3 bg-yellow-500 rounded-full animate-pulse';
|
||||
statusText.textContent = 'Warnung';
|
||||
statusText.className = 'text-sm font-medium text-yellow-600 dark:text-yellow-400';
|
||||
} else {
|
||||
statusIndicator.className = 'w-3 h-3 bg-green-400 rounded-full animate-pulse';
|
||||
statusText.textContent = 'Gesund';
|
||||
statusText.className = 'text-sm font-medium text-green-600 dark:text-green-400';
|
||||
}
|
||||
}
|
||||
|
||||
if (lastMigration) {
|
||||
lastMigration.textContent = data.last_migration || 'Unbekannt';
|
||||
}
|
||||
|
||||
if (schemaIntegrity) {
|
||||
schemaIntegrity.textContent = data.schema_integrity || 'Prüfung';
|
||||
if (data.schema_integrity === 'FEHLER') {
|
||||
schemaIntegrity.className = 'text-lg font-semibold text-red-600 dark:text-red-400';
|
||||
} else {
|
||||
schemaIntegrity.className = 'text-lg font-semibold text-green-600 dark:text-green-400';
|
||||
}
|
||||
}
|
||||
|
||||
if (recentErrorsCount) {
|
||||
const errorCount = data.recent_errors_count || 0;
|
||||
recentErrorsCount.textContent = errorCount;
|
||||
if (errorCount > 0) {
|
||||
recentErrorsCount.className = 'text-lg font-semibold text-red-600 dark:text-red-400';
|
||||
} else {
|
||||
recentErrorsCount.className = 'text-lg font-semibold text-green-600 dark:text-green-400';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateErrorAlerts(data) {
|
||||
const alertContainer = document.getElementById('critical-errors-alert');
|
||||
const errorList = document.getElementById('error-list');
|
||||
|
||||
if (!alertContainer || !errorList) return;
|
||||
|
||||
const allErrors = [...(data.critical_errors || []), ...(data.warnings || [])];
|
||||
|
||||
if (allErrors.length > 0) {
|
||||
// Show alert container
|
||||
alertContainer.classList.remove('hidden');
|
||||
|
||||
// Clear previous errors
|
||||
errorList.innerHTML = '';
|
||||
|
||||
// Add each error
|
||||
allErrors.forEach(error => {
|
||||
const errorElement = document.createElement('div');
|
||||
errorElement.className = `p-3 rounded-lg border-l-4 ${
|
||||
error.severity === 'critical' ? 'bg-red-50 dark:bg-red-900/30 border-red-500' :
|
||||
error.severity === 'high' ? 'bg-orange-50 dark:bg-orange-900/30 border-orange-500' :
|
||||
'bg-yellow-50 dark:bg-yellow-900/30 border-yellow-500'
|
||||
}`;
|
||||
|
||||
errorElement.innerHTML = `
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<h4 class="font-medium ${
|
||||
error.severity === 'critical' ? 'text-red-800 dark:text-red-200' :
|
||||
error.severity === 'high' ? 'text-orange-800 dark:text-orange-200' :
|
||||
'text-yellow-800 dark:text-yellow-200'
|
||||
}">${error.message}</h4>
|
||||
<p class="text-sm mt-1 ${
|
||||
error.severity === 'critical' ? 'text-red-600 dark:text-red-300' :
|
||||
error.severity === 'high' ? 'text-orange-600 dark:text-orange-300' :
|
||||
'text-yellow-600 dark:text-yellow-300'
|
||||
}">💡 ${error.suggested_fix}</p>
|
||||
<p class="text-xs mt-1 text-gray-500 dark:text-gray-400">
|
||||
📅 ${new Date(error.timestamp).toLocaleString('de-DE')}
|
||||
</p>
|
||||
</div>
|
||||
<span class="ml-2 px-2 py-1 text-xs font-medium rounded-full ${
|
||||
error.severity === 'critical' ? 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100' :
|
||||
error.severity === 'high' ? 'bg-orange-100 text-orange-800 dark:bg-orange-800 dark:text-orange-100' :
|
||||
'bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100'
|
||||
}">
|
||||
${error.severity.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
errorList.appendChild(errorElement);
|
||||
});
|
||||
} else {
|
||||
// Hide alert container
|
||||
alertContainer.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
setupErrorAlertHandlers() {
|
||||
// Fix errors button
|
||||
const fixErrorsBtn = document.getElementById('fix-errors-btn');
|
||||
if (fixErrorsBtn) {
|
||||
fixErrorsBtn.addEventListener('click', async () => {
|
||||
await this.fixErrors();
|
||||
});
|
||||
}
|
||||
|
||||
// Dismiss errors button
|
||||
const dismissErrorsBtn = document.getElementById('dismiss-errors-btn');
|
||||
if (dismissErrorsBtn) {
|
||||
dismissErrorsBtn.addEventListener('click', () => {
|
||||
const alertContainer = document.getElementById('critical-errors-alert');
|
||||
if (alertContainer) {
|
||||
alertContainer.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// View details button
|
||||
const viewDetailsBtn = document.getElementById('view-error-details-btn');
|
||||
if (viewDetailsBtn) {
|
||||
viewDetailsBtn.addEventListener('click', () => {
|
||||
// Redirect to logs tab
|
||||
window.location.href = '/admin-dashboard?tab=logs';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fixErrors() {
|
||||
const fixBtn = document.getElementById('fix-errors-btn');
|
||||
if (!fixBtn) return;
|
||||
|
||||
// Show loading state
|
||||
const originalText = fixBtn.innerHTML;
|
||||
fixBtn.innerHTML = '🔄 Repariere...';
|
||||
fixBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/fix-errors', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Show success message
|
||||
this.showNotification('✅ Automatische Reparatur erfolgreich durchgeführt!', 'success');
|
||||
|
||||
// Refresh health check
|
||||
setTimeout(() => {
|
||||
this.checkSystemHealth();
|
||||
}, 2000);
|
||||
} else {
|
||||
// Show error message
|
||||
this.showNotification(`❌ Reparatur fehlgeschlagen: ${data.error}`, 'error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fixing errors:', error);
|
||||
this.showNotification('❌ Fehler bei der automatischen Reparatur', 'error');
|
||||
} finally {
|
||||
// Restore button
|
||||
fixBtn.innerHTML = originalText;
|
||||
fixBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
|
Loading…
x
Reference in New Issue
Block a user