Aller au contenu

Observabilité & traçabilité DYNORS — suivre une requête de bout en bout

Répond à la question centrale : « il y a de la communication partout — comment suivre une requête jusqu'à la fin ? ». Opérationnalise la trajectoire déjà décidée dans docs/architecture/reference-resilience/CAHIER_EXPLOITATION_ET_TRAJECTOIRE.md (« logs + metrics (Prometheus) + traces (OpenTelemetry) corrélés »).


1. Les trois piliers (et l'état réel)

Pilier Question Outil DYNORS État
Logs Que s'est-il passé ? Loki + Promtail + Grafana ✅ en place
Metrics Combien / à quelle vitesse / où ça sature ? Prometheus + Micrometer 🟡 posé dans ce lot (manquait)
Traces Par où est passée CETTE requête, et où part le temps ? OpenTelemetry + Tempo 🔴 à brancher
Uptime Est-ce up ? Uptime-Kuma
Erreurs Quelles exceptions ? Sentry (Forge)

Le diagnostic : DYNORS avait les logs, l'uptime et les erreurs, mais pas la couche métriques (pas de Prometheus, pas de /actuator/prometheus) ni le tracing. Donc on pouvait dire « c'est la même requête » (par id) mais pas « voici son trajet et son goulot ».


2. Suivre une requête : deux niveaux complémentaires

Niveau 1 — Corrélation par requestId (presque en place)

  • RequestIdFilter (core/web-errors) : à l'entrée HTTP, prend le header X-Request-Id entrant ou génère un UUID, le met en MDC (requestId) → chaque ligne de log le porte, et le renvoie dans la réponse.
  • InterAppCallService propage X-Request-Id sur chaque appel inter-app (HTTP via SLY).
  • ApiErrorResponse renvoie le requestId au client.

Aujourd'hui : on peut filtrer Loki par requestId à travers les services HTTP. C'est la traçabilité « par identifiant ».

Dans ce lot : le pattern de log d'auth/entitlements inclut désormais [req:%X{requestId}] (prérequis : RequestIdFilter de core/web-errors sur le classpath).

Niveau 2 — Tracing distribué (le « en profondeur »)

Le requestId dit « même requête » ; il ne dit pas « le hop entitlements a pris 800 ms sur la requête DB ». Pour voir le trajet et le goulot, il faut des traces : un traceId, des spans parent/enfant, une vue en cascade.

Cible (alignée CAHIER) : Micrometer Tracing + OpenTelemetry (natif Spring Boot 3) → export OTLP → Grafana Tempo. Le contexte W3C traceparent se propage automatiquement sur HTTP (servlet, RestClient/WebClient instrumentés). Bénéfice : vue waterfall SLY → app → entitlements → DB → bus, latence par hop, et corrélation trace ↔ logs (Loki) ↔ metrics (Prometheus exemplars).


3. ⚠️ Le maillon critique : la frontière asynchrone (le bus)

C'était LE point qui cassait le « jusqu'à la fin » : sans propagation, le consommateur (ex. projection entitlements) repartait sans requestId ni traceparent.

HTTP  requête (requestId=R, traceId=T)
  └─ app publie un event  ──►  metadata { requestId, traceparent }
       └─ consumer restaure MDC  ──►  logs [req:R] corrélés

Implémenté (P2)core/events/correlation/EventCorrelation :

✅ Implémenté (P2) : core/events/correlation/EventCorrelationenrich() copie le MDC du producteur (requestId, traceparent) dans DomainEvent.metadata à la publication (EventPublisher) ; runWithContext() les restaure en MDC pendant le handler à la consommation (EventSubscriber, donc tous les consommateurs — ex. EntitlementsEventConsumer — en bénéficient automatiquement). Test : EventCorrelationTest. La clé traceparent est déjà prévue (active dès que le palier tracing P4 peuple le MDC/baggage OpenTelemetry).


4. Métrologie & bottlenecks (ce que Prometheus rend visible)

Posé dans ce lot : Prometheus dans dynors-ops/monitoring (+ prometheus.yml), et micrometer-registry-prometheus + endpoint /actuator/prometheus + tags app/env/lane sur auth et entitlements. Restent à instrumenter (les goulots) :

Mesure Métrique (Micrometer) Révèle
Débit / erreurs de publication events.published{type,tenant,result} (Counter) producteur en panne, pics
Latence handler/projection events.handle.latency{type} (Timer p95/p99) consommateur lent
Profondeur de file / lag RabbitMQ exporter (rabbitmq_queue_messages, consumers) le bottleneck classique
Échecs / DLQ events.failed, DLQ count poison messages
Skips d'idempotence events.duplicate.skipped rejeux
Appels inter-app seam InterAppCallMetrics → impl Micrometer livrée (P3) : interapp.calls (Timer), interapp.calls.errors/.started (Counter), tags target,outcome latence/erreurs par cible

✅ Implémenté (P3) : MicrometerInterAppCallMetrics (interapp-client) — branchée automatiquement dès qu'une MeterRegistry est présente (apps avec actuator), sinon repli no-op. Câblage déterministe (@ConditionalOnClass/@ConditionalOnMissingClass, pas d'@ConditionalOnBean). Cardinalité maîtrisée : tags target + outcome seulement ; requestId/tenant restent dans logs/traces. Tests : MicrometerInterAppCallMetricsTest.


