Zum Hauptinhalt springen

Debugging

Laravel Logs

Standard-Logging über den Stack-Channel (single + nightwatch):

# Live Logs verfolgen
tail -f storage/logs/laravel.log

# Laravel Pail (interaktiver Log-Viewer im Terminal)
php artisan pail
php artisan pail --filter="ERROR"

Log-Level und Channels werden über .env konfiguriert:

LOG_CHANNEL=stack
LOG_STACK=single,nightwatch
LOG_LEVEL=debug

Location: config/logging.php, storage/logs/laravel.log

Log Viewer (Web UI)

opcodesio/log-viewer bietet eine Web-Oberfläche für Log-Dateien.

EigenschaftWert
Route/admin/logs
ZugriffAdmin-only (Gate: viewLogViewer)
AssetsMüssen nach Deployment publisht werden
Memory-MiddlewareIncreaseMemoryForLogViewer (512 MB)
Max Log Entry Size10 KB (reduziert von 128 KB)
Scan Chunk Size10 MB (reduziert von 50 MB)
# Assets publishen (im Deployment Script enthalten)
php artisan vendor:publish --tag=log-viewer-assets --force

Features: Volltextsuche, Level-Filter, Log-Rotation, Datei-Auswahl.

Memory-Schutz: Das Package hat einen bekannten Bug — getJsonStringsFromFullText() ruft str_split() auf $this->text auf, was für jeden Charakter ein PHP-Array-Element erzeugt (~100 Bytes Overhead pro Element). Bei 128 KB max Entry-Size × 25 Einträge pro Seite × ~100 Bytes pro Array-Element kann das schnell 300+ MB verbrauchen. Zwei Gegenmaßnahmen:

  1. LogViewer::setMaxLogSize(10 * 1024) in AppServiceProvider::boot() — reduziert max Entry-Size von 128 KB auf 10 KB
  2. IncreaseMemoryForLogViewer Middleware — erhöht Memory-Limit temporär auf 512 MB für Log-Viewer-Requests

Location: config/log-viewer.php, app/Http/Middleware/IncreaseMemoryForLogViewer.php


Laravel Pulse

Pulse sammelt Server-Metriken, Slow Queries, Slow Requests, Exceptions und Cache-Statistiken.

EigenschaftWert
Route/admin/pulse
ZugriffAdmin-only (Gate: viewPulse)
Packagelaravel/pulse
Retention7 Tage (PULSE_STORAGE_KEEP)
Scheduled Purgetäglich 01:20 UTC

Pulse Recorders

Standard-Recorders (Laravel)

RecorderWas wird erfasstThresholdSample Rate
CacheInteractionsCache Hits/Misses1.0
ExceptionsPHP-Exceptions + Location1.0
QueuesQueue-Job-Statistiken1.0
ServersCPU, RAM, Disk (pro Server)
SlowJobsJobs über Threshold1000ms1.0
SlowOutgoingRequestsHTTP-Requests über Threshold1000ms1.0
SlowQueriesDB-Queries über Threshold1000ms1.0
SlowRequestsHTTP-Requests über Threshold1000ms1.0
UserJobsUser-ausgelöste Jobs1.0
UserRequestsUser-Requests1.0

Custom Recorders (Postbox)

RecorderWas wird erfasstBeat-TypEnv-Variable
ActiveUsersMetricsAktive Sessions (letzte X Min)IsolatedBeatSERVER_ACTIVE_USERS_MINUTES=5
PostgresMetricsPG Connections, DB Size, Cache HitIsolatedBeatSERVER_PG_MAX_CONNECTIONS=200
PublicStorageMetricsPublic Storage Größe (MB)SharedBeat— (30min Cache)
SwapMetricsSwap + Disk Usage (%)SharedBeat

Location: app/Pulse/Recorders/

Pulse Artisan Commands

pulse:purge (alias: pulse:clear)

Löscht Pulse-Daten aus der Datenbank. Standardmäßig alles älter als PULSE_STORAGE_KEEP.

# Alle Pulse-Daten löschen (in Production mit --force)
php artisan pulse:purge --force

