Il semble qu'il y ait un certain accord que les messages d'exception doivent contenir des détails utiles .
Pourquoi de nombreuses exceptions courantes aux composants du système ne contiennent-elles pas de détails utiles?
Quelques exemples:
List
accès à l'index ArgumentOutOfRangeException
ne me dit pas la valeur d'index qui a été essayée et qui n'était pas valide, pas plus cela me dit la plage autorisée.Donc, il me semble que pour la plupart, les messages d'exception ne contiennent pas suffisamment de détails pour être utiles. Mes attentes sont-elles hors ligne? Suis-je en train de mal utiliser des exceptions que je remarque même cela? Ou peut-être que mon impression est fausse: la majorité des exceptions fournissent en fait des détails utiles?
Les exceptions ne contiennent pas de détails utiles car le concept d'exceptions n'a pas encore suffisamment mûri dans la discipline du génie logiciel, de nombreux programmeurs ne les comprennent donc pas complètement et ne les traitent donc pas correctement.
Oui, IndexOutOfRangeException
doit contenir l'index précis qui était hors limites, ainsi que la plage qui était valide au moment où il a été lancé, et il est méprisable de la part des créateurs du runtime .NET qui ce n'est pas le cas. Oui, Oracle table or view not found
l'exception doit contenir le nom de la table ou de la vue qui n'a pas été trouvée, et encore une fois, le fait qu'elle ne soit pas méprisable de la part de la personne responsable.
Dans une large mesure, la confusion provient de l'idée originale erronée selon laquelle les exceptions devraient contenir des messages lisibles par l'homme, ce qui à son tour découle d'un manque de compréhension de la nature des exceptions, il s'agit donc d'un cercle vicieux.
Étant donné que les gens pensent que l'exception doit contenir un message lisible par l'homme, ils pensent que toutes les informations portées par l'exception doivent également être formatées dans le message lisible par l'homme, puis ils s'ennuient à écrire tout le message lisible par l'homme - du code du bâtiment, ou ils ont peur que cela ne divulgue une quantité d'informations déconseillée à tous les regards indiscrets qui pourraient voir le message. (Les problèmes de sécurité mentionnés par d'autres réponses.)
Mais la vérité est qu'ils ne devraient pas s'en inquiéter car l'exception devrait pas contenir un message lisible par l'homme. Les exceptions sont des choses que seuls les programmeurs devraient voir et/ou gérer. S'il est nécessaire de présenter des informations sur les défaillances à un utilisateur, cela doit être fait à un niveau très élevé, de manière sophistiquée et dans la langue de l'utilisateur, qui, statistiquement parlant, ne sera probablement pas l'anglais.
Donc, pour nous programmeurs, le "message" de l'exception est le nom de classe de l'exception , et ce que les autres informations pertinentes pour l'exception doivent être copiées dans les variables membres (finales/en lecture seule) de l'objet exception. De préférence, chaque petit morceau imaginable de celui-ci. De cette façon, aucun message ne doit (ou ne devrait) être généré, et donc aucun regard indiscret ne peut le voir.
Pour répondre à la préoccupation exprimée par Thomas Owens dans un commentaire ci-dessous:
Oui, bien sûr, à un certain niveau, vous le ferez créez un message de journal concernant l'exception. Mais vous voyez déjà le problème avec ce que vous dites: d'une part, un message de journal d'exception sans trace de pile est inutile, mais d'autre part, vous ne voulez pas laisser l'utilisateur voir la trace de pile d'exception entière. Encore une fois, notre problème ici est que notre point de vue est biaisé par les pratiques traditionnelles. Les fichiers journaux sont traditionnellement en texte brut, ce qui était peut-être correct pendant que notre discipline était à ses balbutiements, mais peut-être plus: s'il existe un problème de sécurité, le fichier journal doit être binaire et/ou crypté.
Qu'il soit binaire ou en texte brut, le fichier journal doit être considéré comme un flux dans lequel l'application sérialise les informations de débogage. Un tel flux serait réservé aux programmeurs et la tâche de génération des informations de débogage pour une exception devrait être aussi simple que de sérialiser l'exception dans le flux du journal de débogage. De cette façon, en regardant le journal, vous pouvez voir le nom de la classe d'exception (qui, comme je l'ai déjà dit, est à toutes fins pratiques "le message"), chacune des variables de membre d'exception qui décrivent tout ce qui est pertinent- et-pratique-à-inclure-dans-un-journal, et la trace de pile entière. Notez comment la mise en forme d'un message d'exception lisible par l'homme est manifestement manquante de ce processus.
P.S.
Un peu plus de mes réflexions sur ce sujet peuvent être trouvées dans cette réponse: Comment écrire un bon message d'exception
P.P.S.
Il semble que beaucoup de gens étaient cochés par ma suggestion concernant les fichiers journaux binaires, j'ai donc modifié la réponse une fois de plus pour qu'il soit encore plus clair que ce que je suggère ici n'est pas pas que le fichier journal devrait être binaire, mais que le fichier journal peut-être être binaire, le cas échéant.
Pourquoi de nombreuses exceptions courantes aux composants du système ne contiennent-elles pas de détails utiles?
D'après mon expérience, il existe un certain nombre de raisons pour lesquelles les exceptions ne contiennent pas d'informations utiles. Je m'attends à ce que ces sortes de raisons s'appliquent également aux composants du système - mais je ne sais pas avec certitude.
Mes attentes sont-elles hors ligne? Est-ce que j'utilise mal les exceptions que je remarque même cela?
Un peu? Je veux dire, les exceptions devraient avoir des messages sympas, mais elles sont aussi exceptions. Vous devriez passer votre temps à concevoir du code pour éviter des conditions exceptionnelles, ou à écrire du code pour gérer des conditions exceptionnelles (en ignorant le message), sans les utiliser comme une sorte de mécanisme de rétroaction interactif lors du codage. Il est inévitable de les utiliser à des fins de débogage, mais dans la plupart des situations, cela devrait être réduit au minimum. Le fait que vous remarquiez ce problème me fait craindre que vous ne fassiez pas assez bien pour les prévenir.
Je n'ai pas un excès d'expérience en C #, ou en C++ en particulier, mais je peux vous le dire - les exceptions écrites par le développeur 9 fois sur 10 sont plus utiles que toute exception générique que vous trouverez jamais, point final.
Idéalement, oui, une exception générique vous indiquera exactement pourquoi l'erreur s'est produite et vous pourrez la corriger facilement - mais de manière réaliste, dans les grandes applications avec plusieurs classes qui peuvent lever une grande variété de types d'exceptions différents, ou le mêmes types d'exceptions, il est toujours plus utile d'écrire votre propre sortie pour le retour d'erreur que de compter sur le message par défaut.
C'est ainsi que cela devrait être, car comme beaucoup de gens l'ont souligné, certaines applications ne veulent pas que envoie un message d'erreur qu'elles ne veulent pas l'utilisateur pour voir, pour des raisons de sécurité ou pour éviter de les confondre.
Au lieu de cela, vous devez anticiper dans votre conception les types d'erreurs susceptibles d'être générés dans votre application (et il y aura toujours des erreurs) et écrire des messages de détection d'erreurs qui vous aideront à identifier le problème.
Cela ne vous aidera pas toujours - car vous ne pouvez pas toujours prévoir quel message d'erreur sera utile - mais c'est la première étape pour mieux comprendre votre propre application à long terme.
La question est précisément de savoir pourquoi tant d'exceptions levées par les "composants système" (aussi appelés classes de bibliothèque standard) ne contiennent pas de détails utiles.
Malheureusement, la plupart des développeurs n'écrivent pas les composants de base dans les bibliothèques standard, et les documents de conception détaillés ou autres raisons de conception ne sont pas nécessairement rendus publics. En d'autres termes, nous ne le saurons peut-être jamais avec certitude.
Cependant, il y a deux points clés à garder à l'esprit pour expliquer pourquoi des informations détaillées sur les exceptions peuvent ne pas être souhaitables ou importantes:
Une exception peut être utilisée en appelant du code de n'importe quelle manière: une bibliothèque standard ne peut pas imposer de contraintes sur la façon dont l'exception est utilisée. Plus précisément, il peut être affiché pour l'utilisateur. Considérez un index de tableau hors limites: cela peut donner des informations utiles à un attaquant. Le concepteur de langage n'a aucune idée de la façon dont une application utilisera l'exception levée ni même de quel type d'application il s'agit (par exemple, une application Web ou un ordinateur de bureau), donc la suppression d'informations peut être plus sûre du point de vue de la sécurité.
Les exceptions ne devraient pas être affichées pour l'utilisateur. Au lieu de cela, affichez un message d'erreur convivial et connectez l'exception à un emplacement auquel un attaquant n'a pas accès (le cas échéant). Une fois l'erreur identifiée, un développeur doit déboguer à travers le code, inspecter les trames de pile et les chemins logiques. À ce stade, le développeur a plus d'informations qu'une exception ne pourrait jamais espérer avoir.
Tout d'abord, permettez-moi d'éclater une bulle en disant que même si le message de diagnostic contient des informations qui vous amènent à la ligne de code exacte et à la commande secondaire en 4 secondes, il est probable que les utilisateurs ne l'écriront jamais ou ne le transmettront pas aux gens de support et on vous dira "Eh bien, cela a dit quelque chose à propos d'une violation ... Je ne sais pas si ça avait l'air compliqué!"
J'écris des logiciels et je soutiens les résultats d'autres logiciels depuis plus de 30 ans maintenant, et personnellement, la qualité actuelle d'un message d'exception n'a pratiquement rien à voir avec la sécurité, quelle que soit la façon dont les résultats finaux se sont avérés correspondre à leur modèle de l'univers, beaucoup plus à voir avec le fait que tant de gens dans notre industrie étaient à l'origine autodidactes, et ils n'ont tout simplement jamais inclus de cours en communication. Peut-être que si nous forçions tous les nouveaux codeurs dans une position de maintenance pendant quelques années où ils devaient gérer ce qui n'allait pas, ils comprendraient l'importance d'au moins une certaine forme de précision.
Récemment, dans une application en cours de reconstruction, il a été décidé que les codes de retour tomberaient dans l'un des trois groupes suivants:
(100 a été omis pour une raison quelconque)
Dans tout flux de travail particulier, notre pensée a été réutilisée ou le code générique utiliserait des retours génériques (> 199) pour les erreurs fatales, nous laissant 100 erreurs fatales possibles pour un flux de travail. Avec de légères données de différenciation dans le message, des erreurs telles que le fichier introuvable peuvent toutes utiliser le même code et se différencier avec les messages.
Lorsque le code est revenu des entrepreneurs, vous ne croiriez pas notre surprise lorsque pratiquement CHAQUE SIMPLE ERREUR FATALE était le code de retour 101.
Tout ce qui a été pris en compte, je pense que la réponse à votre question est que les messages sont si dénués de sens parce que lorsqu'ils ont été créés à l'origine, ils devaient être des espaces réservés auxquels personne n'est revenu. Finalement, les gens ont compris comment résoudre les problèmes non pas à cause des messages, mais les DISPITE.
Depuis ce temps, les gens autodidactes n'ont tout simplement jamais eu un bon exemple de ce qu'une exception devrait contenir. Ajoutez à cela que plus d'utilisateurs ne lisent pas les messages, encore moins essayer de le transmettre au support (j'ai vu des messages d'erreur qui ont été coupés et collés par les utilisateurs qui ont ensuite été caviardés avant d'être envoyés avec le commentaire ultérieur selon lequel il semblait être beaucoup d'informations et je ne pouvais pas vouloir tout, alors ils en ont retiré au hasard un tas.
Et avouons-le, avec beaucoup trop (pas tous mais beaucoup trop) de la prochaine génération de codeurs, si c'est plus de travail et n'ajoute pas de flash, ça n'en vaut pas la peine ...
Dernière note: si un message d'erreur comprend un code d'erreur/retour, il me semble que quelque part dans les modules exécutés, il devrait y avoir une ligne qui lit quelque chose comme "si condition retourne la valeur du code" et la condition devrait vous dire pourquoi le code retour eu lieu. Cela semble être une approche logique simple, mais pour la vie de moi, essayez simplement que Microsoft vous dise ce qui s'est passé lorsqu'une mise à niveau de Windows a échoué sur CODE 80241013 ou un autre identifiant très unique. Un peu triste, non?
Pour donner une réponse légèrement différente: Le code incriminé a probablement été fait pour spécifier:
Ajoutez la pression de livrer exactement les spécifications (de peur d'être rejeté lors de la révision/du test) en un minimum de temps et avec un minimum d'agitation, puis vous avez votre recette pour une exception de bibliothèque entièrement conforme et inutile.
Les exceptions ont un coût spécifique à la langue et à l'implémentation.
Par exemple, des exceptions C++ sont nécessaires pour détruire toutes les données vivantes entre le lancement de la trame d'appel et la capture de la trame d'appel, et cela coûte cher. Par conséquent, les programmeurs ne souhaitent pas utiliser beaucoup d'exceptions.
Dans Ocaml, le lancement d'exceptions est presque aussi rapide qu'un C setjmp
(son coût ne dépend pas du nombre de trames d'appel traversées), donc les développeurs peuvent l'utiliser beaucoup (même pour les non-exceptionnels, très courants , cas). En revanche, les exceptions C++ sont suffisamment lourdes, donc vous ne les utiliserez probablement pas beaucoup comme vous le feriez dans Ocaml.
Un exemple typique est une recherche ou une exploration récursive qui peut être "arrêtée" à l'intérieur d'une récursion assez profonde (par exemple trouver une feuille dans un arbre ou une fonction d'unification). Dans certaines langues, il est plus rapide (donc plus idiomatique) de propager cette condition à chaque appelant. Dans d'autres langues, lever une exception est plus rapide.
Ainsi, selon la langue (et les habitudes des développeurs qui l'utilisent), une exception pourrait contenir de nombreux détails utiles, ou au contraire être utilisée comme un saut rapide non local et ne transporter que les données très utiles.
Bien que je convienne que les exceptions devraient contenir autant d'informations que possible, ou du moins être moins génériques. Dans le cas de table non trouvée, le nom de la table serait Nice.
Mais vous en savez plus sur ce que vous essayez de faire à l'endroit du code où vous avez reçu l'exception. Bien que vous ne puissiez souvent pas vraiment faire grand-chose pour rectifier la situation lorsque quelque chose ne va pas dans une bibliothèque hors de votre contrôle, vous pouvez ajouter des informations beaucoup plus utiles sur la situation dans laquelle quelque chose s'est mal passé.
Dans le cas d'une table non trouvée, cela ne vous sera d'aucune utilité si on vous dit que la table qui ne peut pas être trouvée s'appelle STUDENTS, car vous n'avez pas une telle table, et cette chaîne n'est nulle part dans votre code.
Mais si vous interceptez l'exception et la relancez avec l'instruction SQL que vous tentiez d'exécuter, il vaudrait mieux, car il s'avère que vous avez essayé d'insérer un enregistrement avec le champ de nom étant Robert '); DROP TABLE STUDENTS; (Il y a toujours un xkcd!)
Donc, pour lutter contre les exceptions moins qu'informatives: essayez-rattrapez-jetez avec plus d'informations spécifiques à ce que vous essayez de faire.
Je devrais probablement ajouter, pour que ce soit plus une réponse au pourquoi de la question, qu'une raison probable pour laquelle les créateurs des bibliothèques ne se sont pas concentrés sur l'amélioration des messages d'exception, c'est qu'ils ne savent pas pourquoi quelque chose a été essayé qui a échoué, cette logique est dans le code appelant.