# 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**
  ```json
  {
    "ts": "2025-03-23T14:22:00Z",
    "level": "info",
    "component": "agent",
    "event": "startup",
    "config_file": "/etc/signage/config.yml",
    "screen_id": "info01"
  }
  ```

- **Server-Sync**
  ```json
  {
    "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**
  ```json
  {
    "ts": "2025-03-23T14:22:10Z",
    "level": "info",
    "component": "agent.mqtt",
    "event": "playlist_changed",
    "source": "mqtt",
    "cause": "playlist-changed-event"
  }
  ```

- **Fehler**
  ```json
  {
    "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**
  ```json
  {
    "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**
  ```json
  {
    "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**
  ```json
  {
    "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)
  ```json
  {
    "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)
  ```json
  {
    "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**
  ```json
  {
    "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**
  ```json
  {
    "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

```json
{
  "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:

```json
{
  "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

1. **stdout/stderr** mit `log/slog` JSON-Formatter
   - erfasst von systemd journal
   - abrufbar via `journalctl`

2. **Lokale Datei** `/var/log/signage/player.log`
   - JSON, eine Zeile pro Event
   - Rotation auf 100 MB, 10 Archive

3. **Schnelle Fehler** an Server via HTTP-POST
   - `POST /api/v1/screens/{screenSlug}/log-event`
   - asynchron, Fehler bei Offline ignoriert
   - nur `error` und `fatal` Events

#### Auf dem Server

1. **stdout/stderr** mit strukturiertem Logging
   - erfasst von Docker/systemd
   - abrufbar via `docker logs` oder `journalctl`

2. **PostgreSQL** (Phase 2+)
   - wichtige Fehler und Status-Events in Tabelle `logs`
   - Abfrage-UI im Admin-Dashboard

3. **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:

```json
{
  "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:

```json
{
  "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:

```yaml
# /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 MB
- `watchdog.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 | Email |
| 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)

1. **Dashboard-Benachrichtigungen** (im Admin-UI sichtbar)
2. **Email** an konfigurierte Admin-Adressen
3. **Webhook** fuer externe Monitoring-Systeme (Zabbix, Grafana)
4. **Server-API** `/api/v1/admin/alerts` fuer 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
