# Laravel-Exceptions als Push aufs Handy

> Laravel-Exceptions mit Honk::reportable() als Push: einer pro Fehler, pro Exception gedrosselt, dazu Scheduler-Alarme und Honk::fake() für Tests.

Source: https://honk-me.app/de/anleitungen/laravel-exceptions-push

Laravel meldet Exceptions schon selbst: ins Log und, mit einem Log-Kanal wie Slack, in einen Chat. Ärgerlich wird es, wenn dieselbe Exception 300-mal in einer Minute auftritt. Du willst einmal davon erfahren, mit genug Details zum Handeln, und dann Ruhe haben, bis etwas Neues kaputtgeht.

Diese Anleitung richtet das mit dem Paket `honk-me/honk-me` und Laravel 13 ein. Exceptions werden zu Pushes, Wiederholungen desselben Fehlers landen in einer Gruppe, und geplante Tasks melden, wenn sie fehlschlagen und wenn sie wieder laufen.

## Voraussetzungen

- Laravel 13 auf PHP 8.3 oder neuer.
- Ein Honk-Konto (vorerst nur auf Einladung: [Zugang anfragen](https://honk-me.app/de/zugang-anfragen)) und ein Projekt mit Ingest-Schlüssel: Öffne in der Web-App das Projekt, dann **Schlüssel**.

## 1. Das Paket installieren

```sh
composer require honk-me/honk-me
# publishes config/honk.php and adds HONK_URL and HONK_KEY to .env.example
php artisan honk:install
```

Trag Server-URL und Schlüssel in `.env` ein. Committe den Schlüssel nie, in `.env.example` gehören nur leere Platzhalter.

```dotenv
HONK_URL=https://honk-me.app
HONK_KEY=honk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Dann prüfst du die Verbindung. `honk:test` sendet ein Leichtes Hupen und gibt die ID der Nachricht aus:

```sh
php artisan honk:test         # sends a light honk and prints the message id
php artisan about --only=honk # URL, key prefix (never the secret), queue, Context metadata
```

## 2. Exceptions melden

Eine Zeile in `bootstrap/app.php`:

```php
// bootstrap/app.php
use HonkMe\Laravel\Facades\Honk;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(Honk::reportable());
})
```

Ab jetzt wird jede gemeldete Exception als Langes Hupen gesendet, und zwar als *Problem*. Drei Details sorgen für Ruhe:

- **Eine Gruppe pro Stelle im Code.** Der Gruppenschlüssel ist `exceptions/<Class>@<file>:<line>`. 300 identische Exceptions sind also ein Vorfall mit Zähler, eine andere Exception ist ein anderer Vorfall.
- **Höchstens ein Hupen pro Gruppe alle 5 Minuten.** Die Drosselung läuft über deinen Cache, die 299 Wiederholungen verlassen deinen Server also gar nicht erst.
- **Gesendet nach der Antwort.** Der Standardmodus `defer` sendet erst, wenn die HTTP-Antwort raus ist. Ein langsames Netzwerk bremst also nie eine Seite aus.

Fehlt `HONK_URL` oder `HONK_KEY`, passiert nichts. So bleiben lokale Entwicklung und CI still. Eine Exception wirft das Paket nie.

Pro App kannst du eine andere Stufe oder ein anderes Zeitfenster wählen, zum Beispiel Dauerhupen mit einer Minute Drosselung für einen Zahlungsdienst:

```php
$exceptions->report(Honk::reportable(severity: 'blast', throttleSeconds: 60));
```

Alles Weitere steht in `config/honk.php`. Dort überspringst du erwartbare Exceptions, etwa eine abgelehnte Zahlung, die die Oberfläche schon erklärt, und legst fest, wie gesendet wird:

```php
// config/honk.php (published by php artisan honk:install)
'exceptions' => [
    'enabled' => (bool) env('HONK_EXCEPTIONS', true),
    'severity' => 'long',
    'channel' => 'exceptions',
    'throttle' => 300,          // seconds per group
    'mode' => 'defer',          // defer (after the response), sync or queue
    'ignore' => [
        \App\Exceptions\PaymentDeclined::class,   // expected, already handled in the UI
    ],
],
```

Exception-Meldungen werden unverändert gesendet und bei Bedarf gekürzt. Schreib keine personenbezogenen Daten hinein, sonst landen sie in einem Push.

## 3. Sofort, nach der Antwort oder über die Queue

Dieselbe Wahl hast du für alles, was du selbst sendest:

```php
use HonkMe\Laravel\Facades\Honk;
use HonkMe\Message;

Honk::defer()->beep('New order', "{$order->email} paid {$order->total} €");   // after the response
Honk::queue(Message::make('Imported 1 204 rows')->beep()->channel('imports'));   // a queued job
Honk::loud('Disk 91%', '/var on app-01', ['groupKey' => 'disk/app-01/var']);     // right now
```

- `Honk::defer()` läuft in diesem Prozess nach der Antwort. Ein Worker ist nicht nötig. Stirbt der Prozess vorher, geht dieses Hupen verloren. Für die meisten Alarme ist das in Ordnung.
- `Honk::queue()` läuft in einem Queue-Worker mit Wiederholungen und Backoff und übersteht Neustarts. Nimm es für alles, was ankommen muss.
- Die einfachen Helfer senden sofort, mit Wiederholungen. Das passt zu Commands, Jobs und dem Scheduler.

`defer()` und `queue()` prüfen die Nachricht sofort. Ein fehlerhaftes Feld fällt also in deinem Code auf, nicht erst später in einem Worker.

## 4. Geplante Tasks

```php
// routes/console.php
Schedule::command('backup:run')->daily()->honkOnFailure();          // problem on failure, recovery once it works again
Schedule::command('reports:send')->hourly()->honkOnSuccess();       // a beep after every successful run
```

`honkOnFailure()` sendet ein Problem mit dem Exit-Code und dem Ende der Ausgabe, gruppiert pro Task. Beim nächsten erfolgreichen Lauf folgt eine Entwarnung, aber nur nach einem Fehlschlag. Ein Task, der sauber läuft, meldet sich also nie. `honkOnSuccess()` sendet nach jedem erfolgreichen Lauf ein Tüt-tüt. Das ist für Berichte gedacht, die du wirklich sehen willst.

## 5. Testen

`Honk::fake()` zeichnet auf, was gesendet oder in die Queue gestellt würde, und schickt nichts übers Netzwerk. Es validiert wie der echte Client, eine ungültige Nachricht lässt den Test also trotzdem scheitern:

```php
use HonkMe\Laravel\Facades\Honk;
use HonkMe\Message;
use HonkMe\Severity;

Honk::fake();

$this->post(route('quote.store'), $data);

Honk::assertSent(fn (Message $m) => $m->groupKey === 'requests/1' && $m->severity === Severity::Light);
Honk::assertSentTimes(1);
Honk::assertNotSent(fn (Message $m) => $m->severity === Severity::Blast);
Honk::assertQueued(fn (Message $m, string $idempotencyKey) => $m->channel === 'imports');
Honk::assertNothingSent(); // also assertNothingQueued(), assertNothingOutgoing()
```

## Was Honk ergänzt

Dein Log bekommt weiterhin jede Exception. Dazu kommt von Honk ein Push pro neuem Fehler, auf iPhone, Apple Watch oder im Browser. Wiederholungen werden mit Zähler gebündelt und landen im selben Posteingang wie deine Cronjobs und Server. Willst du einige davon anders behandeln, ohne den Code anzufassen, reicht eine Regel im Projekt: Schick zum Beispiel `exceptions/` aus einer lauten Integration in die Zusammenfassung.
