Qu’est-ce qu’une trace de stack et comment puis-je l’utiliser pour déboguer les erreurs de l’application?

Parfois, lorsque je lance mon application, cela me donne une erreur qui ressemble à ceci:

Exception in thread "main" java.lang.NullPointerException at com.example.myproject.Book.getTitle(Book.java:16) at com.example.myproject.Author.getBookTitles(Author.java:25) at com.example.myproject.Bootstrap.main(Bootstrap.java:14) 

Les gens ont appelé cela une “trace de stack”. Qu’est-ce qu’une trace de stack? Que peut-il me dire à propos de l’erreur qui se produit dans mon programme?


A propos de cette question – assez souvent je vois une question où un programmeur novice “obtient une erreur”, et il suffit de coller sa trace de stack et un bloc de code aléatoire sans comprendre ce qu’est la trace de la stack ou comment l’utiliser. Cette question est conçue comme une référence pour les programmeurs novices qui pourraient avoir besoin d’aide pour comprendre la valeur d’une trace de stack.

En termes simples, une trace de stack est une liste des appels de méthode que l’application était au milieu d’une exception.

Exemple simple

Avec l’exemple donné dans la question, nous pouvons déterminer exactement où l’exception a été lancée dans l’application. Regardons la trace de la stack:

 Exception in thread "main" java.lang.NullPointerException at com.example.myproject.Book.getTitle(Book.java:16) at com.example.myproject.Author.getBookTitles(Author.java:25) at com.example.myproject.Bootstrap.main(Bootstrap.java:14) 

C’est une trace de stack très simple. Si nous commençons au début de la liste de “à …”, nous pouvons dire où notre erreur s’est produite. Ce que nous recherchons, c’est l’appel à la méthode la plus élevée qui fait partie de notre application. Dans ce cas, c’est:

 at com.example.myproject.Book.getTitle(Book.java:16) 

Pour déboguer ceci, nous pouvons ouvrir Book.java et regarder la ligne 16 , qui est:

 15 public Ssortingng getTitle() { 16 System.out.println(title.toSsortingng()); 17 return title; 18 } 

Cela indiquerait que quelque chose (probablement le title ) est null dans le code ci-dessus.

Exemple avec une chaîne d’exceptions

Parfois, les applications attraperont une exception et la relanceront comme cause d’une autre exception. Cela ressemble généralement à:

 34 public void getBookIds(int id) { 35 try { 36 book.getId(id); // this method it throws a NullPointerException on line 22 37 } catch (NullPointerException e) { 38 throw new IllegalStateException("A book has a null property", e) 39 } 40 } 

Cela peut vous donner une trace de stack qui ressemble à ceci:

 Exception in thread "main" java.lang.IllegalStateException: A book has a null property at com.example.myproject.Author.getBookIds(Author.java:38) at com.example.myproject.Bootstrap.main(Bootstrap.java:14) Caused by: java.lang.NullPointerException at com.example.myproject.Book.getId(Book.java:22) at com.example.myproject.Author.getBookIds(Author.java:36) ... 1 more 

Ce qui est différent à propos de celui-ci est le “Causé par”. Parfois, les exceptions auront plusieurs sections “Causé par”. Pour ceux-ci, vous voulez généralement trouver la “cause première”, qui sera l’une des sections “Caused by” les plus faibles de la trace de la stack. Dans notre cas, c’est:

 Caused by: java.lang.NullPointerException < -- root cause at com.example.myproject.Book.getId(Book.java:22) <-- important line 

Encore une fois, avec cette exception, nous aimerions regarder la ligne 22 de Book.java pour voir ce qui pourrait provoquer l’exception NullPointerException ici.

Exemple plus intimidant avec le code de la bibliothèque

Les traces de stack sont généralement beaucoup plus complexes que les deux exemples ci-dessus. Voici un exemple (c'est long, mais montre plusieurs niveaux d'exceptions en chaîne):

 javax.servlet.ServletException: Something bad happened at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at org.mortbay.jetty.Server.handle(Server.java:326) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228) at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582) Caused by: com.example.myproject.MyProjectServletException at com.example.myproject.MyServlet.doPost(MyServlet.java:169) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30) ... 27 more Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity] at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822) at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71) at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268) at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321) at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204) at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705) at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693) at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689) at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344) at $Proxy19.save(Unknown Source) at com.example.myproject.MyEntityService.save(MyEntityService.java:59) < -- relevant call (see notes below) at com.example.myproject.MyServlet.doPost(MyServlet.java:164) ... 32 more Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...] at org.hsqldb.jdbc.Util.throwError(Unknown Source) at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source) at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105) at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57) ... 54 more 

