139 votes

Pourquoi exception.printStackTrace() est-il considéré comme une mauvaise pratique ?

Il y a beaucoup de matériel out ce qui suggère que l'impression de la trace de la pile d'une exception est une mauvaise pratique.

Par exemple, à partir de la vérification de RegexpSingleline dans Checkstyle :

Cette vérification peut être utilisée [...] pour trouver les mauvaises pratiques courantes telles que l'appel à ex.printStacktrace().

Cependant, j'ai du mal à trouver un endroit qui donne une raison valable, car la trace de la pile est certainement très utile pour trouver ce qui a causé l'exception. Les choses dont je suis conscient :

  1. Une trace de pile ne devrait jamais être visible pour les utilisateurs finaux (pour des raisons d'expérience utilisateur et de sécurité).

  2. La génération d'une trace de pile est un processus relativement coûteux (bien que peu susceptible d'être un problème dans la plupart des circonstances "exceptionnelles").

  3. De nombreux frameworks de journalisation impriment la trace de la pile pour vous (le nôtre ne le fait pas et non, nous ne pouvons pas le changer facilement).

  4. Imprimer la trace de la pile ne constitue pas une gestion des erreurs. Elle doit être combinée avec d'autres informations de journalisation et de gestion des exceptions.

Quelles sont les autres raisons pour éviter d'imprimer une trace de pile dans votre code ?

16 votes

En tant que personne devant régulièrement résoudre des problèmes, je voudrais jamais omettre l'impression d'une trace de pile lorsque quelque chose s'est mal passé. Oui, ne le montrez pas à l'utilisateur, mais oui, enregistrez-le dans un journal d'erreurs.

4 votes

Avez-vous vraiment besoin d'autres raisons ? Je pense que vous avez assez bien répondu à votre propre question. Cependant, le stacktrace devrait être imprimé dans les exceptions exceptionnelles :)

1 votes

136voto

Vineet Reynolds Points 40529

Throwable.printStackTrace() écrit la trace de la pile dans System.err PrintStream. Le site System.err et le flux de sortie standard "error" sous-jacent du processus JVM peuvent être redirigés par l'option

  • en invoquant System.setErr() qui change la destination pointée par System.err .
  • ou en redirigeant le flux de sortie d'erreur du processus. Le flux de sortie d'erreur peut être redirigé vers un fichier/dispositif
    • dont le contenu peut être ignoré par le personnel,
    • le fichier/périphérique peut ne pas être capable de rotation du journal, ce qui implique qu'un redémarrage du processus est nécessaire pour fermer l'identifiant du fichier/périphérique ouvert, avant d'archiver le contenu existant du fichier/périphérique.
    • ou le fichier/dispositif rejette effectivement toutes les données qui lui ont été écrites, comme c'est le cas de /dev/null .

