ADR 0017 — Traces HTTP client-only (erreurs uniquement) via faro-web-tracing → Alloy → Tempo
Retiré par ADR 0020 : Faro (et ses spans HTTP) est supprimé du repo. Conservé comme archive.
Amende les points 4 et 5 de l'ADR 0016 (« pas de
@grafana/faro-web-tracingpour l'instant » ; signaux par défautgetWebInstrumentations()+http_requestsur chaque requête).
Contexte
L'ADR 0016 avait écarté @grafana/faro-web-tracing : l'auto-instrumentation OTel fetch/XHR est aveugle aux appels API sur device (CapacitorHttp route par la couche native) et aucun backend de traces n'était disponible. Depuis, Tempo est branché derrière l'Alloy d'équipe (telemetry.internal.spektrum-suisse.ch) : les logs arrivent dans Loki mais aucune trace n'est émise côté app. On veut des spans HTTP (durée, statut, erreurs) corrélés aux sessions Faro — et on veut limiter le volume au signal d'erreur : pas de span/event par requête réussie, pas de web vitals ni d'events de vue/navigation dans Loki.
Horizon n'est toujours pas instrumenté OTel — un tracing distribué complet reste hors de portée pour l'instant.
Décision
@grafana/faro-web-tracing (2.8.x, lockstep avec le web-sdk) pour le pipeline de traces, auto-instrumentation désactivée ; spans CLIENT créés manuellement dans les deux funnels HTTP via la façade (startHttpSpan), émis pour les échecs uniquement. Télémétrie errors-only : seuls les signaux d'erreur (+ cycle de session) partent au collecteur. Traces client-only : pas de propagation traceparent.
- Pipeline sans auto-instrumentation :
new TracingInstrumentation({ instrumentations: [] })est ajouté aux instrumentations Faro. L'optioninstrumentations: []désactive réellement les instrumentations OTel fetch/XHR par défaut (fallback?? getDefaultOTELInstrumentations(...), vérifié sur le dist 2.8.2) — inutiles ici (CapacitorHttp) et sources de spans dupliqués en session navigateur. Ne reste que le provider de traces + l'export. - Spans manuels depuis les deux funnels uniquement, échecs uniquement : la façade expose
startHttpSpan(method, url): HttpSpan— un handle{ end(status), fail(error, status?) }inerte tant que Faro/OTel ne sont pas initialisés (même doctrine no-op que tous les wrappers).publicFetchetApiClient.requestouvrent le handle à côté du chrono existant et le ferment aux deux mêmes sites quetrackHttp(succès / catch). CôtéApiClient, le span couvre le refresh proactif + le retry 401 : un span par appel logique, même sémantique que la durée trackée.customer.existsreste volontairement non tracé.- Matérialisation paresseuse : le handle ne crée le span OTel qu'à la fermeture, et seulement pour un échec (
end(status ≥ 400 ou 0),fail(...)) — une requête réussie ne produit aucune trace. LestartTimecapturé à l'ouverture est passé àstartSpan, la durée reste donc exacte. Le filtre vit dans la façade, pas dans les funnels. - Span : tracer
buchard-mobile-http, nomHTTP {METHOD}(pas de path — cardinalité non bornée dans les noms), kind CLIENT, attributshttp.request.method,url.full(query strippée, même helper quetrackHttp),http.response.status_codeà la fermeture. Statut ERROR systématique (seuls des échecs sont émis — conforme spec pour un span CLIENT). - Les constantes
kind: 3/code: 2sont des littéraux (enums@opentelemetry/apigelés par la spec) : la façade reste une feuille sans import de valeur statique.
- Matérialisation paresseuse : le handle ne crée le span OTel qu'à la fermeture, et seulement pour un échec (
- Signaux errors-only (remplace les défauts
getWebInstrumentations()de l'ADR 0016) :- Instrumentations réduites à
ErrorsInstrumentation(window.onerror / unhandledrejection),ConsoleInstrumentationrestreinte àconsole.errorseul (configconsoleInstrumentation.disabledLevels: trace/debug/info/log/warn — leconsole.errorpart en signal d'erreur, pas en ligne de log) etSessionInstrumentation(cycle de session conservé : c'est la clé de corrélation des signaux dans Grafana). - Supprimés : web vitals, events de vue (
ViewInstrumentation), navigation, user-action, performance, CSP.setView/setUserrestent actifs — ce sont des métas portées par les signaux d'erreur, pas des events. trackHttpne pousse un eventhttp_requestque pour un échec (HTTP ≥ 400 ou status 0 transport) — filtre dans la façade, funnels inchangés.
- Instrumentations réduites à
- Pas de
traceparentvers Horizon : les spans ne se lieraient à rien (backend non instrumenté) et l'en-tête exigerait une entrée CORS côté Horizon pour les sessions navigateur. Prochaine étape documentée : le handle existe avant le départ de la requête précisément pour permettre l'injection du header le jour où Horizon passe OTel. Pas non plus decontext.with(...): spans plats, sans parent/enfant. - Invariant DCE inchangé : les deux imports
@grafana/*sont desimport()dynamiques dans la même branche gardée par les===littéraux (Promise.all). Prod/e2e : zéro octet (vérifié :npx vite buildpuis grep@grafana|initializeFaro|opentelemetrysurdist/assets→ vide). - Règle de bump lockstep :
@grafana/faro-web-sdket@grafana/faro-web-tracingse releasent ensemble et doivent être bumpés ensemble — un écart de version peut faire installer une seconde copie du web-sdk (deux faro-core → enregistrement cassé). Vérif :npm ls @grafana/faro-web-sdk→ une seule version dédupliquée.
Config Alloy de référence (mise à jour)
faro.receiver "buchard_mobile" {
server { /* inchangé, cf. ADR 0016 */ }
output {
logs = [loki.write.default.receiver]
traces = [otelcol.exporter.otlp.tempo.input]
}
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = "tempo:4317"
// tls { insecure = true } selon le déploiement
}
}Conséquences
- Trade-off assumé : aucune observabilité du trafic sain. Plus de latences/volumétrie des requêtes réussies (ni event ni span), plus de web vitals, plus de parcours de vues. Si un besoin de perf monitoring revient, ré-élargir ici (sampling plutôt que tout-ou-rien).
- Export : spans →
FaroTraceExporter→ transport Faro existant (/collect) →faro.receiver→ Tempo. Batch ~1 s / 30 spans (BatchSpanProcessor) — même perte fire-and-forget que les logs si l'app est tuée. - Corrélation logs ↔ traces par session : les spans portent les métas Faro (session id, user id, app) — jointure dans Grafana entre lignes Loki et traces Tempo d'une même session. Pas de trace_id sur les logs (pas de context manager actif), la session est la clé.
- Poids : le chunk lazy dev/staging passe d'~32 KB à ~70-90 KB gzip (sdk-trace-web, otlp-transformer ; les instrumentations fetch/xhr restent dans le chunk — référencées par le fallback
??— mais inactives). Pas de zone.js en 2.8.x (corrige l'estimation « OTel + zone.js » de l'ADR 0016). Prod/e2e : toujours zéro. - Si les spans n'arrivent pas alors que les logs coulent : vérifier d'abord la sortie
tracesdufaro.receivercôté collecteur, puis le sampling de session Faro (sessionTracking.samplingRate, défaut 1).

