Je me demande où le code de journalisation devrait aller. Par exemple, est-ce que mon référentiel devrait journaliser ses propres erreurs? Ou devrais-je journaliser toutes les erreurs de l'interface utilisateur / du contrôleur? Y a-t-il des principes de conception généraux à ce sujet, ou est-ce que quelqu'un a un lien vers un bon article ou quelque chose du genre.
Réponses
Trop de publicités?L'enregistrement et le suivi est (à mon avis) un art délicat, savoir quoi enregistrer et où prend de l'expérience.
J'ai découvert que le meilleur (pire?) moyen d'apprendre l'art de l'enregistrement est de vivre la douleur de tenter de diagnostiquer des problèmes avec un enregistrement approximatif!
Tout ce que je peux vous offrir, ce sont des conseils :
Pensez à comment le message de journalisation sera utilisé.
La clé d'une bonne journalisation est de réfléchir à comment le message de journalisation sera utilisé en cas de problème. En revanche, le pire aspect de la mauvaise journalisation est que vous vous en rendrez compte uniquement lorsque vous rencontrez un problème et que vous n'avez pas assez d'informations!
Pensez toujours à ce que dit le message de journalisation à la personne qui le lit , par exemple:
- Une appel API Windows a échoué? Dans ce cas, vous devez probablement enregistrer le
HRESULT
et le résultat deGetLastError
(s'il y en a un) pour qu'il soit utile. - Impossible de trouver une entrée dans une collection? Sans le nom de l'entrée non trouvée, personne ne peut déduire grand-chose - il serait également utile de connaître le nombre d'éléments de la collection pour savoir si elle est vide.
Une erreur courante est de ne pas réfléchir aux informations requises lors de la journalisation, cependant, vous devriez également penser attentivement à quand un message sera journalisé - si un message est journalisé fréquemment en fonctionnement normal alors au mieux, son utilité est discutable et au pire, ce message de journal peut être trompeur.
Assurez-vous également de pouvoir identifier ce qui a enregistré un message. Si tout ce que vous pouvez voir dans un journal est une chaîne qui apparaît de nombreuses fois dans votre code (ou pire encore pas du tout!), alors vous devrez faire preuve de déduction et de ruse pour déterminer d'où vient ce message de journal (et sans savoir d'où provient un message, vous avez peu de chances de le comprendre)
- Dans les applications multi-threadées / multi-processeurs, enregistrez toujours l'ID du thread et l'ID du processus.
- Si vous avez un ID de requête de quelque sorte, enregistrez-le également.
- Si vous pensez que vous allez passer un certain temps raisonnable à consulter des fichiers journaux, vous devriez sérieusement envisager de fournir tous les fichiers pdb etc... nécessaires pour voir les fichiers source et les numéros de ligne.
La journalisation n'est utilisée que lorsqu'il y a un problème
Ne confondez pas la journalisation avec la gestion des erreurs. La gestion des erreurs est l'acte de répondre et de résoudre cette erreur (par exemple, afficher un message à l'utilisateur), les journaux ne sont (généralement) utilisés que lorsqu'il y a un problème et que la raison n'est pas claire.
Par exemple: Si un utilisateur tente d'ouvrir un fichier qui n'existe pas, alors si l'erreur est correctement gérée (en informant l'utilisateur que le fichier est introuvable), il ne devrait y avoir aucun besoin de journaliser cette erreur.
(L'exception possible pourrait être si vous vouliez des statistiques sur la fréquence à laquelle cette erreur s'est produite ou quelque chose - cela revient à réfléchir à comment la journalisation sera utilisée.)
En général, bien gérer une erreur est préférable à la journalisation, cependant, la bonne gestion des erreurs est encore plus difficile que la bonne journalisation - ce sont les cas où les informations supplémentaires offertes dans les journaux sont nécessaires.
Vous ne devriez pas non plus confondre la journalisation avec l'audit (bien que dans de nombreux systèmes les deux se chevauchent).
Plus c'est mieux!
La seule façon de trop journaliser est si:
- Vous manquez d'espace de stockage.
- Vous affectez considérablement les performances de votre application.
- Vous êtes submergé par des journaux que vous ne pouvez pas facilement traiter en cas d'erreur.
Vous enregistrez des injures dirigées contre votre patron.
La journalisation existe uniquement pour diagnostiquer la cause des problèmes (ne confondez pas la journalisation avec l'audit) - si vous n'avez pas de problèmes, personne ne regardera vos journaux, et aucun mal n'est fait! Jusqu'à ce que vous ayez un problème, dans ce cas, vous avez besoin de le plus d'informations possible.
Si vous avez des doutes sur le fait de journaliser quelque chose ou non, alors enregistrez-le.
Ne journalisez les exceptions qu'une seule fois.
Après avoir dit tout cela, je ressens le besoin de clarifier l'enregistrement des exceptions.
En général, vous ne devriez enregistrer qu'une seule fois une exception (au moment où elle est gérée). Ne soyez pas tenté de journaliser une exception que vous lèverez ensuite au gestionnaire "au cas où" le gestionnaire ne journaliserait pas correctement l'exception - tout ce qui se passe est que vous finissez par enregistrer plusieurs fois la même exception alors qu'elle est transmise de niveau en niveau (je l'ai déjà vu se produire et cela rend difficile de voir combien d'erreurs se sont réellement produites).
Journalisez ce qui est pertinent, où il est pertinent de le journaliser.
Par exemple, si vous écrivez une application serveur, alors vos fichiers journaux doivent être sur le serveur, où les administrateurs système peuvent les lire - si cependant il y a un risque d'erreurs sur le client (par exemple, en JavaScript), alors votre code de journalisation doit être en JavaScript. La solution? Votre journal JavaScript doit se soumettre au serveur (comme log4js)
Ne vous inquiétez pas de l'endroit où vous devriez et ne devriez pas mettre la journalisation - placez-la simplement là où cela est nécessaire.
En général, il est préférable de consigner les choses à l'endroit où vous avez toutes les informations nécessaires. Et pour simplifier votre application, il est préférable de ne pas transmettre des données juste pour les consigner ailleurs. (Les exceptions semblent être une exception - désolé pour le jeu de mots :-) - mais leur objectif principal n'est pas la consignation, c'est juste un effet secondaire possible.)
Il n'est pas nécessaire de limiter la consignation à des modules/couches spécifiques de l'architecture (sauf dans certains cas spéciaux, par exemple les pilotes de périphériques qui ne sont pas censés consigner quoi que ce soit, ou les bibliothèques d'applications qui ne doivent pas faire d'hypothèses sur l'environnement).
Notez que différents messages de consignation peuvent avoir différents objectifs, même au sein de la même application :
- Pour donner des informations sur le flux (normal) des événements au sein de l'application (par exemple, une application de traitement par lots peut consigner qu'elle a démarré et terminé, éventuellement le bref résultat du traitement, etc.). Ces messages devraient de préférence être relativement peu nombreux contenant une quantité limitée d'informations, car ils sont souvent destinés à être lus par des humains (par exemple, un administrateur système pour vérifier le matin que l'application s'est exécutée avec succès). Comme il s'agit de messages "normaux", leur priorité est au milieu de l'échelle, par exemple INFO.
- Pour alerter sur des événements exceptionnels (par exemple, des erreurs, des plantages, des fichiers manquants, ...). Ces messages devraient être rares (dans une application normale), mais peuvent contenir une quantité excessive d'informations (comme des traces de pile, des vidages de noyau, etc.) qui peuvent aider à identifier le bug sous-jacent. Cependant, quand ils se produisent, nous voulons presque toujours les voir, donc ils ont une priorité élevée, tels que ERROR ou FATAL.
- Pour donner des informations détaillées sur ce qui se passe dans une partie spécifique de l'application (généralement à des fins de débogage). Ces messages sont généralement très volumineux, générant souvent des gigaoctets de données de consignation. Par conséquent, nous voulons les voir uniquement dans des circonstances spécifiques, il est donc judicieux de définir leur niveau à faible, par exemple DEBUG.
Les frameworks de consignation modernes comme la famille Log4J permettent une manipulation très flexible des différents types (niveaux) de messages provenant de différentes parties de l'application. Néanmoins, il est bon de planifier votre schéma de consignation avant d'ajouter une pléthore de messages de consignation au code. Par exemple, quels types de messages prévoyez-vous de consigner dans quelles parties de l'application, et comment prévoyez-vous d'utiliser ces messages ? De combien et de quels types de cibles de consignation (console, fichier, base de données) avez-vous besoin ?
Je trouve que le guide d'architecture Microsoft est une bonne lecture. http://msdn.microsoft.com/en-us/library/ff650706.aspx
La journalisation est une préoccupation transversale de toutes les couches
Pour ce que j'ai implémenté jusqu'à présent, j'ai utilisé la stratégie de la "boîte noire". Chaque morceau de code (fonction ou classe) a sa propre responsabilité à bien des égards : tester les entrées, faire ce qu'il est censé faire, gérer les erreurs, enregistrer des infos. Par conséquent, si votre morceau de code est situé dans une couche métier, il devrait générer des erreurs métier, ainsi que enregistrer des informations métier
Gestion des erreurs courantes à mon avis est très importante dans une application.
De JavaWorld, voici les raisons pour lesquelles la gestion des erreurs courantes est essentielle :
-
Journaux gonflés: Chaque bloc catch contient une instruction de journalisation, conduisant à des entrées de journal gonflées et redondantes causées par du code source pollué.
-
Implémentations redondantes: Le même type d'erreur a différentes représentations, ce qui complique sa gestion.
-
Casse de l'encapsulation: Les exceptions provenant d'autres composants sont déclarées comme faisant partie de la signature de la méthode, rompant la claire division entre l'interface et l'implémentation.
-
Déclaration d'exception non engageante: La signature de la méthode est généralisée pour renvoyer java.lang.Exception. De cette façon, les clients sont assurés de ne pas avoir la moindre idée des spécificités d'erreurs de la méthode.