En déduisant de ce qui précède, en invoquant Throwable.printStackTrace() constitue un comportement valide (pas bon/grand) de gestion des exceptions, seulement

  • si vous n'avez pas System.err étant réaffectés pendant toute la durée de vie de l'application,
  • et si vous n'avez pas besoin de la rotation des journaux pendant l'exécution de l'application,
  • et si elle est acceptée/conçue, la pratique d'enregistrement de l'application consiste à écrire à System.err (et le flux de sortie d'erreur standard de la JVM).

Dans la plupart des cas, les conditions ci-dessus ne sont pas remplies. On peut ne pas être conscient de l'existence d'un autre code s'exécutant dans la JVM, et on ne peut pas prédire la taille du fichier journal ou la durée d'exécution du processus. Une pratique de journalisation bien conçue consisterait à écrire des fichiers journaux "analysables par la machine" (une fonctionnalité préférable mais facultative dans un journal) dans une destination connue, pour faciliter le support.

Enfin, il faut se rappeler que la sortie de l'application Throwable.printStackTrace() serait certainement intercalé avec d'autres contenus écrits pour System.err (et peut-être même System.out si les deux sont redirigés vers le même fichier/dispositif). Il s'agit d'un inconvénient (pour les applications à un seul thread) auquel il faut faire face, car les données relatives aux exceptions ne sont pas facilement analysables dans un tel cas. Pire encore, il est fort probable qu'une application multithread produise des journaux très confus tels que Throwable.printStackTrace() n'est pas à l'abri des fils .

Il n'y a pas de mécanisme de synchronisation pour synchroniser l'écriture de la trace de la pile à System.err lorsque plusieurs threads invoquent Throwable.printStackTrace() en même temps. Pour résoudre ce problème, il faut en fait que votre code se synchronise sur le moniteur associé à System.err (et aussi System.out si le fichier/dispositif de destination est le même), et c'est un prix assez lourd à payer pour l'intégrité du fichier journal. Pour prendre un exemple, le ConsoleHandler y StreamHandler sont responsables de l'ajout d'enregistrements de journal à la console, dans la fonction de journalisation fournie par java.util.logging l'opération réelle de publication des enregistrements de journal est synchronisée - chaque thread qui tente de publier un enregistrement de journal doit également acquérir le verrou sur le moniteur associé à l'enregistrement de journal. StreamHandler instance. Si vous souhaitez avoir la même garantie d'avoir des enregistrements de journal non imbriqués en utilisant System.out / System.err vous devez vous assurer de la même chose : les messages sont publiés dans ces flux de manière sérialisable.

Compte tenu de tout ce qui précède, et des scénarios très restreints dans lesquels Throwable.printStackTrace() est réellement utile, il s'avère souvent que l'invoquer est une mauvaise pratique.


En prolongeant l'argument de l'un des paragraphes précédents, il est également un mauvais choix d'utiliser Throwable.printStackTrace en conjonction avec un logger qui écrit dans la console. Cela s'explique en partie par le fait que le logger se synchronise sur un moniteur différent, tandis que votre application se synchronise (éventuellement, si vous ne voulez pas d'enregistrements entrelacés) sur un moniteur différent. L'argument est également valable lorsque vous utilisez deux loggers différents qui écrivent vers la même destination, dans votre application.

0 votes

Excellente réponse, merci. Cependant, si je suis d'accord pour dire que la plupart du temps, c'est du bruit ou ce n'est pas nécessaire, il y a des moments où c'est absolument critique.

1 votes

@Chris Oui, il y a des moments où vous ne pouvez pas ne pas utiliser System.out.println y Throwable.printStackTrace et bien sûr, le jugement du développeur est requis. J'étais un peu inquiet que la partie concernant la sécurité des fils d'exécution ait été oubliée. Si vous regardez la plupart des implémentations de logger, vous remarquerez qu'elles synchronisent la partie où les enregistrements de log sont écrits (même à la console), bien qu'elles n'acquièrent pas de moniteurs sur System.err o System.out .

0 votes

Excellente remarque. Je n'aimerais pas essayer de regarder deux (ou plus) traces de pile entrelacées ! Quelle joie ce serait...

34voto

Tomasz Nurkiewicz Points 140462

Vous touchez à de multiples sujets ici :

1) Une trace de pile ne devrait jamais être visible pour les utilisateurs finaux (pour des raisons d'expérience utilisateur et de sécurité).

Oui, il devrait être accessible pour diagnostiquer les problèmes des utilisateurs finaux, mais ces derniers ne devraient pas les voir pour deux raisons :

  • Ils sont très obscurs et illisibles, l'application sera très peu conviviale.
  • Montrer une trace de pile à l'utilisateur final pourrait introduire un risque potentiel de sécurité. Corrigez-moi si je me trompe, PHP imprime les paramètres des fonctions dans la trace de la pile - génial, mais très dangereux - si vous obtenez une exception en vous connectant à la base de données, que risquez-vous de voir dans la trace de la pile ?

2) La génération d'une trace de pile est un processus relativement coûteux (bien qu'il soit peu probable que ce soit un problème dans la plupart des circonstances "d'exception").

La génération d'une trace de pile se produit lorsque l'exception est créée/renvoyée (c'est pourquoi le lancement d'une exception a un prix), l'impression n'est pas si coûteuse. En fait, vous pouvez remplacer Throwable#fillInStackTrace() dans votre exception personnalisée, ce qui rend le lancement d'une exception presque aussi facile qu'une simple instruction GOTO.

3) De nombreux frameworks de journalisation impriment la trace de la pile pour vous (le nôtre ne le fait pas et non, nous ne pouvons pas le changer facilement).

Très bon point. La question principale ici est la suivante : si le framework enregistre l'exception pour vous, ne faites rien (mais assurez-vous qu'il le fasse !) Si vous voulez enregistrer l'exception vous-même, utilisez un framework d'enregistrement tel que Logback o Log4J de ne pas les mettre sur la console brute car il est très difficile de la contrôler.

Avec le cadre de journalisation, vous pouvez facilement rediriger les traces de la pile vers un fichier, la console ou même les envoyer à une adresse e-mail spécifiée. Avec des codes en dur printStackTrace() vous devez vivre avec le sysout .

4) L'impression de la trace de la pile ne constitue pas une gestion des erreurs. Elle doit être combinée avec d'autres informations de journalisation et de traitement des exceptions.

