Statut au 2026-11-05 (lot 1c) : implémentés et testés :
/livez,/readyz(contrôle de la base), logs JSON aveccorrelation_id,trace_idetspan_id, métriques HTTP, base de données, build et runtime sur l'écoute d'administration, traces OpenTelemetry exportables en OTLP/HTTP,pprof. Planifiés : métriques de lecture, de jobs, de scan et de workers (elles arrivent avec leurs domaines), diagnostics admin/api/v1/admin/*. Décision associée : ADR 0012.
L'observabilité fait partie du produit : un administrateur qui n'a jamais lu le code doit pouvoir comprendre pourquoi un titre démarre lentement, pourquoi un transcodage échoue et ce qui limite une session.
1. Santé
| Endpoint | Rôle | Authentification |
|---|---|---|
GET /livez | Le processus répond (pas de dépendance vérifiée) | Aucune |
GET /readyz | Prêt à servir : base SQLite ouverte et accessible en écriture, migrations à jour, rôles démarrés. Sur un worker distant : coordinateur joignable | Aucune ; réponse minimale (ok / not ready), sans détail |
GET /api/v1/server/info | Nom, version publique de l'API | Aucune ; aucune information sensible (pas de version FFmpeg, OS, chemins) |
GET /api/v1/admin/health | État détaillé des dépendances, stockages, accélérateurs | Admin |
/livez et /readyz ne doivent jamais renvoyer de détail exploitable par un attaquant.
2. Logs
log/slogavec gestionnaire JSON en production, texte en développement (PROTOFLIX_LOG_FORMAT).- Sortie sur stdout/stderr ; rotation et rétention déléguées à journald ou au pilote Docker (pas d'écriture de fichiers de log par le serveur en V1).
- Niveaux :
debug(développement),info(événements métier notables : démarrage, scan terminé, session démarrée/terminée),warn(dégradation récupérable : source injoignable, retry),error(échec nécessitant attention). - Champs communs :
time,level,msg,correlation_id,trace_idetspan_id(ajoutés automatiquement quand le contexte porte un span valide, implémenté) ; selon le contexteuser_id(opaque),playback_session_id,job_id,job_type,library_id. - Correlation ID :
X-Request-IDaccepté depuis un proxy de confiance s'il est bien formé, sinon généré ; renvoyé dans la réponse et dans chaque erreur structurée (request_id), propagé aux jobs créés par la requête. - Redaction : voir
docs/security/secrets-and-privacy.md§3. Jamais de jeton, cookie, mot de passe, corps d'authentification ; chemins système seulement dans les événements destinés à l'admin.
3. Métriques
Instrumentation OpenTelemetry, exposée au format Prometheus sur l'écoute d'administration (PROTOFLIX_ADMIN_ADDR, désactivée par défaut, typiquement 127.0.0.1:9096). Cette écoute n'a pas d'authentification : elle reste sur loopback ou sur un réseau privé, et n'est jamais publiée via le reverse proxy. Le serveur avertit au démarrage si elle écoute au-delà de loopback. L'écoute publique ne sert jamais /metrics (testé).
Règle de cardinalité : les labels ont des valeurs bornées et connues. Jamais de nom de fichier, chemin, titre, identifiant d'utilisateur, identifiant de session ou URL brute en label. Les routes HTTP sont étiquetées par le motif choisi par le routeur (/api/v1/items/{itemId}) ; toute requête non routée tombe sous l'unique valeur unmatched, et les méthodes inconnues sous _OTHER. Un test envoie des chemins arbitraires et vérifie que l'ensemble des labels reste borné.
Les noms ci-dessous sont ceux exposés en Prometheus. Implémentées (lot 1c) :
| Métrique | Type | Labels |
|---|---|---|
http_server_request_duration_seconds | histogramme | http_request_method, http_route, http_response_status_code (convention sémantique OpenTelemetry) |
http_server_active_requests | jauge | — |
protoflix_db_write_wait_seconds | histogramme | — : attente de l'écrivain unique et de son verrou (BEGIN IMMEDIATE). C'est l'indicateur principal de l'ADR 0019 |
protoflix_db_write_duration_seconds | histogramme | — : durée pendant laquelle une transaction tient l'écrivain |
protoflix_db_write_transactions_total | compteur | outcome (committed, rolled_back, begin_failed, commit_failed) |
protoflix_db_connections_in_use | jauge | pool (writer, reader) |
protoflix_db_connections_waits_total | compteur | pool |
protoflix_build_info | jauge (toujours 1) | version |
protoflix_jobs_live | jauge | type, state (queued, running, retry_wait) |
protoflix_jobs_oldest_ready_age_seconds | jauge | type : âge du plus vieux job prêt, c'est-à-dire la latence de la file |
protoflix_jobs_finished_total | compteur | type, state (terminal) |
protoflix_jobs_attempt_duration_seconds | histogramme | type, outcome (succeeded, failed, cancelled, released, lease_lost) |
go_*, process_* | divers | collecteurs Prometheus standard (mémoire, goroutines, GC, CPU, descripteurs) |
Planifiées, à ajouter avec leur domaine (noms indicatifs) :
| Métrique (nom indicatif) | Type | Labels autorisés |
|---|---|---|
protoflix_playback_sessions_active | jauge | mode (direct_play, remux, transcode), network (local, remote) |
protoflix_playback_negotiation_duration_seconds | histogramme | mode |
protoflix_playback_time_to_first_segment_seconds | histogramme | mode, accel (none, vaapi, qsv, nvenc…) |
protoflix_playback_decisions_total | compteur | mode, reason (énumération fermée : video_codec, audio_codec, container, bitrate, resolution, subtitle_burn_in, hdr_tonemap, policy…) |
protoflix_transcode_engine_start_duration_seconds | histogramme | accel |
protoflix_transcode_speed_ratio | histogramme | accel, video_codec_out |
protoflix_engine_errors_total | compteur | category (driver, codec, filter, input, resource, timeout, crash) |
protoflix_jobs | jauge | type, state |
protoflix_job_duration_seconds | histogramme | type, outcome |
protoflix_job_retries_total | compteur | type |
protoflix_job_queue_oldest_age_seconds | jauge | type |
protoflix_downloads_active | jauge | kind (http, bittorrent) |
protoflix_private_storage_used_bytes | jauge | — (total, pas par compte) |
protoflix_storage_source_state | jauge (0/1) | state (compte par état, pas par source) |
protoflix_scan_files_total | compteur | outcome (discovered, changed, removed_confirmed, error) |
protoflix_disk_free_bytes | jauge | area (data, cache, transcode) |
protoflix_auth_failures_total | compteur | kind (password, quick_code, invitation, token) |
label op sur les métriques d'écriture | — | énumération fermée (catalog, jobs, progress, auth…), quand ces domaines existeront |
protoflix_db_size_bytes | jauge | file (main, wal) |
protoflix_workers | jauge | kind (local, remote), state (online, offline) |
protoflix_worker_jobs_active | jauge | worker (nom donné à l'enrôlement, nombre borné par l'administrateur), type |
protoflix_job_leases_expired_total | compteur | type, kind (local, remote) |
protoflix_internal_api_requests_total | compteur | route, status_class |
protoflix_segment_relay_duration_seconds | histogramme | — (relais coordinateur → worker de transcodage distant) |
L'état par source de bibliothèque (identifiant, chemin) est exposé dans le diagnostic admin, pas en métrique. Les métriques runtime Go standard (mémoire, goroutines, GC) sont incluses.
CPU, mémoire, I/O et GPU de l'hôte : relevés pour le diagnostic admin et le Resource Manager ; pour un monitoring complet de l'hôte, l'administrateur utilise ses propres outils (node_exporter, etc.).
4. Traces
- OpenTelemetry Go, export désactivé par défaut : sans destination, les spans ne sont même pas enregistrés. Activation par
PROTOFLIX_OTLP_ENDPOINT(OTLP/HTTP, par exemplehttp://collector:4318,/v1/tracesajouté si besoin) vers une destination choisie par l'administrateur. Un test vérifie qu'aucun span n'est produit sans destination, et que l'export atteint un collecteur configuré. - Implémenté : span serveur par requête API, nommé
MÉTHODE /motif, en erreur pour les réponses 5xx. Les sondes/livezet/readyzne sont pas tracées. - Spans principaux : requête API, décision de lecture, accès base (par requête nommée sqlc), création de job, exécution de job, lancement du moteur, génération du manifeste, premier segment disponible.
- Propagation W3C
traceparentacceptée seulement depuis les proxies de confiance (implémenté et testé) : un client direct ne peut pas choisir l'identifiant de trace. - Attributs interdits : jetons, cookies, corps, chemins complets, noms de fichiers.
- Échantillonnage : 100 % quand l'export est activé (implémenté), ce qui convient au volume d'un foyer. Un échantillonnage configurable (par exemple 10 % des requêtes, 100 % des erreurs et des négociations de lecture) est planifié si le volume l'exige.
5. Diagnostics administrateur
Exposés sous /api/v1/admin/* (rôle admin) et dans l'espace admin du client web :
- version et build du serveur, version du schéma, version de SQLite embarquée, taille de
protoflix.dbet du fichier-wal; - workers enregistrés (local ou distant), version, capacités annoncées, dernier heartbeat, leases en cours ;
- FFmpeg : version, options de compilation, encodeurs/décodeurs, et pour chaque accélérateur l'état détecté / expérimental / validé / non supporté (jamais « supporté » sur simple détection) ;
- capacité de l'hôte : CPU, mémoire, GPU, charge ;
- sources de bibliothèques : état (
online,unavailable,permission_denied,io_error), dernière vue, chemin (admin uniquement) ; - sessions actives avec plan de lecture : ce qui est lu tel quel, copié, réencodé, et pourquoi (un fichier de bibliothèque privée apparaît comme « Contenu privé », sans titre) ;
- occupation des quotas de bibliothèques privées par compte (volume seulement, jamais les fichiers) ; téléchargements en cours et en échec si le module est activé ;
- jobs actifs, en échec, en dead-letter, avec erreur structurée et action de relance ;
- erreurs récentes regroupées par catégorie ;
- espace disque et quotas des répertoires ;
- diagnostic de configuration (clés présentes, valeurs non sensibles, avertissements).
Le flux temps réel des sessions et jobs pour l'admin passe par SSE (ADR 0007).
6. Profilage
net/http/pprof (/debug/pprof/) est servi uniquement sur l'écoute d'administration, donc désactivé tant que PROTOFLIX_ADMIN_ADDR est vide, et jamais sur l'écoute publique (implémenté). Les profils sont enregistrés sur un routeur dédié, pas sur http.DefaultServeMux.
7. Critères d'acceptation (à transformer en tests)
- Toute réponse d'erreur contient un
request_idretrouvable dans les logs. - Aucune métrique n'a de label hors de la liste autorisée (test d'inventaire). Fait pour les routes HTTP ; inventaire complet à étendre avec chaque domaine.
- Aucune ligne de log ne contient un jeton ou un cookie (test sur la sortie de la suite d'intégration).
/readyzpasse à « non prêt » quand la base est arrêtée, et revient sans redémarrage.
Source : docs/operations/observability.md, dans le dépôt de ProtoFlix.