Je suis actuellement en train de refactoriser un grand sous-système avec une architecture multi-couches, et j'ai du mal à concevoir une stratégie efficace de journalisation et de gestion des erreurs.
Disons que mon architecture se compose des trois couches suivantes:
- Interface publique (IE un contrôleur MVC)
- Couche de domaine
- Couche d'accès aux données
Ma source de confusion est l'endroit où je dois implémenter la journalisation des erreurs \ la gestion:
La solution la plus simple serait d'implémenter la journalisation au niveau supérieur (IE l'interface publique \ contrôleur MVC). Cependant, cela ne semble pas correct, car cela signifie faire remonter l'exception à travers les différentes couches, puis la journaliser; plutôt que d'enregistrer l'exception à sa source.
Consigner l'exception à sa source est évidemment la meilleure solution car j'ai le plus d'informations. Mon problème avec ceci est que je ne peux pas attraper toutes les exceptions à la source sans attraper TOUTES les exceptions, et dans la couche domaine / interface publique, cela conduira à intercepter les exceptions qui ont déjà été interceptées, enregistrées et relancées par la couche ci-dessous .
Une autre stratégie possible est un mélange de # 1 et # 2; par lequel j'attrape des exceptions spécifiques au niveau de la couche qu'elles sont les plus susceptibles d'être levées (IE Catching, logging et re-throwing
SqlExceptions
dans la couche d'accès aux données), puis j'enregistre toute autre exception non capturée au niveau supérieur. Cependant, cela nécessiterait également que j'attrape et reconnecte chaque exception au niveau supérieur, car je ne peux pas distinguer les erreurs qui ont déjà été enregistrées \ traitées de celles qui ne l'ont pas été.
Maintenant, évidemment, c'est un problème dans la plupart des applications logicielles, donc il doit y avoir une solution standard à ce problème qui se traduit par des exceptions interceptées à la source et enregistrées une fois; mais je ne vois pas comment faire cela moi-même.
Remarque, le titre de cette question est très similaire à " Journalisation des exceptions dans une application à plusieurs niveaux" ", mais les réponses dans ce message manquent de détails et ne sont pas suffisantes pour répondre à ma question.
la source
The easiest solution would be to implement the logging at the top level
- fais ça. La journalisation des exceptions à leur source n'est pas une bonne idée et chaque application que j'ai rencontrée qui fait cela était un PITA à déboguer. Il devrait être de la responsabilité de l'appelant de gérer les exceptions.try{ ... } catch(Exception ex) { Log(ex); }
entraînerait la même exception enregistrée dans chaque couche. (Cela semble également être une mauvaise pratique d'attraper chaque exception à chaque couche de la base de code.)Réponses:
A vos questions:
Avoir la bulle d'exception jusqu'au niveau supérieur est une approche absolument correcte et plausible. Aucune des méthodes de couche supérieure n'essaie de poursuivre un processus après l'échec, qui ne peut généralement pas réussir. Et une exception bien équipée contient toutes les informations nécessaires à la journalisation. Et ne rien faire des exceptions vous aide à garder votre code propre et concentré sur la tâche principale au lieu des échecs.
C'est à moitié correct. Oui, les informations les plus utiles y sont disponibles. Mais je recommanderais de mettre tout cela dans l'objet d'exception (s'il n'est pas déjà là) au lieu de le journaliser immédiatement. Si vous vous connectez à un bas niveau, vous devez toujours lever une exception pour dire à vos appelants que vous n'avez pas terminé votre travail. Cela se retrouve dans plusieurs journaux du même événement.
Des exceptions
Ma principale directive est de capturer et de consigner les exceptions au niveau supérieur uniquement. Et toutes les couches ci-dessous doivent s'assurer que toutes les informations de défaillance nécessaires sont transportées au niveau supérieur. Dans une application à un seul processus, par exemple en Java, cela signifie principalement de ne pas essayer / intercepter ou se connecter du tout en dehors du niveau supérieur.
Parfois, vous voulez que certaines informations de contexte soient incluses dans le journal des exceptions qui ne sont pas disponibles dans l'exception d'origine, par exemple l'instruction SQL et les paramètres qui ont été exécutés lorsque l'exception a été levée. Ensuite, vous pouvez intercepter l'exception d'origine et en renvoyer une nouvelle, contenant celle d'origine plus le contexte.
Bien sûr, la vraie vie interfère parfois:
En Java, vous devez parfois intercepter une exception et l'encapsuler dans un type d'exception différent juste pour obéir à certaines signatures de méthode fixes. Mais si vous relancez une exception, assurez-vous que celle-ci contient toutes les informations nécessaires pour une journalisation ultérieure.
Si vous traversez une frontière inter-processus, vous ne pouvez souvent techniquement pas transférer l'objet d'exception complet, y compris la trace de pile. Et bien sûr, la connexion pourrait se perdre. Voici donc un point où un service doit consigner les exceptions, puis faire de son mieux pour transmettre autant d'informations que possible sur la ligne à son client. Le service doit s'assurer que le client reçoit un avis d'échec, soit en recevant une réponse d'échec, soit en exécutant un délai d'expiration en cas de connexion interrompue. Cela entraînera généralement le même échec à se connecter deux fois, une fois à l'intérieur du service (avec plus de détails) et une fois au niveau supérieur du client.
Enregistrement
J'ajoute quelques phrases sur la journalisation en général, pas seulement sur la journalisation des exceptions.
Outre les situations exceptionnelles, vous souhaitez que les activités importantes de votre application soient également enregistrées dans le journal. Utilisez donc un cadre de journalisation.
Faites attention aux niveaux de journalisation (la lecture de journaux où les informations de débogage et les erreurs graves ne sont pas signalées différemment est une douleur!). Les niveaux de journal typiques sont:
En production, définissez le niveau de journalisation sur INFO. Les résultats devraient être utiles à un administrateur système afin qu'il sache ce qui se passe. Attendez-vous à ce qu'il vous appelle pour obtenir de l'aide ou corriger des bogues pour chaque ERREUR dans le journal et la moitié des AVERTISSEMENTS.
Activez le niveau DEBUG uniquement pendant les sessions de débogage réelles.
Regroupez les entrées de journal dans les catégories appropriées (par exemple, par le nom de classe complet du code qui génère l'entrée), ce qui vous permet d'activer les journaux de débogage pour des parties spécifiques de votre programme.
la source
Je me prépare pour les downvotes, mais je vais sortir sur un membre et dire que je ne suis pas sûr d'être d'accord avec cela.
Faire bouillir les exceptions, et encore moins les enregistrer à nouveau, est un effort supplémentaire avec peu d'avantages. Attrapez l'exception à la source (oui, le plus simple), enregistrez-la, mais ne relancez pas l'exception, signalez simplement une "erreur" à l'appelant. "-1", chaîne vide, vide, une énumération, peu importe. L'appelant a seulement besoin de savoir que l'appel a échoué, presque jamais les détails horribles. Et ceux-ci seront dans votre journal, non? Dans les rares cas où l'appelant a besoin des détails, allez-y et faites des bulles, mais pas par défaut irréfléchi automatique.
la source
null
ou la chaîne vide? Est-ce -1 ou un nombre négatif? 2. Si l'appelant s'en fiche (c.-à-d. Ne vérifie pas), cela entraîne des erreurs de suivi sans rapport avec la cause d'origine, par exemple aNullPointerException
. Ou pire: le calcul continue avec des valeurs erronées. 3. Si l'appelant s'en soucie mais que le programmeur ne pense pas que cette méthode échoue, le compilateur ne le lui rappelle pas. Les exceptions n'ont pas ce problème, soit vous attrapez, soit vous relancez.