# Nur bestimmten Recorder-Typ löschen
php artisan pulse:purge --type=slow_queries --force
php artisan pulse:purge --type=exceptions --force

# Mehrere Typen gleichzeitig
php artisan pulse:purge --type=slow_queries --type=slow_jobs --force
OptionTypDefaultBeschreibung
--typestringalleNur bestimmten Recorder-Typ löschen (mehrfach möglich)
--forceflagBestätigung in Production überspringen

Löschbare Typen: cache_interactions, exceptions, queues, slow_jobs, slow_outgoing_requests, slow_queries, slow_requests, user_jobs, user_requests, active_users_count, pg_connections, pg_size_mb, pg_cache_hit, swap_percent, disk_percent, public_storage_mb

Location: vendor/laravel/pulse/src/Console/Commands/

pulse:check

Nimmt Server-Metriken-Snapshots (CPU, RAM, Disk). Läuft als Daemon oder einmalig.

# Einmaliger Snapshot
php artisan pulse:check --once

# Dauerhaft (als Forge Daemon oder Supervisor)
php artisan pulse:check
OptionBeschreibung
--onceEinen einzelnen Snapshot nehmen und beenden

pulse:work

Verarbeitet Pulse-Ingest-Daten aus dem Stream. Nur nötig bei PULSE_INGEST_DRIVER=redis.

php artisan pulse:work
php artisan pulse:work --stop-when-empty

pulse:restart

Signalisiert laufenden pulse:check und pulse:work Prozessen einen Neustart.

php artisan pulse:restart

Pulse Datenbank-Tabellen

TabelleZweckWachstum
pulse_valuesTime-Series Snapshots (1 Row pro Typ/Key)Konstant (~50 Rows)
pulse_entriesRohe Event-Logs (Queries, Exceptions, etc.)Hoch — wird durch pulse:purge bereinigt
pulse_aggregatesVorberechnete Buckets für ChartsMittel — wird durch pulse:purge bereinigt

Pulse DB-Wartung

Automatisch: pulse:purge läuft täglich um 01:20 UTC. Löscht alles älter als PULSE_STORAGE_KEEP (7 Tage).

Manuell bei Tabellen-Bloat:

# Vorschau: wie groß sind die Pulse-Tabellen?
psql -d postbox -c "
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_stat_user_tables
WHERE relname LIKE 'pulse_%'
ORDER BY pg_total_relation_size(relid) DESC;
"

# Alle Pulse-Daten sofort löschen
php artisan pulse:purge --force

# Nur Slow Queries löschen (häufigste Bloat-Ursache)
php artisan pulse:purge --type=slow_queries --force

# Direkt per SQL (schneller bei Millionen Rows)
TRUNCATE pulse_entries, pulse_aggregates;

# Danach: Disk-Space freigeben
VACUUM FULL pulse_entries;
VACUUM FULL pulse_aggregates;

Pulse Konfiguration

# Master-Switch
PULSE_ENABLED=true # false in Tests

# Retention
PULSE_STORAGE_KEEP="7 days" # Wie lange Daten behalten (default: 7 Tage)

# Thresholds (ms) — was als "slow" gilt
PULSE_SLOW_QUERIES_THRESHOLD=1000
PULSE_SLOW_REQUESTS_THRESHOLD=1000
PULSE_SLOW_JOBS_THRESHOLD=1000
PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD=1000

# Sample Rates (0.0 - 1.0) — reduzieren bei High-Traffic
PULSE_SLOW_QUERIES_SAMPLE_RATE=1
PULSE_SLOW_REQUESTS_SAMPLE_RATE=1
PULSE_EXCEPTIONS_SAMPLE_RATE=1

Location: config/pulse.php


Vantage (Queue Monitoring)

Vantage bietet detailliertes Queue-Monitoring mit Job-Details, Retry-Funktionalität und Failure-Analyse.

EigenschaftWert
Route/admin/vantage
ZugriffAdmin-only (Gate: viewVantage)
Packagehoudaslassi/vantage
Retention3 Tage (VANTAGE_RETENTION_DAYS)
DB-Tabellenvantage_jobs, vantage_job_tags

