[{"data":1,"prerenderedAt":1795},["ShallowReactive",2],{"docs-nav:fr":3,"docs:\u002Ffr\u002Fdocs\u002Finfra\u002Fobservability":189},[4],{"title":5,"path":6,"stem":7,"children":8,"page":52},"Fr","\u002Ffr","fr",[9],{"title":10,"path":11,"stem":12,"children":13},"Qu'est-ce que Fougere","\u002Ffr\u002Fdocs","fr\u002Fdocs\u002Findex",[14,15,19,23,27,31,53,78,123,148],{"title":10,"path":11,"stem":12},{"title":16,"path":17,"stem":18},"Démarrer","\u002Ffr\u002Fdocs\u002Fgetting-started","fr\u002Fdocs\u002F01.getting-started",{"title":20,"path":21,"stem":22},"Une app que vous avez déjà","\u002Ffr\u002Fdocs\u002Fexisting-app","fr\u002Fdocs\u002F02.existing-app",{"title":24,"path":25,"stem":26},"Apportez votre schéma","\u002Ffr\u002Fdocs\u002Fbring-your-schema","fr\u002Fdocs\u002F03.bring-your-schema",{"title":28,"path":29,"stem":30},"La CLI","\u002Ffr\u002Fdocs\u002Fcli","fr\u002Fdocs\u002F04.cli",{"title":32,"path":33,"stem":34,"children":35,"page":52},"Concepts","\u002Ffr\u002Fdocs\u002Fconcepts","fr\u002Fdocs\u002F2.concepts",[36,40,44,48],{"title":37,"path":38,"stem":39},"Philosophie","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fphilosophy","fr\u002Fdocs\u002F2.concepts\u002F1.philosophy",{"title":41,"path":42,"stem":43},"La Frond","\u002Ffr\u002Fdocs\u002Fconcepts\u002Ffrond","fr\u002Fdocs\u002F2.concepts\u002F2.frond",{"title":45,"path":46,"stem":47},"Le chemin le plus court","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fshortest-path","fr\u002Fdocs\u002F2.concepts\u002F3.shortest-path",{"title":49,"path":50,"stem":51},"Le socle","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fthe-base","fr\u002Fdocs\u002F2.concepts\u002F4.the-base",false,{"title":54,"path":55,"stem":56,"children":57,"page":52},"Schéma","\u002Ffr\u002Fdocs\u002Fschema","fr\u002Fdocs\u002F3.schema",[58,62,66,70,74],{"title":59,"path":60,"stem":61},"Entités","\u002Ffr\u002Fdocs\u002Fschema\u002Fentities","fr\u002Fdocs\u002F3.schema\u002F1.entities",{"title":63,"path":64,"stem":65},"Vues","\u002Ffr\u002Fdocs\u002Fschema\u002Fviews","fr\u002Fdocs\u002F3.schema\u002F2.views",{"title":67,"path":68,"stem":69},"La carte d'identité","\u002Ffr\u002Fdocs\u002Fschema\u002Fcard","fr\u002Fdocs\u002F3.schema\u002F3.card",{"title":71,"path":72,"stem":73},"Standard Schema","\u002Ffr\u002Fdocs\u002Fschema\u002Fstandard-schema","fr\u002Fdocs\u002F3.schema\u002F4.standard-schema",{"title":75,"path":76,"stem":77},"Comment un schéma bouge","\u002Ffr\u002Fdocs\u002Fschema\u002Fevolution","fr\u002Fdocs\u002F3.schema\u002F5.evolution",{"title":79,"path":80,"stem":81,"children":82,"page":52},"Métier","\u002Ffr\u002Fdocs\u002Fbusiness","fr\u002Fdocs\u002F4.business",[83,87,91,95,99,103,107,111,115,119],{"title":84,"path":85,"stem":86},"Handlers","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fhandlers","fr\u002Fdocs\u002F4.business\u002F1.handlers",{"title":88,"path":89,"stem":90},"Les ports","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fports","fr\u002Fdocs\u002F4.business\u002F10.ports",{"title":92,"path":93,"stem":94},"Presenters","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fpresenters","fr\u002Fdocs\u002F4.business\u002F2.presenters",{"title":96,"path":97,"stem":98},"Collectors","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fcollectors","fr\u002Fdocs\u002F4.business\u002F3.collectors",{"title":100,"path":101,"stem":102},"Erreurs","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ferrors","fr\u002Fdocs\u002F4.business\u002F4.errors",{"title":104,"path":105,"stem":106},"Seeds","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fseeds","fr\u002Fdocs\u002F4.business\u002F5.seeds",{"title":108,"path":109,"stem":110},"Les faits","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ffacts","fr\u002Fdocs\u002F4.business\u002F6.facts",{"title":112,"path":113,"stem":114},"Le port Storage","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fstorage","fr\u002Fdocs\u002F4.business\u002F7.storage",{"title":116,"path":117,"stem":118},"Des écritures qui tiennent ou tombent ensemble","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ftogether","fr\u002Fdocs\u002F4.business\u002F8.together",{"title":120,"path":121,"stem":122},"Les repositories","\u002Ffr\u002Fdocs\u002Fbusiness\u002Frepositories","fr\u002Fdocs\u002F4.business\u002F9.repositories",{"title":124,"path":125,"stem":126,"children":127,"page":52},"Client","\u002Ffr\u002Fdocs\u002Fclient","fr\u002Fdocs\u002F5.client",[128,132,136,140,144],{"title":129,"path":130,"stem":131},"Queries & commands","\u002Ffr\u002Fdocs\u002Fclient\u002Fqueries-commands","fr\u002Fdocs\u002F5.client\u002F1.queries-commands",{"title":133,"path":134,"stem":135},"Formulaires","\u002Ffr\u002Fdocs\u002Fclient\u002Fforms","fr\u002Fdocs\u002F5.client\u002F2.forms",{"title":137,"path":138,"stem":139},"Session","\u002Ffr\u002Fdocs\u002Fclient\u002Fsession","fr\u002Fdocs\u002F5.client\u002F3.session",{"title":141,"path":142,"stem":143},"invoke","\u002Ffr\u002Fdocs\u002Fclient\u002Finvoke","fr\u002Fdocs\u002F5.client\u002F4.invoke",{"title":145,"path":146,"stem":147},"Back-office","\u002Ffr\u002Fdocs\u002Fclient\u002Fadmin","fr\u002Fdocs\u002F5.client\u002F5.admin",{"title":149,"path":150,"stem":151,"children":152,"page":52},"Infrastructure","\u002Ffr\u002Fdocs\u002Finfra","fr\u002Fdocs\u002F6.infra",[153,157,161,165,169,173,177,181,185],{"title":154,"path":155,"stem":156},"Le gradient","\u002Ffr\u002Fdocs\u002Finfra\u002Fgradient","fr\u002Fdocs\u002F6.infra\u002F1.gradient",{"title":158,"path":159,"stem":160},"Surfaces — REST & GraphQL","\u002Ffr\u002Fdocs\u002Finfra\u002Fsurfaces","fr\u002Fdocs\u002F6.infra\u002F2.surfaces",{"title":162,"path":163,"stem":164},"Déploiement","\u002Ffr\u002Fdocs\u002Finfra\u002Fdeployment","fr\u002Fdocs\u002F6.infra\u002F3.deployment",{"title":166,"path":167,"stem":168},"Hôtes","\u002Ffr\u002Fdocs\u002Finfra\u002Fhosts","fr\u002Fdocs\u002F6.infra\u002F4.hosts",{"title":170,"path":171,"stem":172},"Les sources","\u002Ffr\u002Fdocs\u002Finfra\u002Fsources","fr\u002Fdocs\u002F6.infra\u002F5.sources",{"title":174,"path":175,"stem":176},"Le cycle de vie","\u002Ffr\u002Fdocs\u002Finfra\u002Flifecycle","fr\u002Fdocs\u002F6.infra\u002F6.lifecycle",{"title":178,"path":179,"stem":180},"L'identité entre Fronds","\u002Ffr\u002Fdocs\u002Finfra\u002Fidentity","fr\u002Fdocs\u002F6.infra\u002F7.identity",{"title":182,"path":183,"stem":184},"Observabilité","\u002Ffr\u002Fdocs\u002Finfra\u002Fobservability","fr\u002Fdocs\u002F6.infra\u002F8.observability",{"title":186,"path":187,"stem":188},"Tests","\u002Ffr\u002Fdocs\u002Finfra\u002Ftesting","fr\u002Fdocs\u002F6.infra\u002F9.testing",{"id":190,"title":182,"body":191,"description":1790,"extension":1791,"meta":1792,"navigation":295,"path":183,"seo":1793,"stem":184,"__hash__":1794},"docs_fr\u002Ffr\u002Fdocs\u002F6.infra\u002F8.observability.md",{"type":192,"value":193,"toc":1778},"minimark",[194,198,210,235,354,362,367,370,449,542,553,559,563,570,578,585,591,598,602,663,670,681,690,700,706,713,717,724,735,897,908,915,938,942,1001,1021,1029,1048,1051,1055,1063,1157,1178,1191,1198,1205,1288,1303,1307,1317,1335,1378,1393,1418,1422,1425,1494,1511,1521,1528,1538,1542,1548,1559,1570,1574,1584,1633,1643,1646,1730,1745,1774],[195,196,182],"h1",{"id":197},"observabilité",[199,200,201,205,206,209],"p",{},[202,203,204],"code",{},"@fougere\u002Fobservability"," est optionnel au sens fort : le cœur ne contient aucun code de\ntraçage, seulement un champ ",[202,207,208],{},"trace"," sur l'invocation qu'il transporte et ne lit jamais. Une\napp qui ne l'installe pas ne paie rien.",[211,212,217],"pre",{"className":213,"code":214,"language":215,"meta":216,"style":216},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pnpm add @fougere\u002Fobservability\n","bash","",[202,218,219],{"__ignoreMap":216},[220,221,224,228,232],"span",{"class":222,"line":223},"line",1,[220,225,227],{"class":226},"sbgvK","pnpm",[220,229,231],{"class":230},"s_sjI"," add",[220,233,234],{"class":230}," @fougere\u002Fobservability\n",[211,236,240],{"className":237,"code":238,"language":239,"meta":216,"style":216},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","import { trace, onSpan, otlp, metrics } from '@fougere\u002Fobservability';\n\napp.use(trace());                       \u002F\u002F toutes les opérations\napp.use('post', trace());               \u002F\u002F celles d'une entité\n","ts",[202,241,242,290,297,325],{"__ignoreMap":216},[220,243,244,248,252,256,259,262,264,267,269,272,275,278,282,284,287],{"class":222,"line":223},[220,245,247],{"class":246},"sVHd0","import",[220,249,251],{"class":250},"sP7_E"," {",[220,253,255],{"class":254},"su5hD"," trace",[220,257,258],{"class":250},",",[220,260,261],{"class":254}," onSpan",[220,263,258],{"class":250},[220,265,266],{"class":254}," otlp",[220,268,258],{"class":250},[220,270,271],{"class":254}," metrics",[220,273,274],{"class":250}," }",[220,276,277],{"class":246}," from",[220,279,281],{"class":280},"sjJ54"," '",[220,283,204],{"class":230},[220,285,286],{"class":280},"'",[220,288,289],{"class":250},";\n",[220,291,293],{"class":222,"line":292},2,[220,294,296],{"emptyLinePlaceholder":295},true,"\n",[220,298,300,303,306,310,313,315,318,321],{"class":222,"line":299},3,[220,301,302],{"class":254},"app",[220,304,305],{"class":250},".",[220,307,309],{"class":308},"sGLFI","use",[220,311,312],{"class":254},"(",[220,314,208],{"class":308},[220,316,317],{"class":254},"())",[220,319,320],{"class":250},";",[220,322,324],{"class":323},"sutJx","                       \u002F\u002F toutes les opérations\n",[220,326,328,330,332,334,336,338,341,343,345,347,349,351],{"class":222,"line":327},4,[220,329,302],{"class":254},[220,331,305],{"class":250},[220,333,309],{"class":308},[220,335,312],{"class":254},[220,337,286],{"class":280},[220,339,340],{"class":230},"post",[220,342,286],{"class":280},[220,344,258],{"class":250},[220,346,255],{"class":308},[220,348,317],{"class":254},[220,350,320],{"class":250},[220,352,353],{"class":323},"               \u002F\u002F celles d'une entité\n",[199,355,356,357,361],{},"C'est un ",[358,359,360],"a",{"href":85},"middleware d'app"," ordinaire parce qu'une opération a\ndéjà un cycle de vie et que c'est celui-là — un second système de hooks serait une seconde\nréponse à une question déjà tranchée.",[363,364,366],"h2",{"id":365},"un-span-par-opération","Un span par opération",[199,368,369],{},"Un span porte la durée d'une op et son verdict :",[211,371,373],{"className":237,"code":372,"language":239,"meta":216,"style":216},"onSpan((span) => console.log(span.frond, span.entity, span.operation, span.ms, span.error));\n",[202,374,375],{"__ignoreMap":216},[220,376,377,380,382,384,387,390,394,397,399,402,405,407,410,412,415,417,420,422,424,426,429,431,433,435,438,440,442,444,447],{"class":222,"line":223},[220,378,379],{"class":308},"onSpan",[220,381,312],{"class":254},[220,383,312],{"class":250},[220,385,220],{"class":386},"s99_P",[220,388,389],{"class":250},")",[220,391,393],{"class":392},"sbsja"," =>",[220,395,396],{"class":254}," console",[220,398,305],{"class":250},[220,400,401],{"class":308},"log",[220,403,404],{"class":254},"(span",[220,406,305],{"class":250},[220,408,409],{"class":254},"frond",[220,411,258],{"class":250},[220,413,414],{"class":254}," span",[220,416,305],{"class":250},[220,418,419],{"class":254},"entity",[220,421,258],{"class":250},[220,423,414],{"class":254},[220,425,305],{"class":250},[220,427,428],{"class":254},"operation",[220,430,258],{"class":250},[220,432,414],{"class":254},[220,434,305],{"class":250},[220,436,437],{"class":254},"ms",[220,439,258],{"class":250},[220,441,414],{"class":254},[220,443,305],{"class":250},[220,445,446],{"class":254},"error))",[220,448,289],{"class":250},[450,451,452,463],"table",{},[453,454,455],"thead",{},[456,457,458,461],"tr",{},[459,460],"th",{},[459,462],{},[464,465,466,484,493,504,519,528],"tbody",{},[456,467,468,481],{},[469,470,471,474,475,474,478],"td",{},[202,472,473],{},"traceId"," \u002F ",[202,476,477],{},"spanId",[202,479,480],{},"parentId",[469,482,483],{},"où cette étape se situe dans l'arbre",[456,485,486,490],{},[469,487,488],{},[202,489,409],{},[469,491,492],{},"quelle Frond possédait l'op — l'unité de déploiement, donc la première chose à regrouper",[456,494,495,501],{},[469,496,497,474,499],{},[202,498,419],{},[202,500,428],{},[469,502,503],{},"ce qui a été appelé",[456,505,506,511],{},[469,507,508],{},[202,509,510],{},"startedAt",[469,512,513,514,518],{},"un ",[515,516,517],"strong",{},"instant",", en millisecondes epoch",[456,520,521,525],{},[469,522,523],{},[202,524,437],{},[469,526,527],{},"combien de temps",[456,529,530,535],{},[469,531,532],{},[202,533,534],{},"error",[469,536,537,538,541],{},"le code ",[202,539,540],{},"FougereError"," en cas de refus, absent quand elle a répondu",[199,543,544,545,549,550,552],{},"Deux horloges, et ce ne sont pas la même mesure : l'horloge murale dit ",[546,547,548],"em",{},"quand",", ce qui fait\natterrir deux process sur une même frise ; la monotone dit ",[546,551,527],{},", sans être\ndéplacée par une correction NTP en cours d'appel.",[199,554,555,556,558],{},"Rien n'est ouvert tant qu'aucun collecteur n'est posé — observer est une décision, et le\ncoût de celle que personne n'a demandée est nul plutôt que petit. ",[202,557,379],{}," renvoie de quoi\nse retirer.",[363,560,562],{"id":561},"la-trace-survit-au-fil","La trace survit au fil",[199,564,565,566,569],{},"Le parent voyage sur l'",[515,567,568],{},"invocation",", pas dans un en-tête :",[211,571,576],{"className":572,"code":574,"language":575},[573],"language-text","app                     post.list        traceId a3f… spanId 01\n  └─ blog (séparée)     post.list        traceId a3f… spanId 02, parent 01\n","text",[202,577,574],{"__ignoreMap":216},[199,579,580,581,584],{},"Un en-tête appartient à HTTP seul, et le même appel sur une socket serait arrivé sans\ntrace. L'invocation est ce que ",[546,582,583],{},"tout"," transport porte, donc la trace traverse quelle que\nsoit la topologie devenue — et le format est W3C Trace Context, donc un collecteur la lit à\ncôté de n'importe quoi d'autre.",[199,586,587,588,305],{},"Le même middleware tourne sur les deux moitiés d'une séparation : à la porte où un appel\narrive, et à la doublure d'où il part. C'est pour ça que les chiffres se recoupent d'un\nprocess à l'autre — et pourquoi ",[515,589,590],{},"la différence entre les deux spans est ce qu'a coûté le\nfil",[199,592,593,594,597],{},"Un cas que le fil ne sait pas décrire : un handler qui atteint une seconde Frond construit\nune invocation neuve, donc le parent n'est pas sur cet appel. Il est dans le contexte où le\npremier tourne encore, que ",[202,595,596],{},"currentSpan()"," lit.",[363,599,601],{"id":600},"les-quatre-signaux-depuis-le-span-qui-existe-déjà","Les quatre signaux, depuis le span qui existe déjà",[211,603,605],{"className":237,"code":604,"language":239,"meta":216,"style":216},"const measured = metrics(app);\nonSpan(measured.sink);\n\nmeasured.snapshot();   \u002F\u002F ce qu'il y a à publier maintenant\n",[202,606,607,627,641,645],{"__ignoreMap":216},[220,608,609,612,616,620,622,625],{"class":222,"line":223},[220,610,611],{"class":392},"const",[220,613,615],{"class":614},"s_hVV"," measured",[220,617,619],{"class":618},"smGrS"," =",[220,621,271],{"class":308},[220,623,624],{"class":254},"(app)",[220,626,289],{"class":250},[220,628,629,631,634,636,639],{"class":222,"line":292},[220,630,379],{"class":308},[220,632,633],{"class":254},"(measured",[220,635,305],{"class":250},[220,637,638],{"class":254},"sink)",[220,640,289],{"class":250},[220,642,643],{"class":222,"line":299},[220,644,296],{"emptyLinePlaceholder":295},[220,646,647,650,652,655,658,660],{"class":222,"line":327},[220,648,649],{"class":254},"measured",[220,651,305],{"class":250},[220,653,654],{"class":308},"snapshot",[220,656,657],{"class":254},"()",[220,659,320],{"class":250},[220,661,662],{"class":323},"   \u002F\u002F ce qu'il y a à publier maintenant\n",[199,664,665,666,669],{},"Débit, erreurs et durée sont ",[515,667,668],{},"une seule"," métrique — un histogramme de durées, dimensionné\npar l'op et par le fait qu'elle ait refusé ou non. Son compte est le débit, sa dimension le\ntaux d'erreur, ses tranches la latence. La scinder en trois compteurs publierait trois fois\nles mêmes chiffres et les laisserait diverger.",[199,671,672,673,676,677,680],{},"Le quatrième, la saturation, ne peut pas venir d'un span terminé, puisqu'il parle de ceux\nqui ne le sont ",[515,674,675],{},"pas",". Le middleware les compte et ",[202,678,679],{},"activeCalls()"," le lit.",[682,683,684],"note",{},[199,685,686,689],{},[515,687,688],{},"La cardinalité est bornée par construction"," : frond × entité × opération × {ok, erreur},\nplus le code d'erreur quand il y en a un — toutes déclarées dans le code. Rien qui porte un\nid, un utilisateur ou une trace ne devient jamais une dimension ; c'est la seule erreur dont\nune couche de métriques ne se remet pas.",[199,691,692,693,695,696,699],{},"Passer ",[202,694,302],{}," est optionnel et ne nourrit que la topologie, qui est ",[515,697,698],{},"découverte"," et non\ndéclarée, à partir des deux seules choses qu'un process peut dire honnêtement : une Frond\nqu'il a SCANNÉE tourne ici, une Frond qu'il a APPELÉE sans l'avoir scannée tourne ailleurs.",[211,701,704],{"className":702,"code":703,"language":575},[573],"process catalog   catalog → local        il en détient le code\nprocess shop      shop    → local\nprocess shop      catalog → remote       il l'a appelée, il ne l'a jamais scannée\n",[202,705,703],{"__ignoreMap":216},[199,707,708,709,712],{},"Délibérément pas lu depuis ",[202,710,711],{},"remotes:"," — une clé de config énonce une intention, alors qu'une\nFrond qui a répondu à un appel est un fait. Les deux divergent précisément quand quelque\nchose est mal configuré, c'est-à-dire au moment où un tableau de bord doit avoir raison.",[363,714,716],{"id":715},"le-troisième-signal-les-logs-et-la-seule-chose-qui-en-fait-un","Le troisième signal : les logs, et la seule chose qui en fait un",[199,718,719,720,723],{},"Un log expédié sans identifiant de trace est un log rangé ailleurs. Ce qui en fait le\ntroisième pilier, c'est d'ouvrir une trace et d'y lire les lignes que ",[546,721,722],{},"cet appel"," a\nproduites.",[199,725,726,727,730,731,734],{},"Le logger a une porte — ",[202,728,729],{},"onLog"," dans le cœur — qui remet un ",[515,732,733],{},"enregistrement structuré","\navant tout formatage, parce que ce qui arrive à la console est déjà cuit : codes ANSI,\nbadge, horodatage. Le cœur émet l'enregistrement et ne sait rien de plus ; ce paquet y\nattache le span et parle OTLP.",[211,736,738],{"className":237,"code":737,"language":239,"meta":216,"style":216},"import { onLog, loggerMiddleware, Logger } from '@fougere\u002Fcore';\nimport { logs } from '@fougere\u002Fobservability';\n\napp.use(trace());                                  \u002F\u002F d'abord — le span doit exister\napp.use(loggerMiddleware(new Logger('blog')));     \u002F\u002F une ligne à l'entrée, une à la sortie\n\nonLog(logs({ service: 'blog' }).sink);\n",[202,739,740,772,793,797,816,854,859],{"__ignoreMap":216},[220,741,742,744,746,749,751,754,756,759,761,763,765,768,770],{"class":222,"line":223},[220,743,247],{"class":246},[220,745,251],{"class":250},[220,747,748],{"class":254}," onLog",[220,750,258],{"class":250},[220,752,753],{"class":254}," loggerMiddleware",[220,755,258],{"class":250},[220,757,758],{"class":254}," Logger",[220,760,274],{"class":250},[220,762,277],{"class":246},[220,764,281],{"class":280},[220,766,767],{"class":230},"@fougere\u002Fcore",[220,769,286],{"class":280},[220,771,289],{"class":250},[220,773,774,776,778,781,783,785,787,789,791],{"class":222,"line":292},[220,775,247],{"class":246},[220,777,251],{"class":250},[220,779,780],{"class":254}," logs",[220,782,274],{"class":250},[220,784,277],{"class":246},[220,786,281],{"class":280},[220,788,204],{"class":230},[220,790,286],{"class":280},[220,792,289],{"class":250},[220,794,795],{"class":222,"line":299},[220,796,296],{"emptyLinePlaceholder":295},[220,798,799,801,803,805,807,809,811,813],{"class":222,"line":327},[220,800,302],{"class":254},[220,802,305],{"class":250},[220,804,309],{"class":308},[220,806,312],{"class":254},[220,808,208],{"class":308},[220,810,317],{"class":254},[220,812,320],{"class":250},[220,814,815],{"class":323},"                                  \u002F\u002F d'abord — le span doit exister\n",[220,817,819,821,823,825,827,830,832,835,837,839,841,844,846,849,851],{"class":222,"line":818},5,[220,820,302],{"class":254},[220,822,305],{"class":250},[220,824,309],{"class":308},[220,826,312],{"class":254},[220,828,829],{"class":308},"loggerMiddleware",[220,831,312],{"class":254},[220,833,834],{"class":618},"new",[220,836,758],{"class":308},[220,838,312],{"class":254},[220,840,286],{"class":280},[220,842,843],{"class":230},"blog",[220,845,286],{"class":280},[220,847,848],{"class":254},")))",[220,850,320],{"class":250},[220,852,853],{"class":323},"     \u002F\u002F une ligne à l'entrée, une à la sortie\n",[220,855,857],{"class":222,"line":856},6,[220,858,296],{"emptyLinePlaceholder":295},[220,860,862,864,866,869,871,874,878,881,883,885,887,889,891,893,895],{"class":222,"line":861},7,[220,863,729],{"class":308},[220,865,312],{"class":254},[220,867,868],{"class":308},"logs",[220,870,312],{"class":254},[220,872,873],{"class":250},"{",[220,875,877],{"class":876},"skxfh"," service",[220,879,880],{"class":250},":",[220,882,281],{"class":280},[220,884,843],{"class":230},[220,886,286],{"class":280},[220,888,274],{"class":250},[220,890,389],{"class":254},[220,892,305],{"class":250},[220,894,638],{"class":254},[220,896,289],{"class":250},[199,898,899,900,903,904,907],{},"Le span est lu ",[515,901,902],{},"au moment où la ligne est écrite",", jamais au moment de l'envoi : à ce\nmoment-là l'appel est terminé et le contexte appartient à quelqu'un d'autre. Une ligne\nécrite hors de tout appel (un démarrage, un arrêt) part ",[515,905,906],{},"sans"," identifiant de trace\nplutôt qu'avec un identifiant nul : absent veut dire « hors appel », et un zéro forgé\nrassemblerait tous les démarrages de tous les process en une trace fantôme.",[199,909,910,911,914],{},"Le transfert est un ajout, jamais un remplacement : la console garde sa ligne, et un\ncollecteur qui lève ne coûte la sienne à personne. ",[202,912,913],{},"setLogLevel"," filtre en amont, donc un\nniveau qui n'a jamais été imprimé n'est jamais expédié non plus — un seul endroit où le\nniveau vit.",[682,916,917],{},[199,918,919,920,923,924,923,927,930,931,933,934,937],{},"Chaque niveau prend maintenant sa propre méthode de console (",[202,921,922],{},"debug",", ",[202,925,926],{},"info",[202,928,929],{},"warn",",\n",[202,932,534],{},"). Les deux premiers tombaient sur ",[202,935,936],{},"console.log",", donc rien en aval — un filtre de\nterminal, un collecteur — ne pouvait distinguer une ligne de debug d'une ligne d'info.",[363,939,941],{"id":940},"lenvoyer-quelque-part","L'envoyer quelque part",[211,943,945],{"className":237,"code":944,"language":239,"meta":216,"style":216},"const exporter = otlp({ service: 'blog', metrics: measured });\nonSpan(exporter.sink);\n",[202,946,947,988],{"__ignoreMap":216},[220,948,949,951,954,956,958,960,962,964,966,968,970,972,974,976,978,981,984,986],{"class":222,"line":223},[220,950,611],{"class":392},[220,952,953],{"class":614}," exporter",[220,955,619],{"class":618},[220,957,266],{"class":308},[220,959,312],{"class":254},[220,961,873],{"class":250},[220,963,877],{"class":876},[220,965,880],{"class":250},[220,967,281],{"class":280},[220,969,843],{"class":230},[220,971,286],{"class":280},[220,973,258],{"class":250},[220,975,271],{"class":876},[220,977,880],{"class":250},[220,979,980],{"class":254}," measured ",[220,982,983],{"class":250},"}",[220,985,389],{"class":254},[220,987,289],{"class":250},[220,989,990,992,995,997,999],{"class":222,"line":292},[220,991,379],{"class":308},[220,993,994],{"class":254},"(exporter",[220,996,305],{"class":250},[220,998,638],{"class":254},[220,1000,289],{"class":250},[199,1002,1003,1004,1007,1008,1011,1012,923,1015,923,1018,305],{},"OTLP sur HTTP, par lots chaque seconde, en encodage JSON — pas de protobuf, aucune\ndépendance. Les endpoints prennent la convention par défaut\n(",[202,1005,1006],{},"http:\u002F\u002Flocalhost:4318\u002Fv1\u002Ftraces",", et celui des métriques avec son dernier segment\néchangé) — nommez ",[202,1009,1010],{},"metricsUrl"," quand traces et métriques vont vers deux moteurs différents\nplutôt que vers un collecteur en façade. Les trois signaux sont trois chemins sur la même\nporte : ",[202,1013,1014],{},"\u002Fv1\u002Ftraces",[202,1016,1017],{},"\u002Fv1\u002Fmetrics",[202,1019,1020],{},"\u002Fv1\u002Flogs",[682,1022,1023],{},[199,1024,1025,1028],{},[515,1026,1027],{},"Une métrique sans point de données fait rejeter le lot entier par un collecteur."," Mesuré\nface à Prometheus : il répond 500 et toutes les autres métriques de l'envoi sont perdues\navec — donc un process qui n'appelle personne, et n'a donc aucune arête, ne publie pas de\nmétrique d'arête plutôt qu'une métrique vide.",[199,1030,1031,1032,1035,1036,1039,1040,1043,1044,1047],{},"Un process sur le point de sortir appelle ",[202,1033,1034],{},"flush()"," ; ",[202,1037,1038],{},"stop()"," arrête le timer et envoie ce\nqui reste. ",[202,1041,1042],{},"onError"," est prévenu quand un lot n'a pas pu partir, et vaut le silence par\ndéfaut : ",[515,1045,1046],{},"une trace ne doit jamais casser un appel",", et un collecteur qui lève est un\nexportateur cassé, pas un appel cassé.",[199,1049,1050],{},"Les métriques partent à chaque battement même si aucun span ne s'est terminé — une jauge\nqui cesse d'être publiée se lit « disparue », pas « au repos ».",[363,1052,1054],{"id":1053},"le-câbler-une-fois","Le câbler, une fois",[199,1056,1057,1058,1062],{},"Tout ce qui est sur cette page tient derrière un membre de la ",[358,1059,1061],{"href":1060},"\u002Fdocs\u002Finfra\u002Flifecycle","montée","\nde l'app :",[211,1064,1066],{"className":237,"code":1065,"language":239,"meta":216,"style":216},"const app = await createApp({\n  root,\n  createContainer,\n  extensions: [observability({ service: 'blog', otlp: 'http:\u002F\u002Flocalhost:4318' })],\n});\n",[202,1067,1068,1088,1095,1102,1149],{"__ignoreMap":216},[220,1069,1070,1072,1075,1077,1080,1083,1085],{"class":222,"line":223},[220,1071,611],{"class":392},[220,1073,1074],{"class":614}," app",[220,1076,619],{"class":618},[220,1078,1079],{"class":246}," await",[220,1081,1082],{"class":308}," createApp",[220,1084,312],{"class":254},[220,1086,1087],{"class":250},"{\n",[220,1089,1090,1093],{"class":222,"line":292},[220,1091,1092],{"class":254},"  root",[220,1094,930],{"class":250},[220,1096,1097,1100],{"class":222,"line":299},[220,1098,1099],{"class":254},"  createContainer",[220,1101,930],{"class":250},[220,1103,1104,1107,1109,1112,1115,1117,1119,1121,1123,1125,1127,1129,1131,1133,1135,1137,1140,1142,1144,1147],{"class":222,"line":327},[220,1105,1106],{"class":876},"  extensions",[220,1108,880],{"class":250},[220,1110,1111],{"class":254}," [",[220,1113,1114],{"class":308},"observability",[220,1116,312],{"class":254},[220,1118,873],{"class":250},[220,1120,877],{"class":876},[220,1122,880],{"class":250},[220,1124,281],{"class":280},[220,1126,843],{"class":230},[220,1128,286],{"class":280},[220,1130,258],{"class":250},[220,1132,266],{"class":876},[220,1134,880],{"class":250},[220,1136,281],{"class":280},[220,1138,1139],{"class":230},"http:\u002F\u002Flocalhost:4318",[220,1141,286],{"class":280},[220,1143,274],{"class":250},[220,1145,1146],{"class":254},")]",[220,1148,930],{"class":250},[220,1150,1151,1153,1155],{"class":222,"line":818},[220,1152,983],{"class":250},[220,1154,389],{"class":254},[220,1156,289],{"class":250},[199,1158,1159,1162,1163,1166,1167,1170,1171,1174,1175,305],{},[202,1160,1161],{},"up"," installe le middleware, l'accumulateur et les puits ",[515,1164,1165],{},"dans l'ordre qui compte"," —\n",[202,1168,1169],{},"trace()"," ouvre le span que chaque ligne de log écrite dans l'appel portera, donc installés\ndans l'autre sens les lignes partent non corrélées. ",[202,1172,1173],{},"down"," les retire et vide ce qui est en\ntampon, à l'intérieur de ",[202,1176,1177],{},"dispose()",[199,1179,1180,1181,1183,1184,1186,1187,1190],{},"Ce relâchement n'est pas décoratif. ",[202,1182,379],{}," et ",[202,1185,729],{}," ont toujours RENDU leur retrait et\npersonne ne l'appelait : une app jetée continuait d'alimenter les puits de l'app qui l'avait\nremplacée, donc chaque métrique comptait deux fois. Invisible jusqu'à ce qu'on\n",[358,1188,1189],{"href":1060},"tourne l'anneau",", et définitif ensuite.",[199,1192,1193,1194,1197],{},"Sans ",[202,1195,1196],{},"otlp",", rien ne quitte le processus — et la topologie ci-dessous répond quand même.",[199,1199,1200,1201,1204],{},"Sous un hôte qui écrit lui-même le fichier de boot, une extension se ",[515,1202,1203],{},"nomme"," au lieu de se\npasser : c'est une fonction et le module écrit un fichier, donc les options voyagent à côté\ndu nom, en données.",[211,1206,1208],{"className":237,"code":1207,"language":239,"meta":216,"style":216},"\u002F\u002F nuxt.config.ts\nfougere: {\n  observability: { service: 'blog', otlp: 'http:\u002F\u002Flocalhost:4318' },\n  calls: { panel: true },        \u002F\u002F `false` éteint une clé ; absente, c'est pareil\n}\n",[202,1209,1210,1215,1225,1259,1283],{"__ignoreMap":216},[220,1211,1212],{"class":222,"line":223},[220,1213,1214],{"class":323},"\u002F\u002F nuxt.config.ts\n",[220,1216,1217,1220,1222],{"class":222,"line":292},[220,1218,1219],{"class":226},"fougere",[220,1221,880],{"class":250},[220,1223,1224],{"class":250}," {\n",[220,1226,1227,1230,1232,1234,1236,1238,1240,1242,1244,1246,1248,1250,1252,1254,1256],{"class":222,"line":299},[220,1228,1229],{"class":226},"  observability",[220,1231,880],{"class":250},[220,1233,251],{"class":250},[220,1235,877],{"class":226},[220,1237,880],{"class":250},[220,1239,281],{"class":280},[220,1241,843],{"class":230},[220,1243,286],{"class":280},[220,1245,258],{"class":250},[220,1247,266],{"class":226},[220,1249,880],{"class":250},[220,1251,281],{"class":280},[220,1253,1139],{"class":230},[220,1255,286],{"class":280},[220,1257,1258],{"class":250}," },\n",[220,1260,1261,1264,1266,1268,1271,1273,1277,1280],{"class":222,"line":327},[220,1262,1263],{"class":226},"  calls",[220,1265,880],{"class":250},[220,1267,251],{"class":250},[220,1269,1270],{"class":226}," panel",[220,1272,880],{"class":250},[220,1274,1276],{"class":1275},"syTEX"," true",[220,1278,1279],{"class":250}," },",[220,1281,1282],{"class":323},"        \u002F\u002F `false` éteint une clé ; absente, c'est pareil\n",[220,1284,1285],{"class":222,"line":818},[220,1286,1287],{"class":250},"}\n",[199,1289,1290,1291,1294,1295,1298,1299,1302],{},"Les clés sont déclarées, pas ouvertes : une par paquet d'extension, la clé étant à la fois\nle suffixe du paquet et le nom de l'export — ",[202,1292,1293],{},"calls",", c'est\n",[202,1296,1297],{},"import { calls } from '@fougere\u002Fcalls'",". Un enregistrement ouvert accepterait ",[202,1300,1301],{},"callz: {}","\nen silence.",[363,1304,1306],{"id":1305},"le-panneau-de-dev-ce-que-ce-processus-a-dispatché","Le panneau de dev — ce que ce processus a dispatché",[199,1308,1309,1312,1313,1316],{},[202,1310,1311],{},"@fougere\u002Fcalls"," est la seconde extension optionnelle, et elle ",[515,1314,1315],{},"observe"," au lieu de\nparticiper :",[211,1318,1320],{"className":213,"code":1319,"language":215,"meta":216,"style":216},"pnpm add -D @fougere\u002Fcalls\n",[202,1321,1322],{"__ignoreMap":216},[220,1323,1324,1326,1328,1332],{"class":222,"line":223},[220,1325,227],{"class":226},[220,1327,231],{"class":230},[220,1329,1331],{"class":1330},"stzsN"," -D",[220,1333,1334],{"class":230}," @fougere\u002Fcalls\n",[211,1336,1338],{"className":237,"code":1337,"language":239,"meta":216,"style":216},"extensions: [calls({ max: 500, panel: true })],\n",[202,1339,1340],{"__ignoreMap":216},[220,1341,1342,1345,1347,1349,1351,1353,1355,1358,1360,1364,1366,1368,1370,1372,1374,1376],{"class":222,"line":223},[220,1343,1344],{"class":226},"extensions",[220,1346,880],{"class":250},[220,1348,1111],{"class":254},[220,1350,1293],{"class":308},[220,1352,312],{"class":254},[220,1354,873],{"class":250},[220,1356,1357],{"class":876}," max",[220,1359,880],{"class":250},[220,1361,1363],{"class":1362},"srdBf"," 500",[220,1365,258],{"class":250},[220,1367,1270],{"class":876},[220,1369,880],{"class":250},[220,1371,1276],{"class":1275},[220,1373,274],{"class":250},[220,1375,1146],{"class":254},[220,1377,930],{"class":250},[199,1379,1380,1381,1384,1385,1388,1389,1392],{},"Elle s'abonne à ",[202,1382,1383],{},"app.observe"," — passif, et sa propre panne est avalée — donc elle voit ce\nqu'un middleware ne peut pas voir : un appel refusé ",[515,1386,1387],{},"avant"," tout handler (une route\ninconnue, une entité hébergée ailleurs, un appel arrivé pendant que la porte se vide), et\nla nature de route de chaque appel, si bien qu'une exécution locale et un saut vers un\nautre processus se lisent pareil. À côté des appels, elle tient un anneau borné de lignes\nde log, d'erreurs, et — quand ",[202,1390,1391],{},"@fougere\u002Fadapter-sql"," est là — des requêtes que chaque appel\na émises.",[199,1394,1395,1396,1399,1400,1403,1404,1410,1411,1414,1415],{},"Rien n'est stocké et aucun port n'est ouvert tant que ",[202,1397,1398],{},"panel"," ne le dit pas. L'anneau est\nservi sous ",[202,1401,1402],{},"rpc.calls",", et le lecteur est\n",[358,1405,1407],{"href":1406},"\u002Ffr\u002Fdocs\u002Fcli#fougere-devtools--ce-que-les-apps-en-marche-dispatchent",[202,1408,1409],{},"fougere devtools","\nvia ",[202,1412,1413],{},"\u002F_fougere\u002Fcall",", comme n'importe quel autre consommateur. Une app qui ne l'a jamais\ninstallée répond ",[202,1416,1417],{},"Unknown rpc operation 'calls'. It serves discover.",[363,1419,1421],{"id":1420},"la-forme-du-système-sur-le-fil","La forme du système, sur le fil",[199,1423,1424],{},"On peut demander à une app dans quelle forme elle est, sur le même fil que tout le reste :",[211,1426,1428],{"className":237,"code":1427,"language":239,"meta":216,"style":216},"await call({ entity: 'rpc', op: 'topology' });\n\u002F\u002F { fronds: [{ frond: 'shop', placement: 'local', entities: 2, doors: 2 },\n\u002F\u002F             { frond: 'catalog', placement: 'remote', entities: 0, doors: 0 }],\n\u002F\u002F   edges:  [{ from: 'shop', to: 'catalog', count: 12, errors: 1 }],\n\u002F\u002F   active: 0, since: 1755861234567 }\n",[202,1429,1430,1474,1479,1484,1489],{"__ignoreMap":216},[220,1431,1432,1435,1438,1440,1442,1445,1447,1449,1452,1454,1456,1459,1461,1463,1466,1468,1470,1472],{"class":222,"line":223},[220,1433,1434],{"class":246},"await",[220,1436,1437],{"class":308}," call",[220,1439,312],{"class":254},[220,1441,873],{"class":250},[220,1443,1444],{"class":876}," entity",[220,1446,880],{"class":250},[220,1448,281],{"class":280},[220,1450,1451],{"class":230},"rpc",[220,1453,286],{"class":280},[220,1455,258],{"class":250},[220,1457,1458],{"class":876}," op",[220,1460,880],{"class":250},[220,1462,281],{"class":280},[220,1464,1465],{"class":230},"topology",[220,1467,286],{"class":280},[220,1469,274],{"class":250},[220,1471,389],{"class":254},[220,1473,289],{"class":250},[220,1475,1476],{"class":222,"line":292},[220,1477,1478],{"class":323},"\u002F\u002F { fronds: [{ frond: 'shop', placement: 'local', entities: 2, doors: 2 },\n",[220,1480,1481],{"class":222,"line":299},[220,1482,1483],{"class":323},"\u002F\u002F             { frond: 'catalog', placement: 'remote', entities: 0, doors: 0 }],\n",[220,1485,1486],{"class":222,"line":327},[220,1487,1488],{"class":323},"\u002F\u002F   edges:  [{ from: 'shop', to: 'catalog', count: 12, errors: 1 }],\n",[220,1490,1491],{"class":222,"line":818},[220,1492,1493],{"class":323},"\u002F\u002F   active: 0, since: 1755861234567 }\n",[199,1495,1496,1499,1500,1503,1504,1507,1508,1510],{},[515,1497,1498],{},"Rien là-dedans n'est déclaré."," Un Frond est ",[202,1501,1502],{},"local"," parce que ce processus l'a scanné,\n",[202,1505,1506],{},"remote"," parce qu'il a répondu à un appel que personne ici n'héberge — délibérément pas lu\ndepuis ",[202,1509,711],{},", qui énonce une intention. Les deux divergent exactement quand quelque\nchose est mal configuré, c'est-à-dire quand un tableau de bord doit avoir raison.",[199,1512,1513,1516,1517,1520],{},[202,1514,1515],{},"rpc.topology"," est déclarée par ce paquet, pas par le cœur — donc une app qui ne l'a jamais\ninstallé refuse l'op par son nom, et ce refus est toute la dégradation dont un lecteur a\nbesoin. Elle est à côté de ",[202,1518,1519],{},"rpc.discover"," : la carte dit ce qu'un processus HÉBERGE, celle-ci\ndit dans quelle forme il est.",[199,1522,1523,1524,1527],{},"Un ",[202,1525,1526],{},"entities: 0, doors: 0"," sur un distant n'est pas un Frond vide — sa forme est publiée par\nle processus qui le possède, sous son propre nom de service.",[199,1529,1530,1533,1534,1537],{},[202,1531,1532],{},"@fougere\u002Fadmin"," la lit sur sa page ",[515,1535,1536],{},"Topologie",", qui est la même réponse dessinée : ce qui\ntourne ici, ce qui a répondu d'ailleurs, et chaque chemin d'appel observé entre les deux.",[363,1539,1541],{"id":1540},"ce-qui-est-du-signal-et-ce-qui-nen-est-pas","Ce qui est du signal, et ce qui n'en est pas",[199,1543,1544,1545,1547],{},"Les spans sont nommés pour le sujet plutôt que pour la lecture qu'on en fait aujourd'hui :\nune durée et un verdict sont matière à métrique autant qu'à trace, et un exportateur pour\nl'une ou l'autre se branche sur le même ",[202,1546,379],{},". C'est pour ça que les collecteurs sont une\nliste — les deux exportateurs lisent la même valeur au lieu que le middleware la produise\ndeux fois.",[199,1549,1550,1551,1554,1555,1558],{},"L'appelant d'un appel séparé est ",[358,1552,1553],{"href":179},"établi, non prétendu"," : dès qu'un\nrécepteur vérifie une enveloppe, ",[202,1556,1557],{},"invocation.caller"," nomme la Frond qui a signé, et absent\nveut dire que rien n'a été établi — jamais « pair inconnu ».",[199,1560,1561,1562,1565,1566,1569],{},"Rien ici ne le lit encore. Un graphe de services tiré d'un appelant vérifié serait un\n",[515,1563,1564],{},"fait"," plutôt qu'une inférence depuis une adresse, et la dimension est bornée comme les\nautres, donc les deux s'emboîtent — mais le span ne porte aucun ",[202,1567,1568],{},"caller"," aujourd'hui, et\ncette page préfère le dire plutôt que de vous le faire chercher.",[363,1571,1573],{"id":1572},"sur-un-tableau-de-bord","Sur un tableau de bord",[199,1575,1576,1577,1580,1581,1583],{},"Tout ce qui parle OTLP lit ceci tel quel. Deux collecteurs ont été essayés pendant l'écriture\nde cette page — Jaeger avec Prometheus, puis SigNoz — et passer de l'un à l'autre a changé\n",[515,1578,1579],{},"deux chaînes d'URL"," : aucune instrumentation, aucun champ de span, aucune métrique. C'est\nce que rapporte le fait de suivre le standard, et c'est le même marché que ",[202,1582,711],{}," passe\npour la topologie.",[211,1585,1587],{"className":213,"code":1586,"language":215,"meta":216,"style":216},"pnpm -C demos\u002Fobservability dev      # trois Fronds, trois process\npnpm -C demos\u002Fobservability load     # k6, par paliers\npnpm -C demos\u002Fobservability signoz   # un collecteur, OTLP sur 4318, UI sur 8080\n",[202,1588,1589,1605,1619],{"__ignoreMap":216},[220,1590,1591,1593,1596,1599,1602],{"class":222,"line":223},[220,1592,227],{"class":226},[220,1594,1595],{"class":1330}," -C",[220,1597,1598],{"class":230}," demos\u002Fobservability",[220,1600,1601],{"class":230}," dev",[220,1603,1604],{"class":323},"      # trois Fronds, trois process\n",[220,1606,1607,1609,1611,1613,1616],{"class":222,"line":292},[220,1608,227],{"class":226},[220,1610,1595],{"class":1330},[220,1612,1598],{"class":230},[220,1614,1615],{"class":230}," load",[220,1617,1618],{"class":323},"     # k6, par paliers\n",[220,1620,1621,1623,1625,1627,1630],{"class":222,"line":299},[220,1622,227],{"class":226},[220,1624,1595],{"class":1330},[220,1626,1598],{"class":230},[220,1628,1629],{"class":230}," signoz",[220,1631,1632],{"class":323},"   # un collecteur, OTLP sur 4318, UI sur 8080\n",[199,1634,1635,1638,1639,1642],{},[202,1636,1637],{},"demos\u002Fobservability",", c'est cette page en marche : un ",[202,1640,1641],{},"cart.checkout"," qui atteint un catalog\net une Frond shipping dans deux autres process, sous charge par paliers. Son code métier ne\ndit rien du fait d'être observé.",[199,1644,1645],{},"Ce qu'il faut y construire, dans l'ordre où un lecteur en a besoin :",[450,1647,1648,1658],{},[453,1649,1650],{},[456,1651,1652,1655],{},[459,1653,1654],{},"panneau",[459,1656,1657],{},"requête",[464,1659,1660,1670,1682,1692,1702,1713],{},[456,1661,1662,1665],{},[469,1663,1664],{},"Débit",[469,1666,1667],{},[202,1668,1669],{},"sum(rate(fougere_operation_duration_seconds_count[1m]))",[456,1671,1672,1675],{},[469,1673,1674],{},"Taux d'erreur",[469,1676,1677,1678,1681],{},"la même, sur ",[202,1679,1680],{},"fougere_outcome=\"error\"",", divisée par le total",[456,1683,1684,1687],{},[469,1685,1686],{},"Latence p95",[469,1688,1689],{},[202,1690,1691],{},"histogram_quantile(0.95, sum by (le) (rate(…_bucket[1m])))",[456,1693,1694,1697],{},[469,1695,1696],{},"Saturation",[469,1698,1699],{},[202,1700,1701],{},"fougere_operations_active",[456,1703,1704,1707],{},[469,1705,1706],{},"Par Frond",[469,1708,1709,1710],{},"n'importe laquelle des précédentes, ",[202,1711,1712],{},"sum by (fougere_frond)",[456,1714,1715,1718],{},[469,1716,1717],{},"Graphe de services",[469,1719,1720,1723,1724,474,1727],{},[202,1721,1722],{},"fougere_calls_total"," par ",[202,1725,1726],{},"fougere_from",[202,1728,1729],{},"fougere_to",[199,1731,1732,1733,1736,1737,1740,1741,1744],{},"Deux choses valent mieux qu'un cinquième graphique. Une ",[515,1734,1735],{},"heatmap"," de\n",[202,1738,1739],{},"sum by (le) (rate(…_bucket[1m]))"," montre où vivent réellement les appels — un p95 masque\nune distribution bimodale, une heatmap ne le peut pas. Et une ",[515,1742,1743],{},"table des opérations triée\npar p95"," : une seule opération lente à 20 % du trafic tire un quantile global loin de tout\nce qui est réel, et seul le tri dit laquelle.",[682,1746,1747],{},[199,1748,1749,1750,1753,1754,1757,1758,1761,1762,1765,1766,1769,1770,1773],{},"Prometheus renomme les métriques OTLP à l'ingestion : ",[202,1751,1752],{},"fougere.operation.duration"," en unité\n",[202,1755,1756],{},"s"," devient ",[202,1759,1760],{},"fougere_operation_duration_seconds",", et ",[202,1763,1764],{},"fougere.frond"," devient le label\n",[202,1767,1768],{},"fougere_frond",". Lisez les noms sur ",[202,1771,1772],{},"\u002Fapi\u002Fv1\u002Flabel\u002F__name__\u002Fvalues"," plutôt que de les\ndeviner.",[1775,1776,1777],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":216,"searchDepth":292,"depth":292,"links":1779},[1780,1781,1782,1783,1784,1785,1786,1787,1788,1789],{"id":365,"depth":292,"text":366},{"id":561,"depth":292,"text":562},{"id":600,"depth":292,"text":601},{"id":715,"depth":292,"text":716},{"id":940,"depth":292,"text":941},{"id":1053,"depth":292,"text":1054},{"id":1305,"depth":292,"text":1306},{"id":1420,"depth":292,"text":1421},{"id":1540,"depth":292,"text":1541},{"id":1572,"depth":292,"text":1573},"Un span par opération, une trace qui survit à n'importe quel transport, et les quatre signaux qui en dérivent — optionnel, et gratuit quand il n'est pas installé.","md",{},{"title":182,"description":1790},"Zzg0ZKJnpjyXpiLJdn9iAFjtTjobBcRB6IvuqOCvdNM",1788598419940]