presseportale/dev/betrieb/monitoring-plan.md

12 KiB
Raw Blame History

Betriebsüberwachung, Bedienbarkeit, Nutzungsstatistik — Plan

Stand: 02.10.2026 · Branch: technik/monitoring-phase-1

Ziel: Fehler fallen auf, bevor Kunden sie melden — Serverfehler aus der Programmierung ebenso wie Bedienungs- und Usability-Probleme. Dazu eine Nutzungsstatistik, aus der sich ablesen lässt, wer was tut und wo Arbeit aufhört.

Entscheidungen (Betreiber, 02.10.2026)

Nr. Frage Entscheidung
M1 Fehlerwerkzeug GlitchTip, selbst gehostet (wie Umami auf sys.adametz.media), angebunden über die offiziellen Sentry-SDKs (sentry/sentry-laravel, @sentry/browser)
M2 Umami im Portal ja, mit Zuordnung zum Nutzer (umami.identify mit der internen User-ID, keine E-Mail, keine Namen)
M3 Alarmweg Mail bleibt, zusätzlich ntfy (HTTP-POST, JSON)

Bei der Umsetzung getroffen (Bestätigung offen):

Nr. Frage Entscheidung
M4 Personenbezug in GlitchTip Nur die interne User-ID, send_default_pii aus, Request-Bodys nie (max_request_body_size = never)
M5 Browserfehler-SDK Wird erst beim ersten Fehler nachgeladen (dynamischer Import) — kein Ladegewicht auf fehlerfreien Seiten, dafür ohne Breadcrumbs
M6 Umami-Schalter Getrennt: UMAMI_ENABLED (Webseiten), UMAMI_PORTAL_ENABLED (Kundenbereich), UMAMI_IDENTIFY_USERS (Zuordnung). Portal und Zuordnung erst nach DSE-Anpassung einschalten
M7 Umami im Admin, unter Impersonation, auf Auth-Seiten Nicht getrackt: Admin-Nutzung verfälscht die Kundenstatistik, Impersonation würde Admin-Klicks dem Kunden zuschreiben, Auth-Seiten tragen Tokens im Pfad (/reset-password/{token})
M8 Query-Parameter an Umami Nur utm_*, src, ref, q, page gehen raus; alles andere (Signaturen, Tokens) filtert data-before-send — gilt auch für die Webseiten
M9 Fehlgeschlagene Logins Im Verlauf mit User-Bezug (falls Konto existiert) und gekürztem HMAC der IP statt Klartext-IP

Phase 1 — Fehler fallen sofort auf

Schritt Inhalt Stand
1.1 GlitchTip-Anbindung Server: sentry/sentry-laravel, Integration::handles() in bootstrap/app.php, Release aus SENTRY_RELEASE, User-ID als einziger Personenbezug erledigt
1.2 Browserfehler (JS-Fehler, unbehandelte Promises) auf Webseiten, Auth-Seiten und Portal; eigener Vite-Eintrag resources/js/error-reporting.js, SDK erst beim ersten Fehler erledigt
1.3 Fehler ohne Exception als GlitchTip-Warnung: 404 mit Referrer der eigenen Domain (kaputte interne Links), 403 und 429 für angemeldete Nutzer, 419 (Seite abgelaufen, Arbeit verloren). Je Signal gedrosselt erledigt
1.4 Scheduler-Herzschlag (monitoring:heartbeat, minütlich) und Check im Systemstatus erledigt
1.5 Tiefer Health-Endpunkt /health mit Token: DB, Cache, Queue-Rückstau, Scheduler-Herzschlag, Plattenplatz → 200/503 JSON, für Uptime Kuma bzw. Ploi erledigt
1.6 ntfy als zweiter Alarmweg in OperationalAlert erledigt
1.7 Fehlgeschlagene Logins, Login-Sperren und 2FA-Fehler im Aktivitäts-Verlauf erledigt
1.8 queue:prune-failed (30 Tage) im Scheduler erledigt
1.9 Umami: Portal (Kundenbereich) mit identify, Query-Filter, Web Vitals; Schalter getrennt (M6) erledigt

Signale, die nicht im Browser erfasst werden, weil der Server sie schon sieht: fehlgeschlagene Livewire-Requests mit 5xx (Exception in GlitchTip) und 419 (Schritt 1.3).

Phase 2 — Bedienprobleme sichtbar machen

  • Validierungsfehler zentral zählen: Formular/Komponente × Feld × Regel, nie der Wert.
  • Schritt-Tracking für die Kernabläufe (Registrierung, Newsroom anlegen, PM erstellen/einreichen, Checkout, Bildbestätigung): step_viewed / step_completed, pseudonymer Ablauf-Schlüssel, eigene kleine Tabelle, Schreiben per defer().
  • Benannte Umami-Events (data-umami-event) an den Kern-Buttons.
  • „Problem melden“ im Portal: URL, Browser, letzte GlitchTip-Event-ID, Freitext.
  • Geschäftstrichter aus vorhandenen Daten (Registrierung → erste PM → eingereicht → bezahlt → veröffentlicht → zweite PM), ohne neues Tracking.

