Files
SIRH/docs/superpowers/plans/2026-06-28-glitchtip-backend-error-tracking.md
T
matthieu 42b02a8148
Auto Tag Develop / tag (push) Successful in 9s
feat : error tracking backend vers GlitchTip (via Tailscale) (#37)
## Objectif
Remonter les erreurs **backend** Symfony vers **GlitchTip** (SDK Sentry), **prod uniquement**, **inerte sans `SENTRY_DSN`**. Transport réseau via **Tailscale** sur le host de prod (infra, hors repo). Frontend hors périmètre.

## Contenu
- `sentry/sentry-symfony:^5.10` (+ `symfony.lock` recipe)
- `config/bundles.php` → `SentryBundle ['prod' => true]`
- `config/packages/sentry.yaml` (nouveau) : DSN runtime, release `%app.version%`, 4xx ignorés, pas de tracing, handler Monolog ERROR+
- `config/packages/monolog.yaml` : handler `sentry` en `when@prod`
- `.env` : bloc `SENTRY_DSN` documenté (vide → inerte)
- `doc/error-tracking.md` (runbook Tailscale) + section `CLAUDE.md`
- Spec + plan sous `docs/superpowers/`

## Vérifications
- Prod `cache:clear` OK, service `Sentry\Monolog\Handler` chargé
- **267/267 tests verts**, dev/test inchangés (bundle non chargé hors prod)
- Aucun changement `frontend/` / `.gitea/` / `deploy/docker/`
- Revue multi-agents : **READY TO MERGE** (aucun Critical/Important)

## Activation prod (hors code, cf. `doc/error-tracking.md`)
1. Tailscale sur l'hôte GlitchTip **et** sur le VPS OVH (prod)
2. Créer le projet `sirh-api` dans GlitchTip → récupérer le DSN
3. `SENTRY_DSN=http://<clé>@<IP-tailnet>:<port>/<id>` dans l'env_file serveur + redéploiement

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #37
Co-authored-by: matthieu <matthieu@yuno.malio.fr>
Co-committed-by: matthieu <matthieu@yuno.malio.fr>
2026-06-28 11:46:35 +00:00

11 KiB

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) <noreply@anthropic.com>.
  • 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) :

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 :

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 :

# 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) :

            # 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) :

###> 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://<clé>@<host-ou-IP-tailnet>:<port>/<id-projet>
# 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 :

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é

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
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) <noreply@anthropic.com>
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

# 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 <glitchtip-tailnet>
   curl -sS -o /dev/null -w "%{http_code}\n" http://<glitchtip-IP-tailnet>:<port>/_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://<clé>@100.x.y.z:<port>/<id-sirh-api>
   \`\`\`
   \`\`\`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 ») :

## 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
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) <noreply@anthropic.com>
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).