Vantage Architektur

Vantage trackt den Job-Lifecycle über 3 Laravel Queue Events:

graph LR
A[Job dispatched] --> B[JobProcessing Event]
B --> C["RecordJobStart<br/>→ status: processing"]
C --> D{Job Ergebnis}
D -->|Erfolg| E["RecordJobSuccess<br/>→ status: processed"]
D -->|Fehler| F["RecordJobFailure<br/>→ status: failed"]
D -->|"release()"| G["RecordJobSuccess<br/>→ isReleased() = true<br/>→ SKIP Update ⚠️"]
G --> H["Status bleibt 'processing'<br/>(Phantom-Record)"]

Bekanntes Problem: Wenn ein Job $this->release() aufruft (z.B. bei YouTube Quota-Erschöpfung), erkennt Vantage's RecordJobSuccess Listener den isReleased() Status und überspringt das Update. Der "processing"-Record bleibt für immer bestehen. Deshalb läuft vantage:cleanup-stuck täglich um diese Phantom-Records zu bereinigen.

Vantage Artisan Commands

vantage:cleanup-stuck {--timeout=1} {--dry-run}

Markiert Jobs die zu lange in "processing" stecken als "failed". Essentiell für die Bereinigung von Phantom-Records durch $this->release().

# Jobs die länger als 2 Stunden in "processing" stecken als failed markieren
php artisan vantage:cleanup-stuck --timeout=2

# Vorschau: was würde bereinigt?
php artisan vantage:cleanup-stuck --timeout=2 --dry-run

# Default: 1 Stunde Timeout
php artisan vantage:cleanup-stuck
OptionTypDefaultBeschreibung
--timeoutint1Stunden ab denen ein Job als "stuck" gilt
--dry-runflagNur anzeigen, nichts ändern

Interne Logik:

  1. Sucht Jobs mit status = 'processing' und started_at < NOW() - timeout
  2. Im Dry-Run: Zeigt Tabelle mit stuck Job Details (ID, Class, Started, Age)
  3. Ohne Dry-Run: Setzt status = 'failed', finished_at = NOW(), exception_class = TimeoutException

Schedule: Täglich 01:22 UTC

Location: vendor/houdaslassi/vantage/src/Console/Commands/CleanupStuckJobs.php

vantage:prune {--days=} {--hours=} {--status=} {--keep-processing} {--dry-run} {--force}

Löscht alte Job-Records aus der vantage_jobs Tabelle.

# Completed Jobs älter als 24 Stunden löschen
php artisan vantage:prune --status=completed --hours=24 --force

# Failed Jobs älter als 4 Tage löschen
php artisan vantage:prune --status=failed --days=4 --force

# Alle Status älter als Retention (VANTAGE_RETENTION_DAYS=3)
php artisan vantage:prune --force

# ALLE failed Jobs löschen (nur letzte Stunde behalten)
php artisan vantage:prune --status=failed --hours=1 --force

# Processing Jobs explizit löschen (normalerweise geschützt)
php artisan vantage:prune --status=processing --hours=2 --force

# Vorschau: was würde gelöscht?
php artisan vantage:prune --status=completed --hours=24 --dry-run
OptionTypDefaultBeschreibung
--daysintVANTAGE_RETENTION_DAYS (3)Jobs älter als X Tage löschen
--hoursintJobs älter als X Stunden löschen (überschreibt --days)
--statusstringalleNur bestimmten Status löschen: processed, failed, processing
--keep-processingflagProcessing-Jobs immer behalten (Standardverhalten ohne --status)
--dry-runflagNur anzeigen, nichts löschen
--forceflagBestätigung überspringen

Interne Logik:

  1. Berechnet Cutoff aus --hours oder --days (Fallback: config('vantage.retention_days'))
  2. Filtert nach Status wenn angegeben
  3. Zeigt Breakdown nach Status und Gesamtzahl
  4. Löscht in 1000er-Chunks (Performance bei großen Tabellen)
  5. Bereinigt vantage_job_tags Einträge für gelöschte Jobs
  6. Nullifiziert retried_from_id bei verwaisten Retry-Ketten