Phase 3 — Rückgänge und Abwanderung erkennen

  • Tägliche Kennzahlen-Tabelle (Datum × Kennzahl × Dimension → Anzahl), nächtlich verdichtet.
  • Alarme bei ausbleibender Aktivität: Einlieferer ohne Einlieferung, Tage ohne Registrierung/Login/Zahlung, Funktionen deutlich unter dem 4-Wochen-Mittel.
  • Überfällig-Liste je Kunde (übliches PM-Intervall × 2).
  • Admin-Seite „Betrieb & Nutzung“; conversion:funnel und api:usage-report mit Oberfläche.
  • Morgendliche Sammelmail.
  • Löschfristen für activity_events und api_usage_logs (Vorschlag 395 Tage).

Datenschutz (Betreiber/Anwalt, vor dem Einschalten)

  • DSE §5 (Umami): Portal-Nutzung mit Zuordnung zur Nutzer-ID ist personenbezogen (pseudonym) — Abschnitt ergänzen, bevor UMAMI_PORTAL_ENABLED/UMAMI_IDENTIFY_USERS an.
  • Neuer Abschnitt Fehlerprotokollierung (GlitchTip, selbst gehostet; User-ID, URL, Browser, Fehlermeldung; Frist = GlitchTip-Aufbewahrung, Vorschlag 90 Tage).
  • Aktivitäts-Verlauf mit fehlgeschlagenen Anmeldungen (IP nur als gekürzter HMAC).
  • Unabhängig davon offen: activity_events/api_usage_logs ohne Löschfrist und ohne DSE-Erwähnung.

Live-Schritte Phase 1

Ausgeführt mit Deploy v1.1.0 — vollständiger Ablauf in deploy-v1.1.0.md.

Infrastruktur (einmalig)

  1. GlitchTip auf sys.adametz.media (Docker Compose: web, worker, Postgres, Redis; GLITCHTIP_MAX_EVENT_LIFE_DAYS=90). Zwei Projekte anlegen: pressekonto-server (PHP/Laravel) und pressekonto-browser (JavaScript). Alert-Regel je Projekt: Mail an das Ops-Postfach bei neuem Fehlertyp.
  2. ntfy: Server (ntfy.sh oder selbst gehostet), Topic mit Zugriffsschutz, Token anlegen. Optional GlitchTip-Webhook an ntfy über dessen Template-Funktion.
  3. Uptime Kuma (oder Ploi-Monitor) auf https://pressekonto.com/health mit Header Authorization: Bearer <MONITORING_HEALTH_TOKEN>, Intervall 1 min, Benachrichtigung über ntfy und Mail.

.env auf Live

SENTRY_LARAVEL_DSN=…           # Projekt pressekonto-server
SENTRY_ENVIRONMENT=production
SENTRY_RELEASE=                # leer lassen, siehe RELEASE-Datei unten
SENTRY_BROWSER_DSN=…           # Projekt pressekonto-browser
MONITORING_HEALTH_TOKEN=<zufällig, ≥ 32 Zeichen>
MONITORING_NTFY_URL=https://push.adametz.media
MONITORING_NTFY_TOPIC=pressekonto-ops
MONITORING_NTFY_TOKEN=…
UMAMI_ENABLED=true             # Webseiten
UMAMI_PORTAL_ENABLED=false     # erst nach DSE-Anpassung
UMAMI_IDENTIFY_USERS=false     # erst nach DSE-Anpassung

Danach config:cache, npm run build, queue:restart.

Bei jedem Deploy (SFTP/SSH, solange es kein Git-Deploy gibt): lokal vor dem Upload git describe --tags --always --dirty > RELEASE und die Datei RELEASE mit hochladen, danach auf dem Server php artisan config:cache. GlitchTip ordnet Fehler so dem Stand zu (…-dirty = mit nicht committeten Änderungen deployt). Beim späteren Git-Deploy schreibt das Deploy-Skript dieselbe Datei.

GlitchTip-Instanz: errors.adametz.media, Projekt 1 = Server, Projekt 2 = Browser. Umgebungen: production (Live), local (Entwicklung).

Umsetzungsprotokoll

02.10.2026 — Phase 1 umgesetzt (Branch technik/monitoring-phase-1)

Neue Abhängigkeiten: sentry/sentry-laravel ^4.28 (Composer, nur neue Pakete), @sentry/browser ^11.3 (npm).