5. Tags par couloir — observabilité alignée sur le modèle couloirs

Toutes les métriques et traces portent env + lane (couloir) + tenant → on compare un couloir rc1 à ref, on isole un tenant, on suit une promotion int → rmoa → prod. C'est la contrepartie observabilité du modèle couloirs (ADR-0017/0018).


6. Forge — standardiser, pas réinventer

Un fragment observabilité dans les templates Forge (cf. ta question « template métrologie ») : - dépendances micrometer-registry-prometheus (+ plus tard micrometer-tracing-bridge-otel, opentelemetry-exporter-otlp), - exposition /actuator/prometheus, RequestIdFilter actif, pattern de log [req:%X{requestId}], - tags communs app/env/lane/tenant injectés depuis les seeds SIRRAT du couloir.

→ chaque app hérite d'une observabilité identique et comparable. Forge cadre la forme ; l'app fournit le contenu (ses métriques métier).


7. Plan par paliers

Palier Contenu État
P1 — Metrics Prometheus dans la stack + /actuator/prometheus + tags app/env/lane 🟡 fait (ce lot) : auth, entitlements ; reste sly/autres
P2 — Corrélation bus requestId (+traceparent) dans DomainEvent.metadata à la publication, restauré à la consommation livré : EventCorrelation dans EventPublisher/EventSubscriber
P2.5 — DynorsLogger + MDC Merge MDC dans DynorsLogger ; stdout JSON Logback
P3 — Inter-app metrics impl Micrometer de InterAppCallMetrics livré : MicrometerInterAppCallMetrics (auto-branchée si MeterRegistry présente)
P4 — Tracing Micrometer Tracing + OTel + Tempo (décommenter le service) + datasource Grafana
P5 — Bus instrumentation compteurs/timers events + exporter RabbitMQ (lag/DLQ) + dashboards/alertes
P6 — Forge fragment observabilité standardisé

8. Réponse synthétique

  • Court terme (déjà possible) : suivre par requestId dans Loki (HTTP). Avec ce lot, les métriques deviennent collectables (Prometheus).
  • Le trou à fermer en priorité : propager le contexte à travers le bus (P2) — sinon « jusqu'à la fin » s'arrête au premier événement asynchrone.
  • La profondeur : OpenTelemetry + Tempo (P4) donne la vue trajet + latence par hop + le lien trace↔logs↔metrics — c'est ce qui montre est le bottleneck, pas seulement qu'il y en a un.

Réf. : core/web-errors/RequestIdFilter, core/commons/interapp/InterAppCallMetrics & InterAppCallService, core/events/DomainEvent (metadata), dynors-ops/monitoring/ (Prometheus + Loki + Grafana + Tempo prévu), CAHIER d'exploitation (trajectoire observabilité). Tags couloir : ADR-0017/0018.