Salut,
au sujet des logs, sous forme de fichier externe de préférence pour pouvoir être récupéré en effet (mais on pourrait rediriger la console dans un fichier), la règle principale que je respecte personnellement est qu'il faut un maximum d'informations et qu'elles soient pertinentes.
Comment déterminer les informations nécessaires et suffisantes :
- le niveau de log indique l'importance du message par rapport au bon ou mauvais fonctionnement de l'application
- le niveau information indique des messages qui permettent de savoir ce que l'application a fait : par exemple Object truc d'id=103 effacé par user d'id=233
- le niveau alerte indique par exemple un évenement anormal auquel il est nécessaire de porter une attention particulière : par exemple, Nom d'objet truc d'id=127 non précisé : non par défaut utilisé "Sans nom 12". Ou "Tentative d'accès non autorisé à l'objet d'id 208 par l'utilisateur d'id 233 depuis la vue xxx : cet objet ne devrait pas être visible pour cet utilisateur
- ...
- pour les messages d'erreur, en plus du contexte de l'erreur (les informations sur les objets concernés par l'erreur), il est nécessaire de savoir ou se trouve l'erreur dans le code et quelles circonstances ont mené à l'erreur
- la stacktrace permet en particulier de savoir ou se trouve l'erreur, et quelle "chaine" d'appel y a mené
- l'objet qui est concerné par l'erreur pour lequel il faudra peut être intervenir (pour le corriger, le restaurer, etc...) : par exemple, effacement de l'objet bidule d'id 128 annulé à cause d'un time out : objet marqué comme désactivé.
- le contexte de l'erreur : par exemple, "erreur lors du changement de la phase de workflow de l'objet d'id 45 de la phase d'id 12 à la phase d'id 11 à cause de l'erreur suivant : nullpointerexception..." suivi d'une stacktrace
- enfin d'une manière générale il est toujours utile d'avoir pour tout message
- l'endroit ou se trouve l'appel de la méthode de logging (pour retrouver l'emplacement dans le code), donc en gros le nom de la classe, et le numéro de la ligne (éventuellement le nom de la méthode, mais ce n'est pas absolument nécessaire pour moi, c'est un confort)
- le nom du thread
- le timestamp de l'évenement (sous forme de date et heure lisible, et éventuellement de timestamp pour faire des sélections d'intervalles)
De plus, il est important que le process de logging ne pénalise pas le déroulement du code : il faut veiller à ce que le maximum de cpu consommé par l'opération de logging se déroule dans un thread différent de celui qui à provoqué la demande de production du message, tout en conservant l'ordre temporelle de production de tous les messages (ce que les API de logging sont prévues pour faire, mais qui doivent parfois être paramétrées pour).
Il est peut être utile par exemple de ne pas invoquer la méthode de logging si le niveau n'est pas actif, si le message est construit à partir d'un processus long : le message n'étant pas logué au final, autant ne pas consommer de cpu pour produire.
Il faut également veiller à ce que le logging lui même ne puissent pas provoquer d'erreur nécessitant elle même un log...
Enfin, il est nécessaire de fusionner la trace de la console (le system.out et le system.err) de manière claire avec le log : une application peut utiliser des apis ou des programmes externes qui tracent malheureusement dans la console et ces messages peuvent être indispensable à comprendre un dysfonctionnement.
En ce qui concerne le nombre de classes et "l'insufisance" dont tu parles :
- je ne vois pas ce que tu veux dire par insuffisance
- Faire des classes pour faire des classes, ne sert à rien. Il y a deux approches pour moi, lorsqu'on utilise une api de loggin, mais elles ne concernent pas le nombre de classes, mais le nombre d'instances : soit on place dans chaque classe une variable statique attachée à la classe et dédiée au log, ce qui permet d'éviter l'appel systématique à la fabrique, mais consomme de la mémoire, soit faire une méthode utilitaire avec les méthodes qui permettent de simplifier la génération du log (ces méthodes veillant à formater les messages sans qu'on soit obligé de répéter ce code pour chaque appel).
On peut mettre en place éventuellement des classes du type handler : par exemple une classe chargé de gérer le cas d'exception, qu'il suffit d'appeler dans un catch par un truc dans le genre :
DBErrorHandler.handle(exception, DBACTION.DELETE, id1, id2)
et qui gère automatiquement la production d'un message du genre "une erreur a eu lieu lors d'une opération de type <type d'opération> ; les identifiants des objets concérnés étaient <liste des ids>"
Pour finir, j'ai mis tous les exemples de messages en français, mais je logue toujours pour ma part en anglais, c'est plus compact, plus cohérent, plus international, etc...
Partager