Je suis actuellement en train de refactoriser un grand sous-système avec une architecture multicouche, 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:
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 (c'est-à-dire 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 intercepter toutes les exceptions à la source sans intercepter 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 renvoyées par la couche ci-dessous .
Une autre stratégie possible est un mélange de # 1 et # 2; où j'attrape des exceptions spécifiques au niveau de la couche qu'elles sont le plus susceptibles d'être levées (capture, journalisation et re-lancement par exemple SqlExceptions
dans la couche d'accès aux données), puis 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.
A vos questions:
La solution la plus simple serait d'implémenter la journalisation au niveau supérieur
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 sur les exceptions vous aide à garder votre code propre et concentré sur la tâche principale plutôt que sur les échecs.
Consigner l'exception à sa source est évidemment la meilleure solution car j'ai le plus d'informations.
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 niveau bas, 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.
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 ex. en Java, cela signifie surtout de ne pas essayer/attraper ou se connecter du tout en dehors du niveau supérieur.
Parfois, vous souhaitez 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.
J'ajoute quelques phrases sur la journalisation en général, pas seulement 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.