Encore une fois : le journal SQLException correctement (avec la trace complète de la pile, en utilisant le cadre de journalisation) et montrez-le bien : " Désolé, nous ne sommes pas en mesure de traiter votre demande. "message. Pensez-vous vraiment que l'utilisateur est intéressé par les raisons ? Avez-vous vu l'écran d'erreur de StackOverflow ? C'est très humoristique, mais ne révèle pas tout détails. Toutefois, il garantit à l'utilisateur que le problème sera examiné.

Mais il se vous appeler immédiatement et vous devez être en mesure de diagnostiquer le problème. Vous avez donc besoin des deux : un enregistrement correct des exceptions et des messages conviviaux.


Pour résumer : toujours enregistrer les exceptions (de préférence en utilisant cadre logique ), mais ne les exposent pas à l'utilisateur final. Réfléchissez bien aux messages d'erreur dans votre interface graphique et ne montrez les traces de pile qu'en mode développement.

0 votes

Votre réponse est celle que j'ai obtenue.

0 votes

"la plupart des circonstances exceptionnelles". Bien.

22voto

Suraj Chandran Points 12859

Première chose printStackTrace() n'est pas coûteux comme vous le dites, car la trace de la pile est remplie lorsque l'exception est créée elle-même.

L'idée est de faire passer tout ce qui va vers les journaux par un cadre de logger, de sorte que la journalisation puisse être contrôlée. Ainsi, au lieu d'utiliser printStackTrace, il suffit d'utiliser quelque chose comme Logger.log(msg, exception);

12voto

coobird Points 70356

Imprimer la trace de la pile de l'exception ne constitue pas en soi une mauvaise pratique, mais sólo L'impression de la trace de la pile lorsqu'une exception se produit est probablement le problème ici - souvent, l'impression de la trace de la pile n'est pas suffisante.

De plus, on a tendance à soupçonner que le traitement des exceptions n'est pas effectué correctement si tout ce qui est effectué dans une catch Le bloc est un e.printStackTrace . Un traitement incorrect peut signifier au mieux qu'un problème est ignoré, et au pire un programme qui continue à s'exécuter dans un état indéfini ou inattendu.

Exemple

Prenons l'exemple suivant :

try {
  initializeState();

} catch (TheSkyIsFallingEndOfTheWorldException e) {
  e.printStackTrace();
}

continueProcessingAssumingThatTheStateIsCorrect();

Ici, nous voulons effectuer un traitement d'initialisation avant de passer à un traitement qui nécessite que l'initialisation ait eu lieu.

Dans le code ci-dessus, l'exception aurait dû être capturée et gérée correctement pour empêcher le programme de passer à l'étape suivante. continueProcessingAssumingThatTheStateIsCorrect ce qui, on peut le supposer, causerait des problèmes.

Dans de nombreux cas, e.printStackTrace() est une indication qu'une exception a été ignorée et que le traitement peut se poursuivre comme si aucun problème ne s'était produit.

Pourquoi cela est-il devenu un problème ?

L'une des principales raisons pour lesquelles la mauvaise gestion des exceptions est devenue plus répandue est probablement due à la façon dont les IDE tels qu'Eclipse génèrent automatiquement du code qui exécute une opération d'exception. e.printStackTrace pour le traitement des exceptions :

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

(Ce qui précède est une try-catch généré automatiquement par Eclipse pour gérer un problème de type InterruptedException jeté par Thread.sleep .)

Pour la plupart des applications, le simple fait d'imprimer la trace de la pile sur l'erreur standard ne sera probablement pas suffisant. Une gestion incorrecte des exceptions peut, dans de nombreux cas, conduire une application à fonctionner dans un état inattendu et à adopter un comportement inattendu et non défini.

1 votes

Ceci. Bien souvent, il suffit ne pas attraper l'exception du tout, au moins au niveau de la méthode, est mieux que d'attraper, d'imprimer la trace de la pile et de continuer comme si aucun problème ne s'était produit.

5voto

Oh Chin Boon Points 7667

printStackTrace() s'imprime sur une console. Dans les paramètres de production, personne ne regarde jamais cela. Suraj a raison, il faut passer cette information à un logger.

0 votes

Un point valable, bien que nous surveillions attentivement la sortie de notre console de production.

0 votes

Pouvez-vous expliquer ce que vous entendez par "regarder attentivement" ? Comment et qui ? À quelle fréquence ?

0 votes

Par regarder attentivement, j'aurais dû dire quand cela est nécessaire. C'est un fichier d'enregistrement continu qui est l'un des nombreux ports d'appel si quelque chose ne va pas avec notre application.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X