feat : error tracking backend vers GlitchTip (via Tailscale) (#37)
Auto Tag Develop / tag (push) Successful in 9s
Auto Tag Develop / tag (push) Successful in 9s
## 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>
This commit was merged in pull request #37.
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
# 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`) :
|
||||
```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://<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 :
|
||||
```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) <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`**
|
||||
|
||||
```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 <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 ») :
|
||||
```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) <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`).
|
||||
@@ -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://<clé>@<host-ou-IP-tailnet>:<port>/<id-projet>
|
||||
# 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 <glitchtip-tailnet-name-ou-IP>
|
||||
curl -sS -o /dev/null -w "%{http_code}\n" http://<glitchtip-IP-tailnet>:<port>/_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/<id>/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://<clé>@100.x.y.z:<port>/<id-sirh-api>
|
||||
```
|
||||
```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).
|
||||
Reference in New Issue
Block a user