Schritt Dateien
1.1 config/sentry.php (veröffentlicht, gehärtet), app/Support/Monitoring/ErrorReportScrubber.php, bootstrap/app.php (Integration::handles)
1.2 resources/js/error-reporting.js + error-reporting-sdk.js (Chunk 32 kB gzip, nur bei Fehler), resources/views/components/monitoring/browser-errors.blade.php, eingebunden in partials/head (Portal), web/layouts/web-master, components/layouts/auth/pressekonto; Vite-Einträge in beiden Configs
1.3 app/Support/Monitoring/ErrorSignal.php, app/Http/Middleware/ReportErrorSignals.php (global)
1.4 app/Console/Commands/MonitoringHeartbeat.php, Scheduler minütlich, Check scheduler im Systemstatus
1.5 app/Services/Monitoring/HealthProbe.php, app/Http/Controllers/HealthController.php, Route /health (ohne Gruppe, throttle 30/min), Basic-Auth-Ausnahme; Check disk im Systemstatus
1.6 app/Support/OperationalAlert.php (Mail und ntfy unabhängig, beide Best-Effort)
1.7 ActivityType::LoginFailed/LoginLockout/TwoFactorFailed, RecordAuthActivity (Grund unknown_account/no_password/wrong_password, ip_hash), Login-Komponente feuert Failed und Lockout mit Adresse
1.8 queue:prune-failed --hours=720 täglich 03:55
1.9 components/web/umami.blade.php (Web + Portal, data-tag, data-before-send, data-performance, identify('user-<id>')), config/services.php (portal_enabled, identify_users)

Nebenbefund: Das Umami-Snippet der Webseiten schickte bisher die volle Query mit — signierte Claim-Links (?signature=…) landeten so in Umami. Der Filter (M8) gilt jetzt auch dort.

Tests: tests/Feature/Monitoring/* (neu), tests/Feature/Auth/FailedLoginActivityTest.php (neu), OperationalAlertTest und UmamiSnippetTest erweitert. Gesamtsuite 2312 grün (1 übersprungen), Larastan ohne Meldung, Pint sauber.

Nicht im Browser geprüft (kein GlitchTip/ntfy angebunden): das tatsächliche Einliefern an GlitchTip und die Darstellung dort. Erster Test nach dem Aufsetzen: im Browser setTimeout(() => { throw new Error('GlitchTip-Test') }) auf einer Portalseite, serverseitig php artisan tinker --execute 'report(new RuntimeException("GlitchTip-Test"));'.

05.10.2026 — Composer-Update

composer update innerhalb der bestehenden Grenzen (48 Updates, u. a. Laravel 12.69.3, Livewire 4.4.7, Flux 2.20.1, Fortify 1.40.0); damit sind die beiden league/commonmark-Advisories behoben (2.10.3), composer audit sauber.

  • Livewire 4.4.7 rendert ModelNotFoundException im Testhelfer als 404, statt sie zu werfen — zwei Tests auf assertNotFound() umgestellt (CustomerCompanyContextTest, ContactAccessTest). Laufzeitverhalten unverändert.
  • Deploy: Liegt auf Live public/vendor/livewire/ (veröffentlichte Assets), nach composer install php artisan vendor:publish --tag=livewire:assets --force ausführen — sonst läuft ein altes livewire.js gegen das neue Backend. Lokal war die Kopie schon vor dem Update veraltet (Hash 3dabe98a statt 8ea5922c).

05.10.2026 — GlitchTip angebunden (lokal)

DSNs eingetragen (errors.adametz.media, Projekte 1 und 2), lokal SENTRY_ENVIRONMENT=local. Release-Kennung ohne Git auf dem Server: App\Support\Monitoring\Release liest SENTRY_RELEASE, sonst die beim Deploy hochgeladene Datei RELEASE (in .gitignore). Test-Events lokal abgeschickt: Server-Exception (report()), toter interner Link per HTTP.

05.10.2026 — ntfy angebunden (lokal)

ntfy auf push.adametz.media (gleicher Server wie GlitchTip), auth-default-access: deny-all (anonymes Lesen → 403 geprüft). Eigener Benutzer pressekonto-alerts mit write-only auf pressekonto-ops, Token in MONITORING_NTFY_TOKEN; Lesen über den persönlichen Benutzer in der App. Test-Alert aus OperationalAlert ohne Fehler angenommen.

05.10.2026 — Versionsnummer

Versionen sind Git-Tags nach SemVer (v1.0.0 Go-Live 20.07., nächster Deploy v1.1.0). config('app.version') = APP_VERSION oder Datei RELEASE (git describe --tags --always --dirty); dieselbe Kennung als GlitchTip-Release, in /health (version), im Admin-Systemstatus und in php artisan about. Grundlage für die spätere Changelog-Seite.