Schedule:

  • --status=completed --hours=24 → täglich 01:25 UTC
  • --status=failed --days=4 → täglich 01:30 UTC

Location: vendor/houdaslassi/vantage/src/Console/Commands/PruneCommand.php

vantage:retry {run_id} {--force}

Wiederholt einen fehlgeschlagenen Job anhand seiner Vantage-ID.

# Job erneut versuchen
php artisan vantage:retry 12345

# Retry auch ohne gespeichertes Payload erzwingen
php artisan vantage:retry 12345 --force
OptionTypDefaultBeschreibung
run_idintPflichtVantage Job-ID (aus Dashboard oder DB)
--forceflagRetry auch wenn Payload nicht verfügbar

Interne Logik:

  1. Sucht VantageJob Record anhand der ID
  2. Prüft: Status muss failed sein, Job-Klasse muss existieren
  3. Rekonstruiert Job aus gespeichertem payload JSON via Reflection
  4. Stellt Eloquent Models anhand der ID wieder her
  5. Dispatcht Job auf die originale Queue/Connection
  6. Setzt queueMonitorRetryOf Marker für Tracking

Location: vendor/houdaslassi/vantage/src/Console/Commands/RetryCommand.php

vantage:backfill-tags {--days=} {--chunk=1000} {--force}

Befüllt die denormalisierte vantage_job_tags Tabelle aus bestehenden Jobs.

# Alle Tags backfillen
php artisan vantage:backfill-tags --force

# Nur letzte 7 Tage
php artisan vantage:backfill-tags --days=7 --force

# Mit größeren Chunks (Performance)
php artisan vantage:backfill-tags --chunk=5000 --force
OptionTypDefaultBeschreibung
--daysintalleNur Jobs der letzten X Tage backfillen
--chunkint1000Batch-Größe pro Durchlauf
--forceflagBestätigung überspringen (löscht existierende Tags)

Location: vendor/houdaslassi/vantage/src/Console/Commands/BackfillTagsCommand.php

vantage:generate-test-jobs {--count=100} {--success-rate=80} {--tags=} {--queue=default}

Generiert Test-Jobs für Load-Testing.

# 100 Test-Jobs mit 80% Erfolgsrate
php artisan vantage:generate-test-jobs

# 1000 Jobs auf spezifische Queue
php artisan vantage:generate-test-jobs --count=1000 --queue=imports-youtube

# Mit Tags und niedrigerer Erfolgsrate
php artisan vantage:generate-test-jobs --count=500 --success-rate=50 --tags=test,load
OptionTypDefaultBeschreibung
--countint100Anzahl zu generierender Jobs
--success-rateint80Prozent der Jobs die erfolgreich sein sollen (0-100)
--tagsstringKomma-getrennte Tags für Monitoring
--queuestringdefaultZiel-Queue
--duration-minint10Minimale Job-Dauer (ms)
--duration-maxint5000Maximale Job-Dauer (ms)
--batch-sizeint50Jobs pro Dispatch-Batch

Location: vendor/houdaslassi/vantage/src/Console/Commands/GenerateTestJobsCommand.php

Vantage DB-Wartung

Automatische Wartung (Scheduler)

01:22 UTC  vantage:cleanup-stuck --timeout=2     → Stuck "processing" → "failed"
01:25 UTC vantage:prune --status=completed → Completed > 24h löschen
01:30 UTC vantage:prune --status=failed → Failed > 4 Tage löschen

Manuelle Wartung

# Status-Übersicht
psql -d postbox -c "SELECT status, COUNT(*) FROM vantage_jobs GROUP BY status;"

# Tabellengröße prüfen
psql -d postbox -c "
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_stat_user_tables
WHERE relname LIKE 'vantage_%'
ORDER BY pg_total_relation_size(relid) DESC;
"

# Stuck Jobs bereinigen (Preview)
php artisan vantage:cleanup-stuck --timeout=2 --dry-run

# Stuck Jobs bereinigen (Execute)
php artisan vantage:cleanup-stuck --timeout=2

