### Security-Fixes (K1–K6, W1–W4, W7, N1, N5–N6, V1, V5–V7)
- K1: CSRF-Schutz via Double-Submit-Cookie (httpapi/csrf.go + csrf_helpers.go)
- K2: requireScreenAccess() in allen manage-Handlern (Tenant-Isolation)
- K3: Tenant-Check bei DELETE /api/v1/media/{id}
- K4: requirePlaylistAccess() + GetByItemID() für JSON-API Playlist-Routen
- K5: Admin-Passwort nur noch als [gesetzt] geloggt
- K6: POST /api/v1/screens/register mit Pre-Shared-Secret (MORZ_INFOBOARD_REGISTER_SECRET)
- W1: Race Condition bei order_index behoben (atomare Subquery in AddItem)
- W2: Graceful Shutdown mit 15s Timeout auf SIGTERM/SIGINT
- W3: http.MaxBytesReader (512 MB) in allen Upload-Handlern
- W4: err.Error() nicht mehr an den Client
- W7: Template-Execution via bytes.Buffer (kein partial write bei Fehler)
- N1: Rate-Limiting auf /login (5 Versuche/Minute pro IP, httpapi/ratelimit.go)
- N5: Directory-Listing auf /uploads/ deaktiviert (neuteredFileSystem)
- N6: Uploads nach Tenant getrennt (uploads/{tenantSlug}/)
- V1: Upload-Logik konsolidiert in internal/fileutil/fileutil.go
- V5: Cookie-Name als Konstante reqcontext.SessionCookieName
- V6: Strukturiertes Logging mit log/slog + JSON-Handler
- V7: DB-Pool wird im Graceful-Shutdown geschlossen
### Phase 6: Screenshot-Erzeugung
- player/agent/internal/screenshot/screenshot.go erstellt
- Integration in app.go mit MORZ_INFOBOARD_SCREENSHOT_EVERY Config
### UX: PDF.js Integration
- pdf.min.js + pdf.worker.min.js als lokale Assets eingebettet
- Automatisches Seitendurchblättern im Player
### Ansible: Neue Rollen
- signage_base, signage_server, signage_provision erstellt
- inventory.yml und site.yml erweitert
### Konzept-Docs
- GRUPPEN-KONZEPT.md, KAMPAGNEN-AKTIVIERUNG.md, MONITORING-KONZEPT.md
- PROVISION-KONZEPT.md, TEMPLATE-EDITOR.md, WATCHDOG-KONZEPT.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
10 KiB
Info-Board Neu - Logging- und Monitoring-Konzept
Ziel
Logging und Monitoring geben dem Betriebsteam vollstaendige Transparenz ueber:
- Verhalten und Fehler auf dem Player
- Verhalten und Fehler auf dem Server
- Health-Status aller Screens
- Netzwerk- und Synchronisierungsprobleme
- Kapazitaetsauslastung und Trends
Das Konzept muss robust gegen Speicherplatz-Engpaesse auf dem Raspberry Pi arbeiten und zentralisiert auf dem Server auswertbar sein.
Logging-Architektur
Allgemeine Prinzipien
- strukturiertes JSON-Logging — nicht Freitextloggen, sondern strukturierte Felder
- Log-Levels:
debug,info,warn,error,fatal - Zentrale Auswertung — Player loggen lokal und senden auch an Server
- Rotation und Bereinigung — lokale Logs werden rotiert und komprimiert
- Datenschutz — keine sensiblen Inhalte (Passwoerter, API-Keys) ins Log
Komponenten und ihre Logs
1. Player-Logs
Player-Agent
Der Agent protokolliert:
-
Startup/Shutdown
{ "ts": "2025-03-23T14:22:00Z", "level": "info", "component": "agent", "event": "startup", "config_file": "/etc/signage/config.yml", "screen_id": "info01" } -
Server-Sync
{ "ts": "2025-03-23T14:22:05Z", "level": "info", "component": "agent.sync", "event": "sync_complete", "duration_ms": 342, "items_synced": 15, "bytes_downloaded": 4521000 } -
MQTT-Ereignisse
{ "ts": "2025-03-23T14:22:10Z", "level": "info", "component": "agent.mqtt", "event": "playlist_changed", "source": "mqtt", "cause": "playlist-changed-event" } -
Fehler
{ "ts": "2025-03-23T14:22:15Z", "level": "error", "component": "agent.cache", "event": "download_failed", "media_id": "abc123", "url": "https://cdn.example.com/video.mp4", "error": "connection_timeout", "retry_count": 2 } -
Watchdog-Ereignisse (siehe WATCHDOG-KONZEPT.md)
Player-UI
Die lokale Web-App protokolliert:
-
Item-Wechsel
{ "ts": "2025-03-23T14:23:00Z", "level": "info", "component": "ui", "event": "item_change", "previous_item": "img-001", "current_item": "video-002", "source": "campaign" } -
Rendering-Fehler
{ "ts": "2025-03-23T14:23:05Z", "level": "warn", "component": "ui.renderer", "event": "render_failed", "item_id": "url-003", "media_type": "webpage", "error": "load_timeout", "timeout_ms": 10000 } -
Overlay-Status-Aenderungen
{ "ts": "2025-03-23T14:23:10Z", "level": "info", "component": "ui.overlay", "event": "status_change", "old_status": "online", "new_status": "offline", "reason": "broker_connection_lost" }
Chromium
Der Browser ist schwer zu loggable, aber systemd journal erfasst:
- Startup und Argumente
- Crash-Meldungen
- Fehlerrückmeldungen bei Seitenladefehler
2. Server-Logs
Backend-API
Der Server protokolliert:
-
HTTP-Requests (strukturiert, nicht kompletter Request-Body)
{ "ts": "2025-03-23T14:22:20Z", "level": "info", "component": "server.http", "method": "POST", "path": "/api/v1/screens/info01/playlist", "status": 200, "duration_ms": 34, "user_id": "admin123", "tenant_id": "tenant01" } -
Datenbank-Operationen (nur bei Debug-Level)
{ "ts": "2025-03-23T14:22:25Z", "level": "debug", "component": "server.db", "query": "UPDATE playlists SET updated_at = NOW() WHERE screen_id = $1", "duration_ms": 5, "rows_affected": 1 } -
Fehler und Exceptions
{ "ts": "2025-03-23T14:22:30Z", "level": "error", "component": "server.api", "event": "media_download_failed", "media_id": "abc123", "reason": "storage_quota_exceeded", "available_bytes": 1024000, "required_bytes": 50000000 } -
Admin-Kommandos
{ "ts": "2025-03-23T14:22:35Z", "level": "info", "component": "server.command", "event": "command_sent", "command_type": "restart_player", "target_screen": "info01", "triggered_by_user": "admin123" }
Provisionierungs-Worker
{
"ts": "2025-03-23T14:22:40Z",
"level": "info",
"component": "server.provision",
"event": "provision_started",
"screen_id": "new_display_01",
"target_ip": "192.168.1.50",
"ansible_playbook": "site.yml"
}
Log-Format und Ausgabe
Struktur
Alle Logs folgen diesem Schema:
{
"ts": "2025-03-23T14:22:00Z", // ISO 8601, UTC
"level": "info|warn|error|debug",
"component": "agent|ui|server.api|server.db|server.mqtt",
"event": "descriptive_name",
"screen_id": "info01", // nur auf Player relevant
"tenant_id": "tenant01", // nur auf Server relevant
"user_id": "user123", // nur auf Server bei Auth-Events
"duration_ms": 342, // bei Performance-Events
// Fehler-spezifische Felder
"error": "error_code",
"error_message": "readable error",
// Domain-spezifische Felder
"item_id": "...",
"media_type": "image|video|pdf|webpage",
"source": "campaign|tenant_playlist|fallback",
// Sonstige beliebige Felder
"details": { ... }
}
Ausgabeziele
Auf dem Player
-
stdout/stderr mit
log/slogJSON-Formatter- erfasst von systemd journal
- abrufbar via
journalctl
-
Lokale Datei
/var/log/signage/player.log- JSON, eine Zeile pro Event
- Rotation auf 100 MB, 10 Archive
-
Schnelle Fehler an Server via HTTP-POST
POST /api/v1/screens/{screenSlug}/log-event- asynchron, Fehler bei Offline ignoriert
- nur
errorundfatalEvents
Auf dem Server
-
stdout/stderr mit strukturiertem Logging
- erfasst von Docker/systemd
- abrufbar via
docker logsoderjournalctl
-
PostgreSQL (Phase 2+)
- wichtige Fehler und Status-Events in Tabelle
logs - Abfrage-UI im Admin-Dashboard
- wichtige Fehler und Status-Events in Tabelle
-
Dateispeicher (Docker Volume)
/var/log/signage/server.log- Rotation und Verdichtung durch Container-Orchester
Log-Level-Strategie
Debug (development)
- SQL-Queries
- HTTP-Request-Details
- interner State-Uebergaenge
Bei Production: --log-level warn oder --log-level info
Info (standard)
- Startup/Shutdown
- erfolgreiche Operationen
- Status-Wechsel
- Synchronisierungsereignisse
Warn (aufmerksamkeit)
- Timeouts
- Retry-Versuche
- deprecierte APIs
- suboptimale Performance
Error (problematisch)
- gescheiterte HTTP-Requests
- Datenbankfehler
- fehlende Ressourcen
- Auth-Fehler
Fatal (kritisch)
- nicht-wiederherstellbare Fehler
- Prozess beendet sich danach
Monitoring-Metriken
Player-seitig
Metriken, die der Agent periodisch dem Server meldet:
{
"screen_id": "info01",
"ts": "2025-03-23T14:25:00Z",
"heartbeat": {
"uptime_seconds": 86400,
"last_sync_at": "2025-03-23T14:24:55Z",
"seconds_since_last_sync": 5,
"sync_status": "ok|failed|pending",
"sync_fail_count_24h": 0
},
"resources": {
"cpu_percent": 25,
"memory_percent": 45,
"disk_free_mb": 2048,
"disk_used_percent": 35
},
"network": {
"broker_connected": true,
"server_reachable": true,
"ip_addresses": ["192.168.1.10"],
"signal_strength_dbm": -55
},
"playback": {
"current_item_id": "img-001",
"source": "campaign",
"rendering_status": "ok",
"seconds_on_current_item": 23
},
"errors_last_hour": [
{
"event": "download_failed",
"media_id": "video-999",
"count": 2
}
]
}
Uebertragung: HTTP POST /api/v1/screens/{screenSlug}/heartbeat alle 60 Sekunden
Server-seitig
Der Server sammelt und ueberwacht:
{
"screen_id": "info01",
"status": "online|offline|degraded|error",
"last_heartbeat_at": "2025-03-23T14:25:00Z",
"seconds_since_last_heartbeat": 0,
"heartbeat_interval_sec": 60,
"offline_since_sec": null,
"screenshot": {
"latest_at": "2025-03-23T14:25:00Z",
"seconds_since_latest": 0
},
"sync": {
"latest_at": "2025-03-23T14:24:55Z",
"latest_duration_ms": 342,
"fail_count_24h": 1,
"last_error": null
},
"content": {
"current_item": "img-001",
"source": "campaign",
"campaign_id": "xmas-2025"
},
"performance": {
"cpu_avg_percent_1h": 22,
"memory_avg_percent_1h": 44,
"disk_free_mb": 2048
}
}
Diese Metriken werden in PostgreSQL gespeichert und bilden Basis fuer:
- Status-Dashboard
- Alerts
- Trend-Analysen
- Kapazitaetsplanung
Log-Rotation auf dem Player
Der Raspberry Pi hat begrenzte Speicherkapazitaet. Log-Rotation muss aggressiv sein:
# /etc/logrotate.d/signage
/var/log/signage/player.log
{
size 50M
rotate 5
compress
delaycompress
missingok
notifempty
create 0644 root root
postrotate
systemctl reload signage-agent.service || true
endscript
}
/var/log/signage/watchdog.log
{
size 20M
rotate 3
compress
delaycompress
missingok
notifempty
create 0644 root root
}
Resultat:
player.log: max 50 MB * 5 = 250 MBwatchdog.log: max 20 MB * 3 = 60 MB- Komprimierung von alten Logs auf ~10% der urspruenglichen Groesse
Alerting-Strategie
Kriterien fuer Alerts
| Bedingung | Severity | Aktion |
|---|---|---|
| Screen offline > 15 min | High | Email + Dashboard-Alert |
| Screen offline > 2h | Critical | Email + SMS |
| Sync-Fehlerquote > 50% in 1h | Medium | |
| Disk Full auf Player | Critical | Email + Stop-Recording |
| CPU > 90% fuer 5 min | Medium | Warnung + Analysis |
| Provisioning fehlgeschlagen | High | Email an Provisioner |
Alert-Kanal (Phase 2)
- Dashboard-Benachrichtigungen (im Admin-UI sichtbar)
- Email an konfigurierte Admin-Adressen
- Webhook fuer externe Monitoring-Systeme (Zabbix, Grafana)
- Server-API
/api/v1/admin/alertsfuer Polling
Zusammenfassung
Das Logging- und Monitoring-Konzept:
- ist strukturiert — JSON, nicht Freitexte
- ist verteilt — lokal auf Player + zentral auf Server
- ist speicherbewusst — Rotation und Kompression
- gibt Ueberblick — Heartbeat + Metriken fuer jeden Screen
- ermoeglicht Diagnose — detaillierte Logs im Fehlerfall
- skaliert — Verfahren gilt fuer beliebig viele Player