Est-il plus judicieux de consigner les exceptions dans un fourre-tout ou dans une classe d'exception de base?

15

Je suis en train de refactoriser une application Web assez grande. L'un des problèmes majeurs est la gestion incohérente des erreurs et j'essaie de trouver une stratégie sensée. J'ai créé un gestionnaire d'erreurs personnalisé, via set_error_handler, qui transforme essentiellement les erreurs PHP dans ErrorExceptions et une classe d'exception de base personnalisée, qui hérite directement d' Exception .

En production, j'utilise un fourre-tout d'exception générique, via set_exception_handler , et je suis sur le point d'ajouter la journalisation des exceptions * au mix. Mon dilemme est de savoir où faire la journalisation réelle, dans la classe d'exception de base ou dans le fourre-tout.

J'ai pensé à quelques raisons pour le connecter dans le fourre-tout:

  • Il existe un certain nombre d'exceptions dans le code qui doivent être converties en un enfant approprié de la classe d'exception de base. Jusqu'à ce que cela se produise, toutes les exceptions ne seront pas enregistrées.
  • Il semble en quelque sorte plus naturel de le faire dans le fourre-tout, une classe d'exception de base ne devrait pas faire plus que d'être juste cela. (Il pourrait s'agir d'un principe de responsabilité unique, mais cela pourrait simplement être un sentiment erroné)

et une raison de se connecter à la classe d'exception de base:

  • Actuellement, le fourre-tout n'est utilisé que pour la production. Il serait facile de l'introduire sur nos autres environnements (développement, test) mais cela nécessiterait quelques ajustements, car les erreurs sont gérées différemment par environnement, car en production elles sont traduites en pages d'erreur 404/503.

Existe-t-il une pratique acceptable pour savoir où consigner les exceptions?

* La journalisation impliquera d'abord l'écriture dans un fichier texte, et elle peut évoluer vers l'envoi de mails pour certains types d'exceptions.


Quelques clarifications, suscitées par la réponse de @ unholysampler :

Je suis confronté à une base de code sloc 2 * 10 ^ 6, avec beaucoup de choses tierces sur lesquelles je n'ai aucun contrôle, et une partie du code sur lequel je contrôle les exceptions antérieures à PHP. Et il y a aussi un code récent merdique, nous nous remettons d'une longue période de pression intense où nous avons pratiquement dû arrêter de penser et juste pirater.

Nous refactorisons activement pour corriger toutes les incohérences et introduire une approche raisonnable de gestion des erreurs, mais cela va prendre un certain temps. Je suis plus intéressé par ce qu'il faut faire jusqu'à ce que j'arrive au point où les erreurs sont gérées de manière appropriée. Je vais probablement poser une autre question sur une stratégie d'exception raisonnable à un moment donné.

La principale motivation derrière la journalisation est de recevoir un e-mail sur mon téléphone chaque fois que quelque chose de mal se produit en production. Je me fiche que les décharges de données deviennent énormes, si elles le font, j'aurai un travail cron en supprimant les anciennes de temps en temps.

yannis
la source

Réponses:

11

En bref, la seule fois où vous devez consigner l'existence d'une exception est lorsque vous la gérez.

Lorsque vous lancez une exception, c'est parce que votre code a atteint un état où il ne peut pas fonctionner correctement. En lançant une exception, vous représentez un message spécifique à votre programme concernant l'erreur qui s'est produite. Vous ne devez pas intercepter une exception tant que vous n’êtes pas à un point où elle peut être correctement gérée.

Le code que vous écrivez dans le cadre de votre application principale doit être conscient des types d'exceptions qui peuvent être levées et quand elles peuvent être levées. Si vous ne pouvez rien faire de productif avec une exception, ne l'attrapez pas. N'enregistrez pas une exception tant qu'elle n'est pas gérée. Seul le code de gestion sait ce que signifie l'exception dans le contexte du flux de programme et comment y répondre. L'écriture d'un message de journal ici peut avoir un sens ici. Si vous utilisez une infrastructure de journalisation, vous pouvez définir un niveau de journalisation pour le message et éventuellement les filtrer. Cela fonctionne bien pour les exceptions qui peuvent survenir, mais ne sont pas critiques et peuvent être récupérées proprement.

Votre exception fourre-tout, est votre dernier effort pour empêcher votre code de s'écraser une mort laide. Si vous êtes arrivé jusqu'ici, vous enregistrez toutes les informations d'état et d'erreur que vous pouvez. Ensuite, vous faites de votre mieux pour dire à l'utilisateur que le programme plante avant que tout ne s'arrête. Votre objectif devrait être de ne jamais exécuter ce code.

L'incorporation de la journalisation dans la classe de base ne suit pas les directives ci-dessus. La classe de base ne sait rien de l'état du code. (Avoir la trace de la pile ne compte pas parce que vous n'écrirez pas de code qui prend des décisions basées sur son analyse.) La classe de base ne peut rien faire pour indiquer la gravité ou la façon dont l'exception peut être gérée. Vous ne voulez pas de vidages de données énormes et de traces de pile à chaque fois qu'il existe une exception simple que vous pouvez gérer et récupérer proprement.

unholysampler
la source
J'ai ajouté quelques clarifications sur la question suscitée par votre réponse. D'après ce que je comprends, du côté pratique de la question que vous proposez de vous connecter au fourre-tout?
yannis
1
@YannisRizos: Oui, vous devez implémenter le fourre-tout comme première étape. Ce que j'ai dit à propos du fourre-tout était davantage de s'assurer que vous ne l'utilisiez pas comme une partie normale de votre flux de code. L'implémentation d'un gestionnaire d'exceptions non géré est importante car elle vous permet d'obtenir beaucoup d'informations à chaque fois que votre code fait quelque chose de mal.
unholysampler
N'existe-t-il pas de cadres de journalisation qui pourraient facilement gérer le concept de "Voici un tas de choses qui devraient être enregistrées à moins qu'elles ne soient remplacées"? Chaque couche qui voit une exception peut remplacer les données de la précédente, sauf que si une nouvelle exception est levée au cours du déroulement de la pile, le dernier rapport de journal ne sera pas remplacé et sera donc enregistré. Aucun cadre ne prend-il en charge un tel modèle?
supercat
3

Si votre langue / runtime ne vous permet pas facilement de déterminer la source de l'exception, il y a lieu de la consigner au lancement. C ++ et certains moteurs JS n'exposent pas le fichier + la ligne ou la pile d'appels de l'exception au moment où vous l'avez interceptée / mais ces informations sont disponibles au moment où vous construisez l'exception.

Notre solution consistait à fournir un mécanisme qui utilisait la configuration d'exécution pour permettre la journalisation du type de l'exception avec une pile bon marché lors de la tentative de diagnostic de ces problèmes.

JBRWilkinson
la source
+1 C'est certainement un bon cas pour se connecter au lancer ... PHP fournit cependant une trace complète de la pile au moment de la capture, donc je vais probablement aller dans l'autre sens ...
yannis