# Alle completed Jobs löschen (nur letzte Stunde behalten)
php artisan vantage:prune --status=completed --hours=1 --force

# Alle failed Jobs löschen
php artisan vantage:prune --status=failed --hours=1 --force

# ALLES löschen (Nuclear Option)
php artisan vantage:prune --hours=1 --force

Notfall: Massenbereinigung bei Millionen Records

Bei extremem Tabellen-Bloat (z.B. 16M+ stuck "processing" Jobs) ist SQL schneller als Artisan:

-- 1. Stuck Processing Jobs direkt löschen
DELETE FROM vantage_jobs
WHERE status = 'processing'
AND started_at < NOW() - INTERVAL '2 hours';

-- 2. Alte Completed Jobs löschen
DELETE FROM vantage_jobs
WHERE status = 'completed'
AND finished_at < NOW() - INTERVAL '24 hours';

-- 3. Alte Failed Jobs löschen
DELETE FROM vantage_jobs
WHERE status = 'failed'
AND finished_at < NOW() - INTERVAL '4 days';

-- 4. Verwaiste Tags bereinigen
DELETE FROM vantage_job_tags
WHERE NOT EXISTS (
SELECT 1 FROM vantage_jobs WHERE vantage_jobs.id = vantage_job_tags.job_id
);

-- 5. Disk-Space freigeben (ACHTUNG: exklusiver Table Lock!)
VACUUM FULL vantage_jobs;
VACUUM FULL vantage_job_tags;

Hintergrund: VACUUM FULL schreibt die gesamte Tabelle neu und gibt den Disk-Space ans Betriebssystem zurück. Ein normales VACUUM markiert nur tote Rows als wiederverwendbar. Bei Millionen gelöschter Rows ist VACUUM FULL deutlich effektiver, blockiert aber die Tabelle während der Ausführung.

Vantage Konfiguration

# Master-Switch
VANTAGE_ENABLED=true

# Retention (Tage) — Standard für vantage:prune wenn kein --days/--hours
VANTAGE_RETENTION_DAYS=3

# Job-Payload speichern (für Retry-Funktionalität)
VANTAGE_STORE_PAYLOAD=true

# Dashboard aktivieren
VANTAGE_ROUTES=true
VANTAGE_ROUTE_PREFIX=vantage

# Auth für Dashboard
VANTAGE_AUTH_ENABLED=true

# Telemetrie
VANTAGE_TELEMETRY_ENABLED=true
VANTAGE_TELEMETRY_SAMPLE_RATE=1.0
VANTAGE_TELEMETRY_CPU=true

# Notifications (optional)
VANTAGE_NOTIFY_EMAIL=admin@example.com
VANTAGE_SLACK_WEBHOOK=https://hooks.slack.com/...

# Logging
VANTAGE_LOGGING_ENABLED=true

# Separate DB-Connection (optional)
VANTAGE_DATABASE_CONNECTION=
Config-KeyEnvDefaultBeschreibung
enabledVANTAGE_ENABLEDtrueMaster-Switch
store_payloadVANTAGE_STORE_PAYLOADtruePayload speichern (für Retry)
retention_daysVANTAGE_RETENTION_DAYS3Default Retention für Prune
tagging.enabledtrueJob-Tagging aktivieren
tagging.auto_tags.queue_nametrueAuto-Tag: Queue-Name
tagging.max_tags_per_job20Max. Tags pro Job
telemetry.sample_rateVANTAGE_TELEMETRY_SAMPLE_RATE1.0Telemetrie-Sampling (0.0-1.0)
redact_keyspassword, token, secret, ...Redacted Keys im Payload

Location: config/vantage.php


Server Dashboard

Custom Livewire-Seite für Echtzeit-Server-Monitoring basierend auf Pulse-Daten.

EigenschaftWert
Route/admin/server
ComponentAdmin\ServerDashboard
ZugriffAdmin-only

Zeigt aktuelle CPU-, RAM- und Disk-Auslastung. Schwellenwerte für Alerts werden in config/server-monitoring.php konfiguriert. Der Command server:check-alerts prüft alle 5 Minuten ob Metriken die Schwellenwerte überschreiten.