Dans cet exemple, il y a beaucoup plus. Ce qui nous préoccupe le plus, c'est de rechercher des méthodes provenant de notre code , ce qui serait quelque chose dans le package com.example.myproject . À partir du deuxième exemple (ci-dessus), nous voudrions d'abord examiner la cause première, à savoir:

 Caused by: java.sql.SQLException 

Cependant, tous les appels de méthode sous ce code sont des codes de bibliothèque. Nous allons donc passer au "Causé par" au-dessus et chercher le premier appel de méthode provenant de notre code, à savoir:

 at com.example.myproject.MyEntityService.save(MyEntityService.java:59) 

Comme dans les exemples précédents, nous devrions regarder MyEntityService.java sur la ligne 59 , car c'est là que cette erreur est MyEntityService.java (celle-ci est un peu évidente, mais SQLException indique l'erreur, mais la procédure de débogage est après) .

Je publie cette réponse pour que la réponse la plus élevée (une fois sortingée par activité) ne soit pas tout à fait erronée.

Qu’est-ce qu’un Stacktrace?

Un stacktrace est un outil de débogage très utile. Il montre la stack d’appels (c’est-à-dire la stack de fonctions appelées jusqu’à ce point) au moment où une exception non interceptée a été lancée (ou l’heure à laquelle la trace de stack a été générée manuellement). Ceci est très utile car il ne vous montre pas seulement où l’erreur est survenue, mais aussi comment le programme s’est retrouvé à cet endroit du code. Cela mène à la question suivante:

Qu’est-ce qu’une exception?

Une exception est ce que l’environnement d’exécution utilise pour vous dire qu’une erreur s’est produite. Des exemples populaires sont NullPointerException, IndexOutOfBoundsException ou ArithmeticException. Chacun de ces problèmes survient lorsque vous essayez de faire quelque chose d’impossible. Par exemple, une exception NullPointerException sera lancée lorsque vous essayez de déréférencer un object Null:

 Object a = null; a.toSsortingng(); //this line throws a NullPointerException Object[] b = new Object[5]; System.out.println(b[10]); //this line throws an IndexOutOfBoundsException, //because b is only 5 elements long int ia = 5; int ib = 0; ia = ia/ib; //this line throws an ArithmeticException with the //message "/ by 0", because you are trying to //divide by 0, which is not possible. 

Comment dois-je traiter avec les Stacktraces / Exceptions?

Au début, découvrez ce qui cause l’exception. Essayez de rechercher le nom de l’exception pour savoir quelle est la cause de cette exception. La plupart du temps, le code sera incorrect. Dans les exemples donnés ci-dessus, toutes les exceptions sont provoquées par un code incorrect. Donc, pour l’exemple NullPointerException, vous pouvez vous assurer que a n’est jamais nul à ce moment-là. Vous pouvez, par exemple, initialiser a ou inclure un chèque comme celui-ci:

 if (a!=null) { a.toSsortingng(); } 

De cette façon, la ligne incriminée n’est pas exécutée si a==null . Même chose pour les autres exemples.

Parfois, vous ne pouvez pas vous assurer que vous n’obtenez pas une exception. Par exemple, si vous utilisez une connexion réseau dans votre programme, vous ne pouvez pas empêcher l’ordinateur de perdre sa connexion Internet (par exemple, vous ne pouvez pas empêcher l’utilisateur de déconnecter la connexion réseau de l’ordinateur). Dans ce cas, la bibliothèque réseau générera probablement une exception. Maintenant, vous devriez attraper l’exception et la gérer . Cela signifie que, dans l’exemple avec la connexion réseau, vous devez essayer de rouvrir la connexion ou de notifier l’utilisateur ou quelque chose du genre. De plus, chaque fois que vous utilisez catch, interceptez toujours uniquement l’exception que vous voulez intercepter, n’utilisez pas d’instructions catch larges comme catch (Exception e) qui intercepteraient toutes les exceptions. Ceci est très important, car sinon vous pourriez accidentellement prendre la mauvaise exception et réagir de manière incorrecte.

 try { Socket x = new Socket("1.1.1.1", 6789); x.getInputStream().read() } catch (IOException e) { System.err.println("Connection could not be established, please try again later!") } 

