# GlitchTip Backend Error Tracking Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Remonter les erreurs backend Symfony de SIRH vers GlitchTip (SDK Sentry), en prod uniquement, inerte sans DSN, le transport réseau étant assuré par Tailscale (infra, hors repo). **Architecture:** On installe `sentry/sentry-symfony`, enregistré **prod-only** dans `bundles.php`. Un fichier `config/packages/sentry.yaml` (bloc `when@prod`) configure le SDK (DSN runtime `%env(SENTRY_DSN)%`, release = `%app.version%`, 4xx ignorés, pas de tracing) et déclare un handler Monolog qui remonte les logs `ERROR+` comme Issues. Sans `SENTRY_DSN`, le SDK est un no-op. Aucun changement frontend ni CI. **Tech Stack:** PHP 8.4, Symfony 8, API Platform, `sentry/sentry-symfony` ^5.10, Monolog, Docker (prod), Tailscale (transport infra). ## Global Constraints - **Prod only** : `SentryBundle` enregistré `['prod' => true]`, config sous `when@prod`. Dev/test : zéro impact. - **Inerte sans DSN** : `env(SENTRY_DSN)` défaut `''` → SDK no-op si vide. - **DSN runtime** : lu depuis l'env_file serveur, jamais baké dans l'image, jamais committé. - **Version dépendance** : `sentry/sentry-symfony:^5.10` (identique au projet Lesstime, même stack). - **Release** : `%app.version%` (déjà fourni par `config/version.yaml`, ex. `0.1.127`). - **Pas de tracing/APM** : `traces_sample_rate: 0.0`. - **Format commit** (hook SIRH) : Conventional Commits FR, ex. `feat : …`, `docs : …`. Terminer par la ligne `Co-Authored-By: Claude Opus 4.8 (1M context) `. - **Pas de modif frontend** (`frontend/**`), **pas de modif CI** (`.gitea/workflows/**`), **pas de modif Dockerfile** (DSN runtime ; CA HTTPS hors périmètre). - **Branche** : `feat/glitchtip-backend-error-tracking` (déjà créée). --- ### Task 1: Câblage backend Sentry/GlitchTip (inerte sans DSN) **Files:** - Modify: `composer.json`, `composer.lock` (via `composer require`) - Modify: `config/bundles.php` - Create: `config/packages/sentry.yaml` - Modify: `config/packages/monolog.yaml` - Modify: `.env` **Interfaces:** - Consumes: paramètre `%app.version%` (`config/version.yaml`), variable d'env `SENTRY_DSN`, `%env(APP_ENV)%`. - Produces: service `Sentry\Monolog\Handler` (handler Monolog niveau `Error`), variable d'env attendue `SENTRY_DSN` (runtime). Aucune API PHP consommée par d'autres tâches. - [ ] **Step 1: Installer la dépendance** Depuis le container PHP (`make shell` ou `docker compose exec php sh`) : ```bash composer require sentry/sentry-symfony:^5.10 ``` Expected : `composer.json` + `composer.lock` modifiés, paquet `sentry/sentry-symfony` 5.x installé. - [ ] **Step 2: Forcer l'enregistrement prod-only du bundle** Le recipe Flex peut écrire `['all' => true]` dans `config/bundles.php`. Corriger en prod-only. Contenu attendu de la ligne ajoutée : ```php use Sentry\SentryBundle\SentryBundle; // ... dans le tableau de retour : SentryBundle::class => ['prod' => true], ``` Si Flex a aussi généré un `config/packages/sentry.yaml`, il sera remplacé à l'étape suivante. - [ ] **Step 3: Écrire `config/packages/sentry.yaml`** Remplacer intégralement le contenu du fichier par : ```yaml # Error tracking → GlitchTip (compatible SDK Sentry). # Actif uniquement en prod (bundle enregistré prod-only dans bundles.php). # Si SENTRY_DSN est vide/non défini, le SDK est inerte (rien n'est envoyé). when@prod: parameters: env(SENTRY_DSN): '' sentry: dsn: '%env(SENTRY_DSN)%' # Capture des erreurs fatales PHP via le handler. On DÉSACTIVE le listener # kernel pour éviter les doublons avec le handler Monolog (les exceptions du # kernel sont déjà logguées par Symfony → remontées via Monolog). register_error_listener: false register_error_handler: true options: environment: '%env(APP_ENV)%' release: '%app.version%' traces_sample_rate: 0.0 ignore_exceptions: - Symfony\Component\HttpKernel\Exception\NotFoundHttpException - Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException - Symfony\Component\Security\Core\Exception\AccessDeniedException # Handler Monolog → Sentry : remonte les logs niveau ERROR+ comme Issues GlitchTip. services: Sentry\Monolog\Handler: arguments: $hub: '@Sentry\State\HubInterface' $level: !php/const Monolog\Level::Error $bubble: true ``` - [ ] **Step 4: Brancher le handler Monolog en prod** Dans `config/packages/monolog.yaml`, sous `when@prod:` → `monolog:` → `handlers:`, ajouter le handler `sentry` (à côté des handlers `cron`, `main`, `deprecation` existants, sans les modifier) : ```yaml # Remonte les logs ERROR+ vers GlitchTip en tant qu'Issues (service défini # dans sentry.yaml). Envoi immédiat, indépendamment des handlers fichier. sentry: type: service id: Sentry\Monolog\Handler channels: ["!event", "!doctrine", "!deprecation", "!cron"] ``` - [ ] **Step 5: Documenter la variable dans `.env`** Ajouter à la fin de `.env` le bloc commenté (valeur réelle injectée côté serveur uniquement) : ```env ###> sentry/sentry-symfony ### # Error tracking backend → GlitchTip (projet "sirh-api"). Prod only, vide => inerte. # À définir dans l'env_file du serveur, PAS ici. Format : # SENTRY_DSN=http://@:/ # SENTRY_DSN= ###< sentry/sentry-symfony ### ``` > Si Flex a déjà inséré un bloc `###> sentry/sentry-symfony ###`, le remplacer par celui-ci (ne pas dupliquer). - [ ] **Step 6: Vérifier que la conf prod compile (DSN vide → inerte)** Depuis le container : ```bash APP_ENV=prod php bin/console cache:clear --no-warmup APP_ENV=prod php bin/console debug:container --env=prod 2>/dev/null | grep -i sentry | head ``` Expected : - `cache:clear` : `[OK] Cleared the cache.` (aucune erreur de config/DI). - `debug:container` : au moins une ligne `Sentry\...` (services chargés). DSN vide → SDK inerte, aucun envoi. - [ ] **Step 7: Vérifier que le dev n'est pas impacté** ```bash php bin/console cache:clear # APP_ENV=dev par défaut make test ``` Expected : cache dev OK ; suite de tests verte (le bundle n'est pas chargé hors prod). - [ ] **Step 8: Commit** ```bash git add composer.json composer.lock config/bundles.php config/packages/sentry.yaml config/packages/monolog.yaml .env git commit -m "$(cat <<'EOF' feat : error tracking backend vers GlitchTip (prod-only, inerte sans DSN) Ajout du SDK sentry/sentry-symfony enregistré prod-only, config sentry.yaml (DSN runtime, release app.version, 4xx ignorés, pas de tracing) et handler Monolog ERROR+. Sans SENTRY_DSN le SDK est no-op. Transport réseau via Tailscale (infra, hors repo). Co-Authored-By: Claude Opus 4.8 (1M context) EOF )" ``` --- ### Task 2: Documentation (doc/ + CLAUDE.md) **Files:** - Create: `doc/error-tracking.md` - Modify: `CLAUDE.md` **Interfaces:** - Consumes: néant (documentation). - Produces: néant. - [ ] **Step 1: Créer `doc/error-tracking.md`** ```markdown # Error tracking (GlitchTip) Les erreurs **backend** (Symfony) sont remontées vers **GlitchTip** (instance interne MALIO, compatible SDK Sentry), org `malio`, projet `sirh-api`. **Prod uniquement**, **inerte sans DSN**. > Frontend hors périmètre (les erreurs front partent du navigateur RH ; ajout futur possible via > un proxy nginx `/ingest`). ## Contrainte réseau & transport GlitchTip est sur le réseau interne (bloqué par Sophos). SIRH tourne sur un VPS OVH public. Le container PHP joint GlitchTip via un **tunnel Tailscale** monté sur le host de prod. ## Fichiers concernés | Fichier | Rôle | |---|---| | `config/packages/sentry.yaml` | conf backend (prod-only, DSN runtime, 4xx ignorés, release = `app.version`, handler Monolog ERROR+) | | `config/bundles.php` | `SentryBundle` enregistré `['prod' => true]` | | `config/packages/monolog.yaml` | handler `sentry` (service) en `when@prod` | | `.env` | bloc documenté `SENTRY_DSN` (vide → inerte) | ## Activation (runbook) 1. **Tailscale sur le host prod OVH** : \`\`\`bash curl -fsSL https://tailscale.com/install.sh | sh sudo tailscale up # ou --authkey tskey-auth-XXXX (headless) tailscale status && tailscale ip -4 \`\`\` 2. **Vérifier l'accès à GlitchTip** depuis le host : \`\`\`bash tailscale ping curl -sS -o /dev/null -w "%{http_code}\n" http://:/_health/ \`\`\` 3. **Routage container → tailnet** : pointer `SENTRY_DSN` sur l'**IP tailnet** de GlitchTip (le container ne résout pas MagicDNS). Repli si non routé : sidecar `tailscale/tailscale` + `network_mode: service:tailscale`. 4. **Créer le projet GlitchTip** `sirh-api` (plateforme `php-symfony`) dans l'org `malio`, récupérer le DSN (Settings → Client Keys). 5. **Injecter le DSN** dans l'env_file serveur (pas dans l'image), puis redéployer : \`\`\`env SENTRY_DSN=http://@100.x.y.z:/ \`\`\` \`\`\`bash docker compose up -d docker compose exec php php bin/console cache:clear --env=prod \`\`\` ## CA HTTPS (conditionnel) Uniquement si le DSN cible l'HTTPS interne `logs.malio-dev.fr` (cert auto-signé) : baker la CA racine MALIO dans `deploy/docker/Dockerfile.prod` (stage production). Recommandé : préférer l'endpoint **HTTP** via le tailnet (déjà chiffré par WireGuard) → pas de CA. Design détaillé : `docs/superpowers/specs/2026-06-28-glitchtip-backend-error-tracking-design.md`. ``` - [ ] **Step 2: Ajouter une section à `CLAUDE.md`** Insérer une section (après « ## Notifications ») : ```markdown ## Error tracking (GlitchTip) - Backend Symfony → GlitchTip (SDK Sentry), org `malio`, projet `sirh-api`. **Prod only**, **inerte sans `SENTRY_DSN`**. - Config : `config/packages/sentry.yaml` (DSN runtime `%env(SENTRY_DSN)%`, release `%app.version%`, 4xx ignorés, `traces_sample_rate: 0`, handler Monolog ERROR+) ; `SentryBundle` enregistré `['prod' => true]` (`config/bundles.php`) ; handler `sentry` en `when@prod` (`config/packages/monolog.yaml`). DSN runtime via l'env_file serveur, jamais committé/baké. - **Transport réseau** : GlitchTip est interne (bloqué Sophos), SIRH sur VPS OVH → tunnel **Tailscale** sur le host prod. Frontend hors périmètre (ajout futur via proxy nginx `/ingest`). - Doc : `doc/error-tracking.md`. ``` - [ ] **Step 3: Commit** ```bash git add doc/error-tracking.md CLAUDE.md git commit -m "$(cat <<'EOF' docs : documentation error tracking GlitchTip (doc/ + CLAUDE.md) Co-Authored-By: Claude Opus 4.8 (1M context) EOF )" ``` --- ## Notes d'exécution - **TDD non applicable** ici : ce sont des changements de **configuration** (pas de logique métier unitairement testable). La vérification se fait par commandes console (`cache:clear` prod, `debug:container`) + non-régression de la suite existante (`make test`). Ne pas ajouter de test PHPUnit factice. - Les valeurs `<…>` du runbook (IP tailnet, port GlitchTip, clé DSN) sont renseignées au **déploiement**, hors repo. - Après les deux tasks : pousser la branche et ouvrir la MR (`git push -u origin feat/glitchtip-backend-error-tracking`).