Location: app/Livewire/Admin/ServerDashboard.php


Flare (Error Tracking)

Flare (spatie/laravel-flare) ist für Backend-Error-Tracking integriert. Nightwatch (laravel/nightwatch) ergänzt die Observability für Logs, Requests und Jobs.

Features

FeatureBeschreibung
Error TrackingAutomatische PHP-Exception-Erfassung via Flare
ObservabilityRequest-, Job- und Log-Monitoring via Nightwatch
Cron MonitoringScheduled Command Überwachung via CronHeartbeatMonitorService
FLARE_KEY=your-flare-key
NIGHTWATCH_TOKEN=your-token

Location: config/flare.php


Admin Log & Queue

Die Admin-Seite /admin/log-queue kombiniert Collector-Job-Status und Laravel Queue Status in einer Ansicht.

EigenschaftWert
Route/admin/log-queue
ComponentAdmin\LogQueue\Index
Unter-ComponentsQueueMetricsChart

Features:

  • Collector-Job-Status (queued, leased, completed, failed)
  • Laravel Queue Pending/Failed Zähler
  • Queue Metrics Chart (15-Minuten-Snapshots)

Location: app/Livewire/Admin/LogQueue/Index.php, app/Livewire/Admin/LogQueue/QueueMetricsChart.php


Queue Inspection

Pending Jobs nach Queue und Typ