Pourquoi ne devrais-je pas utiliser catch (Exception e) ?

Utilisons un petit exemple pour montrer pourquoi vous ne devriez pas simplement saisir toutes les exceptions:

 int mult(Integer a,Integer b) { try { int result = a/b return result; } catch (Exception e) { System.err.println("Error: Division by zero!"); return 0; } } 

Ce que ce code essaie de faire est de récupérer l’ ArithmeticException provoquée par une division possible par 0. Mais il intercepte également une éventuelle NullPointerException qui est levée si a ou b sont null . Cela signifie que vous pourriez obtenir une NullPointerException mais vous la traiterez comme une exception ArithmeticException et vous ferez probablement la mauvaise chose. Dans le meilleur des cas, il vous manque encore une NullPointerException. Des trucs comme ça rendent le débogage beaucoup plus difficile, alors ne le faites pas.

TLDR

  1. Déterminez quelle est la cause de l’exception et corrigez-la, afin qu’elle ne lance pas d’exception du tout.
  2. Si 1. n’est pas possible, attrapez l’exception spécifique et gérez-la.

    • N’ajoutez jamais un try / catch et ignorez simplement l’exception! Ne fais pas ça!
    • Ne jamais utiliser catch (Exception e) , attraper toujours des exceptions spécifiques. Cela vous évitera beaucoup de maux de tête.

Pour append à ce que Rob a mentionné. La définition de points d’arrêt dans votre application permet le traitement pas à pas de la stack. Cela permet au développeur d’utiliser le débogueur pour voir à quel point la méthode fait quelque chose d’inattendu.

Étant donné que Rob a utilisé l’ NullPointerException (NPE) pour illustrer quelque chose de commun, nous pouvons vous aider à supprimer ce problème de la manière suivante:

si nous avons une méthode qui prend des parameters tels que: void (Ssortingng firstName)

Dans notre code, nous voudrions évaluer que firstName contient une valeur, nous le ferions comme ceci: if(firstName == null || firstName.equals("")) return;

Ce qui précède nous empêche d’utiliser firstName comme paramètre non sécurisé. Par conséquent, en effectuant des vérifications NULL avant le traitement, nous pouvons vous aider à vous assurer que notre code fonctionnera correctement. Pour développer un exemple qui utilise un object avec des méthodes, nous pouvons regarder ici:

if(dog == null || dog.firstName == null) return;

Ce qui précède est le bon ordre pour vérifier les valeurs NULL, nous commençons par l’object de base, chien dans ce cas, puis commençons à parcourir l’arborescence des possibilités pour nous assurer que tout est valide avant le traitement. Si l’ordre était inversé, un NPE pourrait potentiellement être lancé et notre programme tomberait en panne.

Il existe une autre fonctionnalité de stacktrace offerte par la famille Throwable: la possibilité de manipuler les informations de trace de la stack.

Comportement standard:

 package test.stack.trace; public class SomeClass { public void methodA() { methodB(); } public void methodB() { methodC(); } public void methodC() { throw new RuntimeException(); } public static void main(Ssortingng[] args) { new SomeClass().methodA(); } } 

Trace de la stack:

 Exception in thread "main" java.lang.RuntimeException at test.stack.trace.SomeClass.methodC(SomeClass.java:18) at test.stack.trace.SomeClass.methodB(SomeClass.java:13) at test.stack.trace.SomeClass.methodA(SomeClass.java:9) at test.stack.trace.SomeClass.main(SomeClass.java:27) 

Trace de stack manipulée:

 package test.stack.trace; public class SomeClass { ... public void methodC() { RuntimeException e = new RuntimeException(); e.setStackTrace(new StackTraceElement[]{ new StackTraceElement("OtherClass", "methodX", "Ssortingng.java", 99), new StackTraceElement("OtherClass", "methodY", "Ssortingng.java", 55) }); throw e; } public static void main(Ssortingng[] args) { new SomeClass().methodA(); } } 

