12 KiB
Betriebsüberwachung, Bedienbarkeit, Nutzungsstatistik — Plan
Stand: 05.10.2026 · Phase 1 live seit 05.10.2026 (v1.1.0)
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 perdefer(). - 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:funnelundapi:usage-reportmit Oberfläche. - Morgendliche Sammelmail.
- Löschfristen für
activity_eventsundapi_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_USERSan. - 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_logsohne 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)
- GlitchTip auf sys.adametz.media (Docker Compose: web, worker, Postgres,
Redis;
GLITCHTIP_MAX_EVENT_LIFE_DAYS=90). Zwei Projekte anlegen:pressekonto-server(PHP/Laravel) undpressekonto-browser(JavaScript). Alert-Regel je Projekt: Mail an das Ops-Postfach bei neuem Fehlertyp. - ntfy: Server (ntfy.sh oder selbst gehostet), Topic mit Zugriffsschutz, Token anlegen. Optional GlitchTip-Webhook an ntfy über dessen Template-Funktion.
- Uptime Kuma (oder Ploi-Monitor) auf
https://pressekonto.com/healthmit HeaderAuthorization: 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
ModelNotFoundExceptionim Testhelfer als 404, statt sie zu werfen — zwei Tests aufassertNotFound()umgestellt (CustomerCompanyContextTest,ContactAccessTest). Laufzeitverhalten unverändert. - Deploy: Liegt auf Live
public/vendor/livewire/(veröffentlichte Assets), nachcomposer installphp artisan vendor:publish --tag=livewire:assets --forceausführen — sonst läuft ein alteslivewire.jsgegen 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.
05.10.2026 — Phase 1 live (v1.1.0)
Mit Deploy v1.1.0 ausgeliefert (deploy-v1.1.0.md).
GlitchTip (production), ntfy und Scheduler-Herzschlag auf Live aktiv.
Offen aus Phase 1: externer Monitor auf /health; DSE-Absätze, danach
UMAMI_PORTAL_ENABLED/UMAMI_IDENTIFY_USERS.
06.10.2026 — Erste Auswertung von GlitchTip
Lesezugriff per API eingerichtet; Arbeitsanleitung, Regeln und Protokoll der
Einträge in ../glitch-tip/README.md. Befunde:
Testsuite meldete an die echte DSN (behoben, 2f02acc), Search-Console-Lauf
robuster (a90f24a), Registrierung mit Alpine-Fehler, Promise-Ablehnungen
lesbar, Tippfehler bei artisan nicht mehr gemeldet (16af374).