Lab 7 bonus — Voir le bagage, et s'en servir
Le Lab 7 vous fait lire trois lignes de Baggage dans ReviewController.java, puis vous annonce que le bagage « voyage avec le contexte » — et vous ne voyez rien. Ni dans Jaeger, ni ailleurs. Cette page comble ce trou : on rend le bagage visible, on regarde jusqu’où il descend, et on s’en sert pour garder 100 % des traces d’un client donné.
Contrairement aux autres bonus, celle-ci se manipule : une variable d’environnement, quelques curl, un helm upgrade.
1. Pourquoi on ne voit rien
Un attribut est une donnée du span : le SDK l’exporte avec lui, Jaeger l’affiche.
Un bagage n’est pas une donnée du span. C’est une valise que le contexte trimballe : le SDK la sérialise dans un en-tête HTTP au moment d’appeler le service suivant, et c’est tout. Elle ne part jamais vers le collecteur, donc rien n’en arrive jamais dans Jaeger.
D’où le silence du Lab 7. Le mécanisme fonctionne pourtant, et pour le prouver il faut faire passer le bagage du premier rail au second — c’est exactement ce que fait la section suivante.
« Mais le bagage n’est vraiment pas envoyé au collecteur ? » Non : OTLP ne transporte pas le bagage. Le protocole décrit un span par son nom, ses attributs, ses événements, son statut — il n’a aucun champ pour ça.
Et une copie automatique serait coûteuse. Recopier toutes les clés du bagage sur chaque span d’une trace multiplie la donnée stockée ; et un bagage peut contenir ce qu’un service amont y a mis, y compris ce que vous ne tenez pas à garder six mois dans Jaeger.
D’où la règle : ce que vous voulez voir dans les traces, vous l’y mettez explicitement, clé par clé. C’est exactement ce que demande la variable de la section suivante — elle ne copie que les clés que vous nommez.
Constatez-le tout de suite, avant d’avoir rien changé. Envoyez une requête portant un bagage bien visible :
Le produit DOESNOTEXIST n’existe pas, et c’est voulu : la requête échoue, or keep-errors (Lab 7) conserve 100 % des traces en erreur. La vôtre sera donc bien là, sans dépendre du tirage à 25 %.
Cherchez babbaba9e00000000000000000000009 dans le champ Trace ID de Jaeger, ouvrez le span POST /api/reviews et dépliez ses Tags. Relevé sur le cluster de la formation :
Vingt et un attributs, et pas un seul ne vient des deux clés pourtant envoyées dans l’en-tête baggage. Ce qui n’a pas été copié en attribut dans le processus émetteur n’existe nulle part en aval.
Conséquence pratique, et elle surprend : aucun processor du collecteur ne peut récupérer un bagage, puisqu’il ne lui arrive jamais. La conversion bagage → attribut se fait toujours du côté de l’application — par la variable d’environnement de la section suivante, ou par un BaggageSpanProcessor ajouté au SDK.
Au fait, qu’est-ce qu’on peut mettre dans un bagage ? Des paires chaîne → chaîne, et rien d’autre : ni nombre, ni booléen, ni objet, ni liste. Le format est décrit par la spécification W3C Baggage, sœur de traceparent, et se résume à un en-tête :
Deux limites du standard valent d’être retenues, elles reviendront à la section 5 : l’en-tête est plafonné à 8 192 octets et 64 entrées, et il circule en clair.
2. Rendre le bagage visible
L’agent Java sait recopier des clés du bagage dans les attributs de chaque span qu’il crée. La fonctionnalité est marquée expérimentale, elle s’active par une variable d’environnement — et il faut nommer les clés attendues, il n’y a pas de copie aveugle :
C’est le pont entre les deux rails du schéma ci-dessus : à partir de maintenant, ce qui est dans le bagage se retrouve aussi sur les spans.
⚠️ Laissez passer quelques secondes avant le premier
curl. Changer une variable d’environnement remplace le pod, et lekubectl port-forwardqui vous donne accès aureview-serviceétait accroché à l’ancien : il meurt avec lui. SicurlrépondFailed to connect, c’est cela — le superviseur de./scripts/open-ui.shrétablit l’accès tout seul, attendez quelques secondes et recommencez.
3. L’expérience
Une seule requête, avec deux en-têtes fabriqués à la main — un traceparent pour retrouver la trace tout de suite, un baggage qui joue le rôle du service appelant. Comme à la section 1, le produit n’existe pas : la requête échoue, keep-errors la conserve à coup sûr, et vous n’avez pas à courir après le tirage à 25 %.
Ouvrez babbaba9e00000000000000000000010 dans Jaeger et dépliez les spans. Relevé sur le cluster de la formation, en ne gardant que les deux clés du bagage :
Trois choses s’y lisent, et aucune n’était visible au Lab 7.
Le bagage marque des spans que personne n’a écrits. app.tenant=acme est entré par un en-tête HTTP, et il ressort sur GET — le span du client HTTP, créé par un module de l’agent, dans du code qui n’a jamais entendu parler de « tenant ». C’est toute la différence avec un attribut : Span.current().setAttribute(...) ne marque que le span courant, le bagage marque tout ce qui suit dans le contexte.
mobile, puis web. Le span serveur porte la valeur envoyée par le client ; tous les suivants portent web. Entre les deux, il s’est passé ceci, à la ligne 114 de ReviewController.java :
Vous voyez donc une ligne de code agir, et vous voyez sa portée : le try délimite exactement les spans qui portent web. app.tenant, que le code ne connaît pas, traverse sans être touché.
Le frontend ne montre rien. L’en-tête baggage lui est pourtant bien envoyé — le propagateur par défaut de l’agent est tracecontext,baggage, les deux partent ensemble. Mais le frontend est en Node.js, et personne n’y a activé de copie vers les attributs : il reçoit le bagage, le repropage à product-catalog, et n’en dit rien. La copie en attributs est une décision par service, pas une propriété de la trace.
💡 Sans la variable de la section 2, cette même requête donne exactement la trace du Lab 7 : aucun
app.tenant, aucunapp.review.channel, nulle part. Le bagage voyageait déjà — c’est le regard qui manquait.
Cette trace s’arrête tôt : le produit n’existant pas, la requête n’a jamais atteint la base. La section suivante montrera la même chose sur une requête réussie, où le bagage descend deux couches plus bas encore.
4. À quoi ça sert : garder les traces d’un client
Le cas d’usage type du bagage, c’est le tenant. Une requête traverse cinq services ; seul le premier sait de quel client elle vient. Quand product-catalog, quatre sauts plus loin, met trois secondes sur une requête SQL, il n’a aucun moyen de savoir qui il faisait patienter : l’information est restée à l’entrée. Le bagage la lui apporte.
Et une fois app.tenant posé sur tous les spans, le collecteur peut décider dessus. Reprenez la politique du Lab 7 et ajoutez-lui une quatrième règle — « ce client-là, on garde tout » :
💡 Le tail sampling, lui, n’a besoin de rien de tout ça. Les trois politiques du Lab 7 décident sur ce que les spans portent déjà : un statut
ERROR, une durée, un tirage au sort. C’est la règle générale du collecteur — il ne peut trancher que sur ce qui est dans les spans. Le bagage n’y étant pas, la seule façon de décider dessus est de l’y avoir copié en amont, dans l’application. Retirez la variable de la section 2 et cette quatrième politique cesse silencieusement de retenir quoi que ce soit : les requêtes du clientacmeretombent dans les 25 % du tirage ordinaire, sans le moindre message d’erreur nulle part.
⚠️ Ce fichier ne s’empile pas sur celui du Lab 7, il le remplace. Helm fusionne les maps, mais remplace une liste en bloc : un fichier ne contenant que
keep-tenant-acmeeffacerait les trois autres politiques. D’où le fichier de référence71-otel-traces-values.yaml, qui les redonne toutes les quatre.
Vingt requêtes ordinaires par client — ni en erreur, ni lentes, donc soumises aux 25 % de sample-the-rest :
Dans Jaeger, cherchez l’opération GET /api/reviews avec le tag app.tenant=acme, puis app.tenant=globex. Relevé sur le cluster de la formation :
Et la trace complète, promise à la section 3. La requête en erreur s’arrêtait avant la base ; maintenant que les traces acme sont toutes conservées, une requête réussie se retrouve à coup sûr, et le bagage y descend deux couches plus bas :
app.tenant figure sur le span de l’INSERT, créé par le module JDBC de l’agent — du code qui ignore tout de vos clients, et qui n’a jamais reçu cette valeur autrement que par le contexte.
Le compteur du processor dit la même chose, sans compter de lignes à l’écran :
💡 Un seul service copie le bagage — et cela suffit. L’attribut
app.tenantn’existe que sur les spans dureview-service: c’est le seul où la variable de la section 2 est posée. Or le tail sampling raisonne trace par trace, jamais span par span :string_attributeinterroge tous les spans de la trace, la retient dès qu’un seul correspond, et conserve alors la trace entière. CinqPOSTordinaires envoyés avecapp.tenant=acmesur le cluster de la formation le montrent — le tirage à 25 % n’en aurait gardé qu’un :
C’est la boucle complète : le nom du client, connu du seul premier service, voyage par le bagage, se dépose en attribut sur les spans, et devient un critère de tri pour le collecteur.
Sans ce chemin, il n’y aurait qu’un moyen d’être sûr de garder les traces du client acme : garder toutes les traces, de tous les clients — c’est-à-dire renoncer à l’échantillonnage, et à ce qu’il fait économiser.
5. Le revers : c’est un en-tête, et il va loin
Deux propriétés du bagage à ne pas oublier, et elles annoncent le Lab 8.
Le client écrit ce qu’il veut. Vous venez de le faire : app.tenant=acme a été fabriqué à la main dans un curl. Rien ne le distingue d’un bagage légitime — c’est le même problème que le traceparent de l’étape 4 du Lab 7. Un bagage qui arrive de l’extérieur est une déclaration du client, pas un fait ; sur un endpoint public, on le filtre à l’entrée et on repose soi-même les clés de confiance.
Il voyage vers tout l’aval, sans exception. Chaque service instrumenté le repropage au suivant, y compris vers des API tierces si votre code en appelle. N’y mettez donc jamais de donnée personnelle ni de secret : ce serait publier un e-mail ou un jeton dans un en-tête HTTP, chez des gens qui ne l’ont pas demandé — la faute que le Lab 8 corrige sur les attributs, en pire, puisqu’elle sort de votre système. Restez sur des étiquettes courtes et anodines, ce qui tombe bien : 8 Ko et 64 entrées, pas davantage.
6. Désactiver la copie du bagage, et revenir à la politique du Lab 7
Deux commandes, une par changement fait dans cette page : la première retire la copie du bagage vers les attributs de span, la seconde réapplique les trois politiques du Lab 7 — donc retire keep-tenant-acme.
Le - final du nom de variable la supprime — c’est la syntaxe de kubectl set env, la même qui retire JAVA_TOOL_OPTIONS au Lab 2.