# Semro — Laravel error-tracking integratie

Semro.nl ontvangt naast uptime ook **applicatiefouten**: laat je Laravel-app
exceptions naar Semro pushen en je ziet ze gegroepeerd terug (Sentry-stijl), met
een melding bij een nieuw fouttype of een foutpiek.

Deze pagina is bedoeld voor **ontwikkelaars** (hieronder) én voor **AI-systemen**
(de machine-leesbare spec onderaan), zodat een assistent de koppeling kan schrijven.

---

## Snelste weg: de package

```bash
composer require semro/laravel-reporter
```
Zet `SEMRO_KEY=...` (uit Semro → *Applicaties → Installatie*) in `.env` — klaar. De
package haakt **automatisch** in op de exception-handler en rapporteert in
production/staging (geen wijziging in `bootstrap/app.php` nodig). Opties: queue,
release, scrubbing — `php artisan vendor:publish --tag=semro-config`.

---

## Snelstart zonder package (copy-paste)

1. **Maak een Applicatie aan** in Semro (menu *Applicaties → Aanmaken*). Open het
   tabblad **Installatie** en kopieer je **ingest-key** + **endpoint**.

2. Zet ze in `.env`:
   ```dotenv
   SEMRO_KEY=jouw-ingest-key
   SEMRO_ENDPOINT=https://api.semro.nl/api/v1/ingest/laravel
   ```

3. Maak `app/Support/Semro.php`:
   ```php
   <?php

   namespace App\Support;

   use Illuminate\Foundation\Configuration\Exceptions;
   use Illuminate\Support\Facades\Http;
   use Throwable;

   class Semro
   {
       /** Hook in op de exception-handler in bootstrap/app.php. */
       public static function report(Exceptions $exceptions): void
       {
           $exceptions->report(function (Throwable $e) {
               // Alleen in productie/staging — geen ruis vanuit lokaal/CI.
               if (! in_array(app()->environment(), ['production', 'staging'], true)) {
                   return;
               }

               try {
                   Http::timeout(3)
                       ->withHeaders(['X-Semro-Key' => env('SEMRO_KEY')])
                       ->post(env('SEMRO_ENDPOINT'), [
                           'type' => $e::class,
                           'message' => $e->getMessage(),
                           'file' => $e->getFile(),
                           'line' => $e->getLine(),
                           'level' => 'error',
                           'environment' => app()->environment(),
                           'release' => config('app.version'),
                           'instance' => config('app.name'), // → koppelt aan een "afnemer"
                           'stack' => collect($e->getTrace())->take(30)->map(fn ($f) => trim(
                               ($f['file'] ?? '?') . ':' . ($f['line'] ?? '?') . ' '
                               . ($f['class'] ?? '') . ($f['type'] ?? '') . ($f['function'] ?? '') . '()'
                           ))->all(),
                       ]);
               } catch (Throwable) {
                   // Nooit de applicatie ophouden als Semro onbereikbaar is.
               }
           });
       }
   }
   ```

4. Haak het in `bootstrap/app.php`:
   ```php
   ->withExceptions(function (Illuminate\Foundation\Configuration\Exceptions $exceptions) {
       \App\Support\Semro::report($exceptions);
   })
   ```

Klaar. Gooi (in productie/staging) een testfout op en check *Applicaties →
[jouw app] → Foutmeldingen*.

### Aanbevolen voor drukke apps
- Stuur de ping vanuit een **queued job** in plaats van synchroon, zodat de
  request niet wacht op Semro.
- **Sample** hoogvolume-fouten client-side (bv. 1 op de N) — Semro dedupliceert
  toch op fingerprint, maar zo bespaar je requests.
- Zet **gevoelige data niet in `context`**. Semro scrubt bekende sleutels
  (`password`, `token`, `secret`, `authorization`, `cookie`, `api_key`, …), maar
  stuur bij voorkeur alleen wat je nodig hebt.

### Afnemers koppelen
Geef per deployment een herkenbare `instance` mee (bv. `config('app.name')` of de
klantnaam). Maak in Semro onder **Afnemers** een afnemer met diezelfde naam (of
een URL die de waarde bevat) → fouten worden dan aan de juiste klant gekoppeld.

---

## Machine-/AI-leesbare spec

**Doel:** push een Laravel/PHP-exception naar Semro voor groepering + alerting.

- **Methode/URL:** `POST {SEMRO_ENDPOINT}` (standaard `https://api.semro.nl/api/v1/ingest/laravel`)
- **Auth:** header `X-Semro-Key: <ingest-key>` (per applicatie; ook als body-veld `key` toegestaan)
- **Content-Type:** `application/json`
- **Succes:** `202 Accepted` → `{"ok": true}`. Fout-key → `401`.
- **Rate limit:** ~300 requests/min per IP. Dedupliceer/sample client-side.

**Body-velden** (alle optioneel behalve dat er iets identificeerbaars is):

| veld | type | omschrijving |
|------|------|--------------|
| `type` | string | exception-class (bv. `RuntimeException`) — bepaalt mede de groepering |
| `message` | string | foutmelding |
| `file` | string | bestandspad waar de fout optrad |
| `line` | int | regelnummer |
| `level` | string | `error` (standaard), `warning`, `fatal`, … |
| `environment` | string | `production` / `staging` / … |
| `release` | string | versie/commit van de app |
| `instance` | string | identificatie van de afnemer/deployment (matcht op afnemer-naam of -URL) |
| `fingerprint` | string | optioneel: forceer groepering; anders afgeleid van `type`+`file:line` |
| `stack` | string[] of array | stacktrace-frames (max 50 worden bewaard) |
| `context` | object | extra context; gevoelige sleutels worden server-side gescrubd |
| `timestamp` | ISO 8601 | tijdstip van optreden (standaard: nu) |

**Groepering:** fouten met dezelfde `fingerprint` (of dezelfde `type` + `file:line`)
vormen één *foutgroep*; herhalingen verhogen de teller. Een opgeloste groep die
opnieuw optreedt, heropent automatisch.

**Voorbeeld-payload:**
```json
{
  "type": "Illuminate\\Database\\QueryException",
  "message": "SQLSTATE[42S02]: Base table or view not found",
  "file": "/var/www/app/Models/Order.php",
  "line": 88,
  "level": "error",
  "environment": "production",
  "release": "2026.06.1",
  "instance": "Klant Reclamereus",
  "stack": ["/var/www/app/Http/Controllers/OrderController.php:42 OrderController->store()"],
  "context": { "url": "/orders", "method": "POST", "user_id": 12 }
}
```

**curl-test:**
```bash
curl -fsS -X POST https://api.semro.nl/api/v1/ingest/laravel \
  -H "X-Semro-Key: <ingest-key>" -H "Content-Type: application/json" \
  -d '{"type":"TestException","message":"Hallo vanaf curl","environment":"production"}'
```