php8.4 artisan tinker --execute="
DB::table('jobs')
->selectRaw(\"queue, payload::json->>'displayName' as job_class, COUNT(*) as count\")
->groupBy('queue', DB::raw(\"payload::json->>'displayName'\"))
->orderByDesc('count')
->get()
->each(fn(\$r) => print(\"\$r->queue: \$r->job_class (\$r->count)\n\"));
"

Pending Jobs mit Alter

php8.4 artisan tinker --execute="
DB::table('jobs')
->selectRaw(\"queue, payload::json->>'displayName' as job_class, MIN(to_timestamp(available_at)) as oldest, COUNT(*) as count\")
->groupBy('queue', DB::raw(\"payload::json->>'displayName'\"))
->orderByDesc('count')
->get()
->each(fn(\$r) => print(\"\$r->queue: \$r->job_class (\$r->count, oldest: \$r->oldest)\n\"));
"

Jobs die mehrere Tage alt sind deuten auf gestoppte oder gecrashte Forge Workers hin.

Vantage Job-Status (vantage_jobs)

# Status-Übersicht
psql -d postbox -c "SELECT status, COUNT(*) FROM vantage_jobs GROUP BY status;"

# Stuck Processing Jobs anzeigen
psql -d postbox -c "
SELECT id, job_class, started_at, NOW() - started_at as age
FROM vantage_jobs
WHERE status = 'processing'
AND started_at < NOW() - INTERVAL '2 hours'
ORDER BY started_at ASC
LIMIT 20;
"

# Failed Jobs der letzten 24h mit Exception
psql -d postbox -c "
SELECT id, job_class, exception_class, finished_at
FROM vantage_jobs
WHERE status = 'failed'
AND finished_at > NOW() - INTERVAL '24 hours'
ORDER BY finished_at DESC
LIMIT 20;
"

Fehlgeschlagene Jobs (Laravel Standard)

# Alle fehlgeschlagenen Jobs auflisten
php artisan queue:failed

# Fehlgeschlagene Jobs nach Queue filtern
php artisan queue:failed --queue=ai-detection

# Alle erneut versuchen
php artisan queue:retry all

# Einzelnen Job erneut versuchen
php artisan queue:retry <uuid>

# Alte fehlgeschlagene Jobs löschen (> 7 Tage)
php artisan queue:prune-failed --hours=168

Hinweis: Postbox nutzt Vantage — Failed Jobs landen in vantage_jobs, nicht in der Standard-failed_jobs-Tabelle. Für Retry/Prune daher bevorzugt vantage:retry und vantage:prune verwenden.


DB-Tabellen-Wartung (PostgreSQL)

Tabellengröße prüfen

psql -d postbox -c "
SELECT relname,
pg_size_pretty(pg_total_relation_size(relid)) as total_size,
pg_size_pretty(pg_relation_size(relid)) as data_size,
pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) as index_size,
n_live_tup as live_rows,
n_dead_tup as dead_rows
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;
"

Dead Tuples und Bloat

# Tabellen mit vielen Dead Tuples (brauchen VACUUM)
psql -d postbox -c "
SELECT relname, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 10000
ORDER BY n_dead_tup DESC;
"

# Standard VACUUM (non-blocking, markiert Platz als wiederverwendbar)
VACUUM ANALYZE vantage_jobs;

# VACUUM FULL (blocking, gibt Platz ans OS zurück — nur bei großem Bloat)
VACUUM FULL vantage_jobs;

Monitoring-Tabellen im Überblick

TabelleAutomatische BereinigungRetentionWartungs-Command
vantage_jobsJa (täglich 01:22-01:30)3 Tage (completed: 24h)vantage:prune, vantage:cleanup-stuck
vantage_job_tagsJa (mit vantage:prune)wie vantage_jobsvantage:prune
pulse_entriesJa (täglich 01:20)7 Tagepulse:purge
pulse_aggregatesJa (täglich 01:20)7 Tagepulse:purge
pulse_valuesJa (Lottery)7 Tagepulse:purge
failed_jobsJa (täglich 01:15)7 Tagequeue:prune-failed
db_monitoring_snapshotsJa (täglich 02:15)30 Tagedb:snapshot --prune
db_slow_query_logJa (täglich 02:15)14 Tagedb:snapshot --prune
error_page_logsJa (wöchentlich So 03:00)365 Tageerror-monitor:prune
error_page_daily_statsJa (wöchentlich So 03:00)365 Tageerror-monitor:prune

Reverb Test Page

Zum Testen der WebSocket-Verbindung steht eine dedizierte Test-Seite bereit:

EigenschaftWert
Route/admin/reverb-test
ComponentAdmin\ReverbTest\Index
EventTestBroadcast

Die Seite sendet Test-Events über Reverb und zeigt empfangene Broadcasts in Echtzeit an. Hilfreich um die WebSocket-Verbindung, Nginx-Proxy und Echo-Konfiguration zu verifizieren.

Location: app/Livewire/Admin/ReverbTest/Index.php, app/Events/TestBroadcast.php


Health Endpoint

Der System-Health-Endpoint prüft alle kritischen Subsysteme:

EigenschaftWert
RouteGET /up_system
AuthX-Health-Token Header
ControllerSystemHealthController
curl -H "X-Health-Token: <token>" https://app.postbox.so/up_system

Prüft:

  • Datenbank-Verbindung
  • Queue-Status (Pending, Failed)
  • Collector-Heartbeat
  • Cron-Heartbeats (Instagram Scrape, YouTube Scrape, YouTube Video Sync, Explore Metrics, Scores, Google API, Queue Metrics, Server Alerts, DB Monitoring, Cross-Platform, Tag Cache)

Cron-Heartbeats werden nach jeder erfolgreichen Ausführung via Cache::put() gesetzt. Wenn ein Heartbeat älter als max_minutes ist, meldet der Endpoint CRON_{NAME} FAILED.

HEALTH_TOKEN=<32-byte-hex>
HEALTH_FAILED_JOBS_THRESHOLD=100
HEALTH_COLLECTOR_HEARTBEAT_MINUTES=30

Location: app/Http/Controllers/SystemHealthController.php, config/postbox.php (health-Sektion), routes/web.php


Nightwatch

Laravel Nightwatch sammelt Telemetriedaten (Exceptions, Commands, Requests, Scheduled Tasks):

NIGHTWATCH_TOKEN=<token>
NIGHTWATCH_EXCEPTION_SAMPLE_RATE=1.0
NIGHTWATCH_COMMAND_SAMPLE_RATE=0.1
NIGHTWATCH_REQUEST_SAMPLE_RATE=0.1

Location: config/nightwatch.php