ADR-0036 — Accès contextualisé aux logs applicatifs depuis SIRRAT¶
- Statut : Proposée
- Date : 2026-09-03
- Contexte amont : ADR-0016 (cercles), ADR-0017 (couloir/
lane), ADR-0035 (trois identités), R-236 (Forge propriétaire des slots)
0. Ce que l'audit a trouvé avant tout arbitrage¶
La fonctionnalité existe déjà pour l'essentiel. Cet ADR ne conçoit pas une nouveauté : il ratifie un mécanisme livré, corrige son contrat de contexte, et nomme ce qui l'empêche aujourd'hui de fonctionner.
Déjà en place :
| Élément | État |
|---|---|
LogConsoleLinkService |
livré — résout un gabarit d'URL avec le contexte |
GET /v1/sirrat/logs/console-links |
livré, gardé (requireRead(env) + requireProject) |
sirrat_log_provider.console_url_template |
migration 040, managed_by_forge |
| Garde anti-DSN en base | chk_log_provider_console_url_sans_dsn |
| Encodage d'URL des valeurs injectées | livré |
| Front | sirrat-api.service.ts:829 consomme la route |
Template Forge dynors-logging-multi-provider-2.0.0 |
40+ variables, presets DEV/PROD/AUDIT |
Ce qui bloque n'est donc pas la conception, mais l'alimentation. Trois faits, mesurés :
- Aucun conteneur applicatif ne porte le label docker
dynors.service. Promtail filtre dessus (docker_sd_configs.filters) ; seuluptime-kumale porte. Aucun log applicatif n'atteint Loki aujourd'hui. - La pile
monitoring/n'est déployée par aucun playbook.docker-compose.monitoring.ymln'est référencé que dans un README. sirrat_log_providerest vide — la migration 040 le documente explicitement et refuse de semer une URL plausible mais fausse : « un lien qui s'affiche, se clique, et tombe sur une page d'erreur — pire qu'un lien absent, qui lui se voit tout de suite ».
Le provider par défaut est console (DYNORS_LOGGING_PROVIDER:console chez DAWALALE) : les
logs vont sur la sortie standard, et le service le dit franchement plutôt que d'offrir un lien.
1. Objectif¶
Donner au DEV et au recetteur, depuis SIRRAT, l'endroit où regarder les logs d'une instance applicative — sans qu'il ait à retrouver lui-même les bons filtres dans Grafana.
SIRRAT indique OÙ observer.
La plateforme d'observabilité fournit l'expérience d'observation.
2. Non-objectifs¶
SIRRAT ne stocke pas les logs applicatifs
SIRRAT ne devient pas Loki / Datadog / Sentry
SIRRAT ne proxy pas les logs
SIRRAT ne fournit pas un dashboard infra DEV/QA
SIRRAT ne fusionne pas ses logs avec ceux de l'application
SIRRAT ne gère pas la navigation temporelle (période, from/to, « 15 dernières minutes »)
Ce dernier point est un arbitrage explicite : Grafana, Sentry et Datadog savent déjà naviguer dans le temps. Le dupliquer dans SIRRAT ajouterait une seconde manière de se tromper de fenêtre.
3. Deux natures de logs, jamais fusionnées¶
| Logs de déploiement | Logs applicatifs | |
|---|---|---|
| Producteur | SIRRAT, deploy-agent, Ansible | l'application |
| Rattachement | un Deployment précis | une instance logique (app × cercle × couloir) |
| Contenu | préparation, agent, playbook, preuve de santé, verdict | ce que l'application émet |
| Durée de vie | l'exécution | permanente, indépendante de tout déploiement |
| Accès | logs_url du Deployment, servi par l'agent |
deep-link externe |
Un déploiement réussi dont l'application plante ensuite : les premiers disent « succès », les seconds disent « erreur ». Les deux ont raison — c'est exactement pourquoi ils ne se mélangent pas.
4. Responsabilités¶
| Acteur | Responsabilité | Ne fait pas |
|---|---|---|
| Application | émet ses logs, alimente le MDC | ne connaît pas SIRRAT |
dynors-logging |
format ECS, abstraction provider, lecture MDC — c'est lui qui doit garantir les dimensions standardisées | ne décide pas de la destination |
| Forge | déclare le provider, le gabarit d'URL, le contrat structurel des dimensions | n'invente pas les valeurs d'un cercle |
| SIRRAT | fournit les valeurs concrètes (app, cercle, couloir) et compose le lien | n'invente pas les labels, ne lit pas les logs |
| Backend d'observabilité | stocke, indexe, cherche, navigue dans le temps | — |
SIRRAT ne charge pas dynors-logging. Il reste un producteur de logs indépendant, avec sa
propre identité. Même backend possible, producteurs distincts.
5. Dimensions de corrélation réellement disponibles¶
| Dimension | Existe ? | Source | Stable ? | Dans les logs ? |
|---|---|---|---|---|
appCode (couche 2) |
✅ côté SIRRAT | cleFonctionnelle(project) |
oui | ❌ non émis |
appTechCode (couche 3) |
✅ slot CODEX_APP_TECH_CODE |
Forge/recette | oui | ❌ non émis |
circle / environment |
✅ | MDC env → service.environment ; label docker dynors.env |
oui | ✅ |
lane (couloir) |
✅ | LaneContextFilter → MDC lane → deployment.lane |
oui | ✅ |
slot (au sens instance) |
— | n'existe pas sous ce nom — voir §6 | — | — |
version |
⚠️ | SIRRAT le connaît | oui | ❌ non émis |
deploymentId |
⚠️ | SIRRAT le connaît | oui | ❌ non émis |
Ce qui identifie l'application dans les logs aujourd'hui est service.name, alimenté par
spring.application.name. Pour DAWALALE cela vaut dawalale-backend.
Correction après contre-audit (2026-09-03). Une première lecture y voyait une « quatrième identité de fait ». C'est faux, et la preuve est dans le codex :
SirratCodexConstantsdocumenteCORE_APP_NAMEcomme « Nom Spring de l'application —spring.application.name».dawalale-backendest doncCORE_APP_NAME, une variable gouvernée par Forge — le nom de service observable, distinct de l'appCode et légitime.Le défaut réel est plus étroit, et le codex le dit lui-même à la ligne suivante :
CORE_APP_CODE= « Code court de l'app — utilisé dans les logs/SLY ». Le contrat prévoyait déjà l'appCode dans les logs ;JsonStdoutLoggingProvidern'a simplement jamais été branché dessus. Il n'y a pas d'identité en trop : il y a une dimension qui manque.
Double vocabulaire dans Loki, à corriger un jour : les mêmes concepts arrivent par deux
chemins — labels docker (service, environment, lane) et champs JSON promus
(service_name, service_env, deployment_lane).
Classement décidé¶
CONTEXTE V1 (contrat) appTechCode · circle · lane
UTILE SI DÉJÀ EN PLACE appCode
HORS CONTRAT V1 version · deploymentId · période · timestamp construit par SIRRAT
Aucune application n'est modifiée pour propager version ou deploymentId. Si l'information
existe, elle enrichit ; son absence ne bloque rien.
6. Décision de vocabulaire : slot ≠ couloir¶
Point de collision à trancher avant tout code.
Dans le modèle DYNORS, slot désigne une variable de recette (CORE_APP_CODE,
TAIL_DB_URL) — table sirrat_environment_slot_value, définie et activée par Forge (R-236).
Le « slot blue » du besoin d'observabilité désigne autre chose : une unité d'exécution
parallèle, c'est-à-dire le couloir (lane) d'ADR-0017.
Décision : l'observabilité utilise lane, jamais slot.
Deux raisons, dont une décisive :
laneest déjà émis dans les logs (deployment.lane, label Lokilane), alimenté parLaneContextFilter. La dimension recherchée existe donc — sous son vrai nom.- Employer « slot » ici créerait un homonyme au cœur d'un modèle où R-236 vient précisément d'établir qui possède les slots. Le premier lecteur qui croirait que l'observabilité manipule des slots conclurait que SIRRAT en gère le cycle de vie. R-236 reste intact.
Dans l'UI, le mot affiché peut rester celui que l'équipe emploie ; le contrat technique, lui,
dit lane.
7. Contexte du lien, et rien de plus¶
appTechCode + circle + lane
Composé exclusivement à partir des données persistées de SIRRAT — projet, cercle, couloir,
identité applicative. Jamais à partir de paramètres composés par le frontend : c'est déjà la
règle appliquée par LogConsoleLinkController, qui dérive le code applicatif du projet chargé
plutôt que de l'accepter en paramètre.
Le gabarit reste une donnée portée par Forge. Le mécanisme ne connaît ni Grafana ni Sentry.
Ce que cela change par rapport à l'existant¶
LogConsoleLinkService injecte aujourd'hui APP_CODE · ENV · LANE · VERSION · LEVEL.
| Variable | Décision |
|---|---|
ENV, LANE |
conservées — cœur du contrat |
APP_CODE |
conservée — lisibilité et navigation |
APP_TECH_CODE |
à ajouter — corrélation technique stable |
VERSION, LEVEL |
sortent du contrat V1 — restent utilisables par un gabarit qui les réclame, mais leur absence ne doit plus rien empêcher |
Aucune quatrième identité n'est créée. Une clé de fournisseur (projet Sentry, datasource
Grafana, service Datadog) reste une configuration du provider, portée par configJson,
jamais une identité métier DYNORS.
8. Historique d'un Deployment¶
Deux boutons, deux informations différentes :
Deployment #842
[ Logs déploiement ] → logs_url de l'exécution, servi par l'agent
[ Logs ↗ ] → même deep-link que la vue instance
Aucune logique temporelle spéciale en V1. SIRRAT connaît startedAt/finishedAt, mais ne
s'en sert pas pour construire une fenêtre : la plateforme d'observabilité contient déjà
l'historique, et le DEV/QA y remonte lui-même. Si un besoin d'ergonomie apparaît plus tard
(« ouvrir autour de l'heure du déploiement »), ce sera un confort de navigation, pas une
extension du contrat d'observabilité.
9. Deep-link, jamais proxy¶
SIRRAT → construit une URL → le navigateur ouvre la plateforme
Et non :
SIRRAT → interroge le backend → récupère → republie
Conséquences recherchées : aucun stockage supplémentaire, aucune duplication, aucun identifiant de backend manipulé par SIRRAT, autonomie complète du système d'observabilité.
10. Authentification¶
Aucun jeton dans le lien. L'authentification appartient au fournisseur : SSO, session existante, reverse proxy.
La distinction est déjà gravée en base : le DSN sert à émettre, c'est une clé d'écriture,
il vit au coffre ; le gabarit d'URL sert à regarder, il n'est pas secret. La contrainte
chk_log_provider_console_url_sans_dsn refuse tout gabarit qui ressemble à un DSN — pour qu'un
secret ne finisse pas affiché à l'écran, copié dans un ticket, collé dans une conversation.
11. Autorisation — aucune nouvelle capacité¶
État réel de la route : accessGuard.requireRead(env) puis requireProject(appCode), plus le
cloisonnement d'instance (environmentPolicy.assertVisible). Ni DEPLOY, ni capacité dédiée.
Décision : on ne crée ni VIEW_LOGS ni OBSERVE.
- Exiger
DEPLOYexclurait le recetteur — précisément l'utilisateur cible. - Créer une capacité dédiée pour une action de lecture déjà couverte par le périmètre projet ajouterait une dimension à administrer sans rien fermer de plus. Le lien ne révèle qu'une URL composée d'informations que l'appelant voit déjà à l'écran.
Une capacité OBSERVE deviendrait justifiée le jour où un accès aux logs donnerait accès à des
données (contenu de logs, donc données métier) et non plus à une adresse. Ce n'est pas le
cas d'un deep-link. Arbitrage à rouvrir si SIRRAT devait un jour lire le contenu — ce que
le §2 interdit.
12. Provider V1¶
Aucune abstraction multi-provider supplémentaire. Elle existe déjà à deux niveaux — les
LoggingProvider de dynors-logging, et le console_url_template par provider dans SIRRAT —
et aucun des deux n'a besoin d'être étendu.
Le provider V1 est celui que Forge déclarera, ce qui n'engage pas cet ADR. Constat factuel :
la pile Loki/Promtail/Grafana est la seule outillée dans dynors-ops, mais elle n'est ni
déployée ni alimentée (§0). Sentry est câblé côté code, sans DSN.
Conséquence à assumer : la V1 du bouton ne produira un lien utilisable qu'une fois la chaîne d'émission réellement branchée. D'ici là, le service dit correctement pourquoi il n'y a pas de lien — c'est son comportement actuel, et il vaut mieux que le contraire.
13. UI V1¶
DAWALALE
INT
Couloir ref
Déployer Historique Logs ↗
- Libellé :
Logs ↗. Le chevron dit « vous quittez SIRRAT ». - Jamais le nom du fournisseur dans le libellé : Grafana, Sentry ou Datadog sont des détails d'infrastructure qui doivent pouvoir changer sans toucher à l'interface.
- Pas de menu
Logs / Metrics / Traces / APM / Infrastructure. Les métriques d'infrastructure ne sont pas un besoin DEV/QA. Erreurs ↗seulement si un provider d'erreurs est réellement déclaré. Pas de bouton pour une capacité absente.
Quand aucun lien n'est utilisable, l'écran dit lequel des deux cas s'applique — provider non déclaré, gabarit incomplet, ou logs sur sortie standard — plutôt que d'afficher un bouton mort.
14. Stratégie d'évolution¶
| Étape | Contenu | Bloquant pour la V1 ? |
|---|---|---|
| Prérequis d'émission | labels docker dynors.service / .env / .lane posés par les composes ; déploiement de la pile monitoring |
oui, en pratique |
| Identité dans les logs | émettre appTechCode (et appCode) via dynors-logging plutôt que spring.application.name |
oui pour le contrat §7 |
| Déclaration Forge | provider + console_url_template renseignés |
oui |
| Vocabulaire Loki | résorber le double jeu de labels | non |
| Enrichissement | version, deploymentId dans les logs |
non |
| Navigation temporelle | ouvrir autour d'un déploiement | non |
Décisions restant à arbitrer¶
- ~~
service.name: que doit-il porter ?~~ TRANCHÉ —service.nameresteCORE_APP_NAME(le nom de service observable, ex.dawalale-backend). C'est un nom de composant, pas une identité applicative : SuperGest le prouve, avecsupergest-apietsupergest-acquisitionsous une même application. On ajoutedynors.app_codeplutôt que de détournerservice.name.appTechCodeest écarté du contrat V1 : il désigne une Trust Unit, et aucun projet ne le porte en valeur de recette aujourd'hui — l'inscrire au contrat rendrait la V1 dépendante d'une donnée absente. - Qui pose les métadonnées runtime — voir §14 ; le rôle
dynors-appest recommandé, avec la réserve du fichier de surcharge (labels par service, pas par pile). - Provider V1 effectivement retenu — Loki/Promtail/Grafana, seule chaîne outillée.
ERREURS_APPLICATION: Sentry est câblé sans DSN, et aucun autre backend d'erreurs n'existe. Le bouton n'existe pas tant que la capacité n'est pas réelle.