Comment créer des logs d'application SaaS

Cet article présente une approche systématique pour générer les logs applicatifs d'applications SaaS. Cet publication fait suite à l'article qui présente les principes d'architecture Twelve Factor App, et se concentre sur le le 11ème facteur:
Les logs applicatifs doivent être considérés comme un journal d'évènements.

Pourquoi la journalisation est importante?
La journalisation est un mécanisme permettant de suivre le comportement d'exécution d'une application, principalement à des fins de débogage/traçage. Dans presque toutes les applications considérablement complexes/vitales, la tenue d’un journal d’application approprié est indispensable ; par conséquent, tout développeur doit être familier avec le paradigme de la journalisation.
Comment ça marche?

Lorsque un client passe une commande en utilisant cette l'application mobile illustrée ci-dessus:
- Le client va cliquer sur le bouton dans son app mobile pour soumettre sa commande
- L'app va envoyer une requête au service
User
pour obtenir les informations du client. - Le service
User
va retourner les informations du client, et enregistrer dans son journaluser.log
un événement associé à cette requête. - L'app en soumettre la commande au service
Order
- Le service
Order
va traiter la commande et enregistrer dans son journalorder.log
un événement associé à ce traitement. - L'app va enregistrer dans son journal
app.log
un événement associé à cette interaction.
Chacune des interactions des usagers laisse des traces dans le journal de chaque nœud du système.
Quelles informations à inclure dans le journal?
Voici la liste des informations que tout événement d'un journal d'application devrait inclure:
datetime
: La date et l'heure pour comprendre quand l'événement est survenu. Il est aussi important de standardiser l'affichage en utilisant le format ISO 8601.level
: La priorité de l'événement. Les frameworks de journalisation standard tels que Log4J définissent les prioritésTRACE
,DEBUG
,INFO
,WARNING
,ERROR
.message
: Une message convivial pour les humains qui décrit l'événement.correlation_id
: Il s'agit d'un identificateur qui permet de corréler les événements associés à une interactions donnée. Cet identificateur est souvent appelé aussispan_id
outrace_id
device_id
: il s'agit de l'identificateur de l'appareil qui a initié l'événement. Cette information permet de d'identifier les événements d'un appareil donné.client_id
: Il s'agit de l'identificateur du service ou de l'application qui a envoyé la requête associée à l'événement.tenant_id
: Pour les applications mutualisées (multi-tenant), letenant_id
identifie l'organisation associée à l'événement.build_id
: La version du logiciel qui a produit l'événement.hostname
: Le nom de la machine ou l'événement a été généré.error_code
: En cas d'erreur, le code d'erreurexception
: En cas d'erreur causée par une exception, le nom de la classe de l'exception. Ex:java.lang.NulPointerException
.exeption_message
: Le message de l'exception qui a causé l'erreur.exception_stacktrace
: La trace complète de l'exception qui a causé l'erreur.http_endpoint
: le nom de l'opération du web-service qui produit l'événement. Ex:/v1/users
http_method
: La méthode HTTP du service web qui produit l'événement (GET
,PUT
,POST
,DELETE
,OPTIONS
)http_status
: Le statut d'appel HTTP du service web. Vous pouvez vous référer à cet article pour en savoir plus sur les statut HTTP.latency_millis
: Le temps de traitement de l'opération en millisecondes.- Les paramètres d'entrée, ce qui permet de comprendre les intrants associées à l'événement.
- Les paramètres de sorties, ce qui permet de comprendre le extrants associé à l'événement
Quel format pour le journal?
Le journal d'événements doit être construit pour être:
- Analysés, traités et stockés par des machines
- Lus, compris et utilisé par les humains pour leur permettre de diagnostiquer les problèmes éventuels.
Les formats de choix pour un journal d'événements:
1. Le format clé/valeur
date_time="06/Nov/2014:19:10:38 +0000" level=INFO build_id=v1.14.5 device_id=8ec8ee57-139b-454e-b7be-9d3dd089d17f correlation_id=f346310c-accf-4abc-9b69-c81d855c7139 client_id=user_service tenant_id=555 http_method=GET http_endoint="/news/53f8d72920ba2744fe873ebc.html" http_status=404 message="User created"
2. Le format JSON
{ "date_time": "2023-11-24T22:24:17Z", "level": "INFO", "build_id": "v1.14.5", "device_id": "8ec8ee57-139b-454e-b7be-9d3dd089d17f", "correlation_id": "f346310c-accf-4abc-9b69-c81d855c7139", "client_id=user_service", "tenant_id": 555 , "http_method": "GET", "http_endoint": "/news/53f8d72920ba2744fe873ebc.html", "http_status": 404, "message": "User created" }
Exemple
Revenons a l'exemple d'envoie de commande pour comprendre comment la journalisation est faite à travers les différents nœuds du système.

1. L'usager soumet sa commande
2. L'app mobile va:
- Générer un
correlation_id
unique pour identifier cette interaction. Il est recommandé d'utiliser les UUIDs offert par tous les langages de programmation. - Récupérer l'identifiant de l'appareil (
device_id
). - Récupérer l'identifiant de l'app (
client_id
). L'identifiant de l'app est une constante définie par le développeur de l'application (Ex:amz_app
). - Envoyer la requête
GET /users/555
au serviceUser
, en incluant dans l'entête de la requête lescorrelation_id
,device_id
etclient_id
.
3. Le service User
va:
- Rechercher les informations de l'usager
555
. - Enregistrer dans
user.log
l'événements associé à la requête, en incluant: lescorrelation_id
,device_id
etclient_id
qu'il a extrait de l'entête de la requête; l'intrantuser_id
. - Retourner à l'app les informations de l'usager.
4. L'app mobile va envoyer la requête POST /orders
au service Order
, en incluant dans l'entête de la requête les correlation_id
, device_id
et client_id
.
5. Le service Order
va
- Créer la commande
- Enregistrer dans
order.log
l'événement associé à la requête, en incluant: lescorrelation_id
,device_id
etclient_id
qu'il a extrait de l'entête de la requête; les intrantsuser_id
.product_id
etquantity
; l'extrantorder_id
. - Retourner à l'app les informations de la commande créée
6. L'app va enregistrer dans app.log
l'événement associé a cette interaction.
Autres considérations
Comment récupérer l'identifiant de l'appareil?
- Dans le cas d'app mobile, les plateformes Android et iOS offrent des API pour accéder aux informations de l'appareil, dont son identifiant.
- Dans le cas d'app web, il faut installer un cookie dans le navigateur contenant un UUID, et l'utiliser le comme
device_id
.
Et si le service appelle un autre en aval?
Dans le cas ou un service Order
doit appeler Delivery
pour initier la livraison de la commande, il faut que Order
:
- Transfère les
device_id
etcorrelation_id
dans l'entête de la requête versDelivery
. - Envoie comme
client_id
l'identificateur du serviceOrder
.

L'agrégation des journaux
A cause du fait que chaque nœud du système a son propre journal, il devient très vite fastidieux de naviguer à travers le différents journaux. Il faut donc considérer intégrer dans votre architecture un système de gestion des journaux d'événements qui va fusionner tous les journaux dans une seule base de données, et offrir un interface pour accéder aux événements. Des plateformes telles que New Relic, Datadog, Splunk etc. offrent de telles fonctionnalités.

Si vous avez aimé l'article, montrez votre soutien avec un ❤️ et abonnez vous a mon blog! Votre engagement m’inspire!