From 417f342fc0d47a46ec3110d0d29005cc12a0c618 Mon Sep 17 00:00:00 2001 From: matthieu Date: Sun, 28 Jun 2026 13:10:54 +0200 Subject: [PATCH] docs : spec error tracking backend GlitchTip via Tailscale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design d'intégration du SDK Sentry/GlitchTip côté backend Symfony (prod only, inerte sans DSN), avec runbook Tailscale complet (install, routage container→tailnet, création projet, injection DSN). Backend-only ; front et CA HTTPS interne hors périmètre. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...glitchtip-backend-error-tracking-design.md | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-28-glitchtip-backend-error-tracking-design.md diff --git a/docs/superpowers/specs/2026-06-28-glitchtip-backend-error-tracking-design.md b/docs/superpowers/specs/2026-06-28-glitchtip-backend-error-tracking-design.md new file mode 100644 index 0000000..aafcf02 --- /dev/null +++ b/docs/superpowers/specs/2026-06-28-glitchtip-backend-error-tracking-design.md @@ -0,0 +1,223 @@ +# Error tracking backend SIRH → GlitchTip (via Tailscale) + +> Date : 2026-06-28 +> Périmètre : **backend Symfony uniquement**, **prod only**, transport **Tailscale**. +> Référence pattern : projet **Lesstime** (`config/packages/sentry.yaml`, `README.md` § Error tracking). + +## 1. Contexte & contrainte + +GlitchTip (instance auto-hébergée MALIO, compatible SDK Sentry) vit sur le **réseau interne**, +bloqué par **Sophos**, sur le domaine interne `logs.malio-dev.fr` (DNS local, CA auto-signée). +SIRH tourne sur un **VPS OVH** (Internet public) → le container PHP ne peut pas joindre l'interne. + +**Décision** : on monte un **tunnel Tailscale** sur le host de prod OVH. Le container PHP atteint +GlitchTip par le tailnet. **Backend seulement** pour l'instant (les erreurs front partent du +navigateur RH, hors périmètre — pourra être ajouté plus tard via un proxy nginx `/ingest`). + +Flux retenu : + +| Flux | Source | Chemin vers GlitchTip | +|---|---|---| +| **Backend** Symfony | container PHP sur le VPS OVH | → host Tailscale → tailnet → GlitchTip ✅ | +| Frontend SPA | navigateur RH | **hors périmètre** (pas de SDK front) | + +## 2. Principes + +- **Prod only** : le bundle n'est enregistré que pour `prod`. En dev/test : zéro impact. +- **Inerte sans DSN** : si `SENTRY_DSN` est vide/absent, le SDK ne fait rien (no-op). +- **Runtime DSN** : le DSN est lu à l'exécution depuis l'`env_file` du serveur, jamais baké dans + l'image (pas de secret dans le repo ni dans l'image Docker). +- **Pas d'APM/tracing** (`traces_sample_rate: 0`) : on ne remonte que les erreurs. +- **Bruit filtré** : 4xx HTTP (404/405/AccessDenied) ignorés ; channels `event/doctrine/ + deprecation/cron` exclus du handler Monolog. + +## 3. Changements de code (repo SIRH) + +### 3.1 Dépendance +```bash +make shell # ou docker exec dans le container php +composer require sentry/sentry-symfony:^5.10 +``` +Met à jour `composer.json` + `composer.lock`. Version identique à Lesstime (stack PHP 8.4 / +Symfony 8 commune). + +### 3.2 `config/bundles.php` +Ajouter l'enregistrement **prod-only** : +```php +use Sentry\SentryBundle\SentryBundle; +// ... +SentryBundle::class => ['prod' => true], +``` +> `composer require` ajoute généralement la ligne `['all' => true]` via Flex — la corriger en +> `['prod' => true]`. + +### 3.3 `config/packages/sentry.yaml` *(nouveau fichier)* +```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)%' + # On capture les erreurs fatales PHP via le handler, mais 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 +``` +> `release: '%app.version%'` réutilise `config/version.yaml` (`app.version`, ex. `0.1.127`). + +### 3.4 `config/packages/monolog.yaml` +Dans le bloc `when@prod.monolog.handlers`, ajouter : +```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"] +``` +> Les autres handlers (`main`, `cron`, `deprecation`) restent inchangés. + +### 3.5 `.env` (+ `.env.example` si présent) +Bloc documenté (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 ### +``` + +### 3.6 CI / Dockerfile +**Aucun changement requis** pour le backend : le DSN est runtime (env_file). La CI +(`.gitea/workflows/build-docker.yml`) ne build/push que l'image — rien à toucher. + +**CA TLS (conditionnel)** — voir §4.4 : nécessaire **uniquement** si le DSN cible l'HTTPS interne +`logs.malio-dev.fr`. Si on tape l'endpoint **HTTP** GlitchTip via le tailnet (recommandé), pas de +modif Dockerfile. + +## 4. Runbook infra (hors repo) — toutes les étapes & commandes + +### 4.1 Installer Tailscale sur le host de prod OVH +```bash +# Sur le serveur OVH (Debian/Ubuntu), en root/sudo : +curl -fsSL https://tailscale.com/install.sh | sh + +# Jointure du tailnet (ouvre une URL d'auth, ou utiliser une auth key headless) : +sudo tailscale up +# --- headless (CI/scripté) : +# sudo tailscale up --authkey tskey-auth-XXXXXXXXXXXX + +# Vérifier l'état et récupérer l'IP tailnet du serveur : +tailscale status +tailscale ip -4 +``` + +> **Si GlitchTip est sur une autre machine du tailnet** : noter son IP tailnet (`100.x.y.z`) ou son +> nom MagicDNS. **Si GlitchTip est derrière un subnet router** (LAN interne non tailnet) : ajouter +> `--accept-routes` au `tailscale up`, et s'assurer qu'un subnet router annonce le sous-réseau. + +### 4.2 Vérifier la connectivité host → GlitchTip via le tailnet +```bash +# Depuis le host OVH : +tailscale ping +curl -sS -o /dev/null -w "%{http_code}\n" http://:/_health/ # → 200 attendu +``` + +### 4.3 Rendre le tailnet joignable depuis le container PHP + +Le container PHP est sur le réseau bridge Docker, pas directement sur le tailnet. Deux options : + +**Option A — Host Tailscale + IP tailnet dans le DSN (recommandé, simple).** +L'egress du container est masqueradé par le host, qui route `100.x.y.z` via `tailscale0`. +→ Pointer `SENTRY_DSN` directement sur l'**IP tailnet** de GlitchTip (pas MagicDNS, que le +container ne résout pas). Optionnellement figer le nom via `extra_hosts` dans le compose : +```yaml + # docker-compose.yml (serveur) + extra_hosts: + - "glitchtip.tailnet:100.x.y.z" +``` +Prérequis : IP forwarding actif sur le host (`net.ipv4.ip_forward=1`, déjà posé par l'install +Tailscale). + +**Option B — Sidecar Tailscale (robuste, si A ne route pas).** +Service `tailscale/tailscale` dans le compose, et le container app en +`network_mode: service:tailscale` → l'app partage l'interface tailnet (MagicDNS dispo). +À retenir seulement si l'option A ne fonctionne pas. + +### 4.4 (Conditionnel) CA racine MALIO — uniquement si DSN = HTTPS interne +Si le DSN cible `https://logs.malio-dev.fr` (cert auto-signé), baker la CA dans l'image +(`deploy/docker/Dockerfile.prod`, stage `production`) — `ca-certificates` est déjà installé : +```dockerfile +COPY deploy/docker/malio-dev-root-ca.crt /usr/local/share/ca-certificates/malio-dev-root-ca.crt +RUN update-ca-certificates +``` +(Le `.crt` public est récupérable depuis le repo Lesstime : `infra/prod/malio-dev-root-ca.crt`.) +Vérification : +```bash +curl --cacert deploy/docker/malio-dev-root-ca.crt https://logs.malio-dev.fr/api//store/ +``` +> **Recommandation** : préférer l'endpoint **HTTP** via le tailnet (déjà chiffré par WireGuard) → +> on évite complètement la CA et cette modif Dockerfile. + +### 4.5 Créer le projet GlitchTip `sirh-api` +Dans l'UI GlitchTip (org `malio`) : **New Project** → plateforme `php-symfony` → nom `sirh-api`. +Récupérer le **DSN** dans *Settings → Client Keys (DSN)*. Adapter le host du DSN à l'IP/nom tailnet +si nécessaire. +> Le MCP GlitchTip est en lecture seule (pas de `create_project`) → création manuelle UI. + +### 4.6 Injecter le DSN sur le serveur +Ajouter à l'`env_file` du docker-compose serveur (PAS dans l'image), puis redéployer : +```env +SENTRY_DSN=http://@100.x.y.z:/ +``` +```bash +docker compose up -d # recharge l'env_file +docker compose exec php php bin/console cache:clear --env=prod +``` + +## 5. Documentation (règles SIRH) + +- **`doc/error-tracking.md`** *(nouveau)* : pattern back, activation, runbook Tailscale, CA, lien + vers ce spec. +- **`CLAUDE.md`** : nouvelle section « Error tracking (GlitchTip) » résumant le pattern + le fait + que c'est prod-only / inerte sans DSN / transport Tailscale. +- **In-app documentation** (`frontend/data/documentation-content.ts`) : **non concernée** — + infra invisible pour les utilisateurs RH (employé/chef de site/admin), aucun changement + fonctionnel UI. + +## 6. Vérification + +| Niveau | Test | Attendu | +|---|---|---| +| Dev (sans DSN) | `make test`, boot dev | aucune régression, SDK absent en dev | +| Prod config | build image + `APP_ENV=prod cache:clear` (DSN bidon) | bundle chargé, pas d'erreur de conf | +| Inerte | prod sans `SENTRY_DSN` | aucun envoi, no-op | +| End-to-end | une fois Tailscale + projet OK : déclencher une erreur ERROR+ | Issue visible dans GlitchTip `sirh-api` | + +## 7. Hors périmètre (explicite) + +- Frontend (SDK Nuxt, source maps, build-args CI) — ajout futur via proxy nginx `/ingest`. +- APM / tracing / performance (DuckDB-like) — non. +- Exposition publique de GlitchTip — non (tout passe par Tailscale).