Journalisez la décision, pas seulement le résultat, ou le débogage échoue
L'affirmation Quand quelque chose dans votre application se comporte de façon inattendue — un client facturé du mauvais montant, une commande acheminée au mauvais entrepôt, un raba...
L'affirmation
Quand quelque chose dans votre application se comporte de façon inattendue — un client facturé du mauvais montant, une commande acheminée au mauvais entrepôt, un rabais appliqué ou non — les journaux vous diront habituellement ce qui est arrivé mais non pourquoi le système a décidé de le faire. Le résultat est enregistré ; le raisonnement qui l'a produit ne l'est pas. Journaliser les intrants et le chemin de décision, non seulement le résultat final, est la différence entre diagnostiquer un problème en minutes et être incapable de l'expliquer du tout, et cela coûte quelques lignes de journal de plus placées là où les décisions se prennent.
L'écart entre le quoi et le pourquoi
La plupart de la journalisation enregistre des résultats : « commande 8812 expédiée de l'entrepôt B », « client facturé 47,20 $ », « rabais non appliqué ». Ceux-ci vous disent ce que le système a fait, et quand le résultat est correct c'est tout ce qu'il faut. Mais quand le résultat est faux, le journal de résultat est presque inutile, car il vous donne la conclusion sans aucun du raisonnement qui y a mené. Pourquoi l'entrepôt B et non A ? Pourquoi 47,20 $ et non 42,00 $ ? Pourquoi aucun rabais ? La décision qui a produit le résultat — les intrants qu'elle a considérés et la règle qu'elle a appliquée — s'est produite invisiblement, et sans relevé de celle-ci vous en êtes réduit à deviner une logique que vous ne pouvez pas voir.
C'est la différence entre un journal qui vous laisse confirmer que le système a fonctionné et un journal qui vous laisse comprendre pourquoi il a fait ce qu'il a fait. Le premier suffit jusqu'à ce que quelque chose tourne mal ; le second est ce dont vous avez besoin précisément quand quelque chose a mal tourné.
Journalisez les intrants de la décision
La première chose à capter est ce que le code savait au moment où il a décidé. Une décision d'acheminement, un calcul de prix, une vérification d'admissibilité — chacun prend des intrants, et ces intrants sont ce qu'il vous faut pour reconstruire pourquoi la décision a penché comme elle l'a fait :
// pas seulement le resultat, mais ce qui l'a nourri :
log.info("decision d'acheminement", {
order_id: order.id,
ship_to_region: order.region,
warehouse_stock: { A: 0, B: 14 }, // les intrants
chosen: "B",
reason: "A en rupture pour la region", // le pourquoi
});
Désormais, quand quelqu'un demande pourquoi la commande 8812 a été expédiée de l'entrepôt le plus loin, le journal répond complètement : l'entrepôt A avait un stock nul, donc B a été choisi. Vous n'inférez pas le raisonnement à partir du résultat ; vous avez enregistré le raisonnement tel qu'il s'est produit. Les intrants et la raison énoncée transforment un résultat inexplicable en un résultat évident.
Enregistrez quelle branche a été prise et pourquoi
Là où le code fait un choix entre des chemins, journalisez quel chemin il a pris et la condition qui l'a décidé. C'est particulièrement précieux pour les décisions qui produisent le plus souvent la confusion des clients — qu'un rabais, un frais, une taxe ou une exception s'applique :
if (order.total >= freeShippingThreshold) {
log.info("livraison gratuite appliquee", { total: order.total, threshold });
// ...
} else {
log.info("livraison facturee", { total: order.total, threshold, short_by: threshold - order.total });
}
La seconde ligne de journal est la précieuse : elle enregistre non seulement que la livraison a été facturée mais que la commande manquait d'un montant précis au seuil de livraison gratuite. Quand le client écrit pour demander pourquoi on lui a facturé la livraison, la réponse est dans le journal, exacte et immédiate, plutôt que d'exiger de quelqu'un qu'il reconstruise la logique du seuil à la main en espérant ne pas se tromper.
Portez un identifiant à travers toute la décision
Une décision s'étend souvent sur plusieurs étapes et plusieurs composants, et pour la reconstruire vous devez rassembler toutes les lignes de journal liées. Un identifiant de corrélation — un identifiant de requête, un identifiant de commande — attaché à chaque ligne de journal de la chaîne vous laisse récupérer l'histoire complète d'une décision à travers tout le système en une seule requête. Sans lui, les lignes de journal de la décision sont éparpillées parmi celles de tous les autres et ne peuvent être assemblées ; avec lui, un seul filtre produit tout le raisonnement dans l'ordre. C'est la même discipline d'identifiant qui rend les journaux distribués utiles, appliquée précisément pour que le chemin complet d'une seule décision puisse être ramené ensemble après coup.
Ce qu'il ne faut pas journaliser
Journaliser les intrants de décision se heurte directement à une contrainte de confidentialité, car les intrants d'une décision incluent souvent exactement les données sensibles que vous ne devez pas éparpiller dans vos journaux. Le numéro de carte qui a déterminé si un paiement a été accepté, le dossier personnel complet qui a nourri une vérification d'admissibilité, les identifiants qui ont gouverné une décision d'accès — ceux-ci ne doivent pas paraître dans le journal même s'ils étaient des intrants de la décision. Journalisez le fait et la forme non sensible de l'intrant, non la valeur sensible elle-même : journalisez que la carte a été refusée et le code de refus, non le numéro de carte ; journalisez que la vérification d'âge a réussi, non la date de naissance. Le but est assez pour reconstruire le raisonnement, sans transformer vos journaux en une seconde copie des données que vous êtes obligé de protéger.
Le test à appliquer
Pour chaque décision importante que prend votre système — celles qui, quand elles sont fausses, engendrent une plainte de client ou une enquête interne — demandez si vos journaux actuels vous laisseraient expliquer une instance précise après coup. Pourriez-vous dire à un client exactement pourquoi on lui a facturé ce qu'on lui a facturé, acheminé où on l'a acheminé, accordé ou refusé ce qu'il a demandé, en n'utilisant que ce qui est dans les journaux ? Si la réponse est que vous pourriez voir le résultat mais devriez deviner le raisonnement, alors vous journalisez des résultats et non des décisions, et la prochaine fois qu'une de ces décisions sera questionnée vous reconstruirez la logique à partir du résultat au lieu de la lire dans le relevé. Ajoutez les intrants et la raison au point de décision, et la question « pourquoi a-t-il fait cela » cesse d'être une enquête pour devenir une consultation.