Trace de la stack:

 Exception in thread "main" java.lang.RuntimeException at OtherClass.methodX(Ssortingng.java:99) at OtherClass.methodY(Ssortingng.java:55) 

Pour comprendre le nom : Une trace de stack est une liste d’exceptions (ou vous pouvez dire une liste de “Cause par”), de l’exception la plus superficielle (ex. Exception de couche de service) à la plus profonde (ex. Exception de firebase database). Tout comme la raison pour laquelle nous l’appelons «stack», car la stack est la première sortie (FILO), l’exception la plus profonde s’est produite au tout début, puis une chaîne d’exception a été générée, la dernière étant une exception de surface. l’un est arrivé à temps, mais nous le voyons en premier lieu.

Clé 1 : Une chose délicate et importante doit être comprise ici: la cause la plus profonde peut ne pas être la “cause principale”, car si vous écrivez un “mauvais code”, cela peut provoquer des exceptions plus profondes que sa couche. Par exemple, une requête SQL incorrecte peut provoquer la réinitialisation de la connexion SQLServerException dans le bottem au lieu de l’erreur syndax, qui peut se trouver juste au milieu de la stack.

-> Localisez la cause principale au milieu de votre travail. entrer la description de l'image ici

Key 2 : Une autre chose délicate mais importante est à l’intérieur de chaque bloc “Cause par”, la première ligne était la couche la plus profonde et arrivait en premier lieu pour ce bloc. Par exemple,

 Exception in thread "main" java.lang.NullPointerException at com.example.myproject.Book.getTitle(Book.java:16) at com.example.myproject.Author.getBookTitles(Author.java:25) at com.example.myproject.Bootstrap.main(Bootstrap.java:14) 

Book.java:16 a été appelé par Auther.java:25 qui était appelé par Bootstrap.java:14, Book.java:16 était la cause principale. Joignez ici un diagramme pour sortinger la stack de traces dans l’ordre chronologique. entrer la description de l'image ici

Pour append aux autres exemples, il existe des classes internes (nestedes) qui apparaissent avec le signe $ . Par exemple:

 public class Test { private static void privateMethod() { throw new RuntimeException(); } public static void main(Ssortingng[] args) throws Exception { Runnable runnable = new Runnable() { @Override public void run() { privateMethod(); } }; runnable.run(); } } 

Entraînera cette trace de stack:

 Exception in thread "main" java.lang.RuntimeException at Test.privateMethod(Test.java:4) at Test.access$000(Test.java:1) at Test$1.run(Test.java:10) at Test.main(Test.java:13) 

Les autres articles décrivent ce qu’est une trace de stack, mais cela peut toujours être difficile à utiliser.

Si vous obtenez une trace de stack et souhaitez identifier la cause de l’exception, vous pouvez commencer par utiliser la console de trace Java Stack dans Eclipse . Si vous utilisez un autre IDE, il peut y avoir une fonctionnalité similaire, mais cette réponse concerne Eclipse.

Tout d’abord, assurez-vous que toutes vos sources Java sont accessibles dans un projet Eclipse.

Ensuite, dans la perspective Java , cliquez sur l’onglet Console (généralement en bas). Si la vue de la console n’est pas visible, accédez à l’option de menu Fenêtre -> Afficher la vue et sélectionnez Console .

Ensuite, dans la fenêtre de la console, cliquez sur le bouton suivant (à droite)

Bouton Consoles

puis sélectionnez Java Stack Trace Console dans la liste déroulante.

Collez votre trace de stack dans la console. Il fournira ensuite une liste de liens dans votre code source et tout autre code source disponible.

Voici ce que vous pourriez voir (image de la documentation Eclipse):

Diagramme à partir de la documentation Eclipse

L’appel de méthode le plus récent sera le haut de la stack, qui est la première ligne (à l’exclusion du texte du message). En descendant la stack remonte dans le temps. La deuxième ligne est la méthode qui appelle la première ligne, etc.

Si vous utilisez un logiciel open source, vous devrez peut-être télécharger et joindre à votre projet les sources si vous souhaitez les examiner. Téléchargez les fichiers JAR source dans votre projet, ouvrez le dossier Bibliothèques référencées pour trouver votre fichier JAR pour votre module open-source (celui contenant les fichiers de classe), puis cliquez avec le bouton droit, sélectionnez Propriétés et joignez le fichier jar source.