java - traduccion - ¿Qué es un seguimiento de pila y cómo puedo usarlo para depurar los errores de mi aplicación?




stacktrace java (5)

En términos simples, un seguimiento de pila es una lista de las llamadas de método que la aplicación estaba en medio de cuando se lanzó una excepción.

Ejemplo simple

Con el ejemplo dado en la pregunta, podemos determinar exactamente dónde se lanzó la excepción en la aplicación. Echemos un vistazo a la traza de la pila:

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)

Este es un seguimiento de pila muy simple. Si comenzamos al principio de la lista de "en ...", podemos decir dónde ocurrió nuestro error. Lo que buscamos es el método de llamada superior que forma parte de nuestra aplicación. En este caso, es:

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

Para depurar esto, podemos abrir Book.java y mirar la línea 16 , que es:

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

Esto indicaría que algo (probablemente el title ) es null en el código anterior.

Ejemplo con una cadena de excepciones.

A veces las aplicaciones detectan una Excepción y la vuelven a lanzar como la causa de otra Excepción. Esto típicamente se ve como:

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   }

Esto podría darte un rastro de pila que parece:

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

Lo que es diferente acerca de este es el "Causado por". Algunas veces las excepciones tendrán múltiples secciones "Causadas por". Para estos, normalmente desea buscar la "causa raíz", que será una de las secciones más bajas de "Causado por" en el seguimiento de la pila. En nuestro caso, es:

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

Nuevamente, con esta excepción, nos gustaría consultar la línea 22 de Book.java para ver qué puede causar la excepción NullPointerException aquí.

Ejemplo más desalentador con código de biblioteca.

Por lo general, las trazas de pila son mucho más complejas que los dos ejemplos anteriores. Aquí hay un ejemplo (es largo, pero muestra varios niveles de excepciones encadenadas):

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

En este ejemplo, hay mucho más. Lo que más nos preocupa es buscar métodos que com.example.myproject de nuestro código , lo cual sería algo en el paquete com.example.myproject . A partir del segundo ejemplo (arriba), primero queremos buscar la causa raíz, que es:

Caused by: java.sql.SQLException

Sin embargo, todas las llamadas de método bajo ese código de biblioteca. Por lo tanto, nos moveremos hasta "Causado por" encima de él, y buscaremos la primera llamada de método que se origina en nuestro código, que es:

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

Como en los ejemplos anteriores, deberíamos ver MyEntityService.java en la línea 59 , porque ahí es donde se originó este error (esto es un poco obvio, lo que salió mal, ya que la SQLException indica el error, pero el procedimiento de depuración es lo que buscamos) .

A veces, cuando ejecuto mi aplicación, me da un error que parece:

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)

La gente se ha referido a esto como una "traza de pila". ¿Qué es una traza de pila? ¿Qué puede decirme sobre el error que está ocurriendo en mi programa?

Acerca de esta pregunta: muy a menudo veo una pregunta en la que un programador novato está "recibiendo un error", y simplemente pegan su seguimiento de pila y un bloque de código aleatorio sin entender qué es el seguimiento de pila o cómo pueden usarlo. Esta pregunta pretende ser una referencia para los programadores novatos que pueden necesitar ayuda para entender el valor de un seguimiento de pila.


Estoy publicando esta respuesta por lo que la respuesta más alta (cuando está ordenada por actividad) no es simplemente errónea.

¿Qué es un Stacktrace?

Un stacktrace es una herramienta de depuración muy útil. Muestra la pila de llamadas (es decir, la pila de funciones que se llamaron hasta ese momento) en el momento en que se lanzó una excepción no detectada (o la hora en que se generó el seguimiento de la pila manualmente). Esto es muy útil porque no solo te muestra dónde ocurrió el error, sino también cómo terminó el programa en ese lugar del código. Esto lleva a la siguiente pregunta:

¿Qué es una excepción?

Una excepción es lo que el entorno de ejecución utiliza para informarle que se produjo un error. Ejemplos populares son NullPointerException, IndexOutOfBoundsException o ArithmeticException. Cada uno de estos se produce cuando intentas hacer algo que no es posible. Por ejemplo, se lanzará una NullPointerException cuando intente eliminar la referencia a un objeto Null:

Object a = null;
a.toString();                 //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.

¿Cómo debo tratar con Stacktraces / Exceptions?

Al principio, averigüe qué está causando la excepción. Intente buscar en Google el nombre de la excepción para averiguar cuál es la causa de esa excepción. La mayoría de las veces será causado por un código incorrecto. En los ejemplos anteriores, todas las excepciones están causadas por un código incorrecto. Por lo tanto, para el ejemplo NullPointerException, puede asegurarse de que nunca sea nulo en ese momento. Podría, por ejemplo, inicializar o incluir un cheque como este:

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

De esta manera, la línea ofensiva no se ejecuta si a==null . Lo mismo ocurre con los otros ejemplos.

A veces no puede asegurarse de no obtener una excepción. Por ejemplo, si está utilizando una conexión de red en su programa, no puede evitar que la computadora pierda la conexión a Internet (por ejemplo, no puede evitar que el usuario desconecte la conexión de red de la computadora). En este caso, la biblioteca de red probablemente lanzará una excepción. Ahora deberías atrapar la excepción y manejarla . Esto significa que, en el ejemplo con la conexión de red, debe intentar volver a abrir la conexión o notificar al usuario o algo así. Además, siempre que use la captura, siempre capture solo la excepción que desea atrapar, no use declaraciones de captura amplias como catch (Exception e) que atraparía todas las excepciones. Esto es muy importante, ya que de lo contrario podría detectar accidentalmente la excepción incorrecta y reaccionar de manera incorrecta.

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!")
}

¿Por qué no debería usar catch (Exception e) ?

Vamos a usar un pequeño ejemplo para mostrar por qué no debería capturar todas las excepciones:

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;
    }
}

Lo que este código intenta hacer es atrapar la ArithmeticException causada por una posible división entre 0. Pero también atrapa una posible NullPointerException que se produce si a o b son null . Esto significa que puede obtener una NullPointerException pero la tratará como una ArithmeticException y probablemente hará algo incorrecto. En el mejor de los casos, todavía extraña que hubo una NullPointerException. Cosas como esas hacen que la depuración sea mucho más difícil, así que no hagas eso.

TLDR

  1. Averigüe cuál es la causa de la excepción y corríjala, para que no la arroje en absoluto.
  2. Si 1. no es posible, capture la excepción específica y manipúlela.

    • ¡Nunca solo agregue un try / catch y luego ignore la excepción! ¡No hagas eso!
    • Nunca use catch (Exception e) , siempre capture Excepciones específicas. Eso te ahorrará muchos dolores de cabeza.

Las otras publicaciones describen qué es un seguimiento de pila, pero aún puede ser difícil trabajar con él.

Si obtiene un seguimiento de pila y desea rastrear la causa de la excepción, un buen punto de partida para entenderlo es utilizar la Consola de seguimiento de pila Java en Eclipse . Si usa otro IDE, puede haber una característica similar, pero esta respuesta es sobre Eclipse.

Primero, asegúrese de tener todas sus fuentes Java accesibles en un proyecto de Eclipse.

Luego, en la perspectiva de Java , haga clic en la pestaña Consola (generalmente en la parte inferior). Si la vista de la Consola no está visible, vaya a la opción del menú Ventana -> Mostrar vista y seleccione Consola .

Luego, en la ventana de la consola, haga clic en el siguiente botón (a la derecha)

y luego seleccione Java Stack Trace Console en la lista desplegable.

Pega el rastro de tu pila en la consola. Luego proporcionará una lista de enlaces a su código fuente y cualquier otro código fuente disponible.

Esto es lo que podría ver (imagen de la documentación de Eclipse):

El método más reciente llamado será la parte superior de la pila, que es la línea superior (excluyendo el texto del mensaje). Bajando la pila retrocede en el tiempo. La segunda línea es el método que llama a la primera línea, etc.

Si está utilizando software de código abierto, es posible que deba descargar y adjuntar a su proyecto las fuentes si desea examinar. Descargue los archivos jar de origen, en su proyecto, abra la carpeta Bibliotecas de referencia para encontrar su archivo jar para su módulo de código abierto (el que contiene los archivos de clase), luego haga clic derecho, seleccione Propiedades y adjunte el archivo fuente.


Para agregar a lo que Rob ha mencionado. Establecer puntos de ruptura en su aplicación permite el procesamiento paso a paso de la pila. Esto permite al desarrollador usar el depurador para ver en qué punto exacto está el método haciendo algo que no se anticipó.

Dado que Rob ha usado la NullPointerException (NPE) para ilustrar algo común, podemos ayudar a eliminar este problema de la siguiente manera:

Si tenemos un método que toma parámetros como: void (String firstName)

En nuestro código, nos gustaría evaluar si firstName contiene un valor, lo haríamos así: if(firstName == null || firstName.equals("")) return;

Lo anterior nos impide utilizar el primer nombre como un parámetro inseguro. Por lo tanto, al realizar verificaciones nulas antes del procesamiento, podemos ayudar a garantizar que nuestro código se ejecute correctamente. Para ampliar un ejemplo que utiliza un objeto con métodos, podemos verlo aquí:

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

Lo anterior es el orden correcto para verificar si hay nulos, comenzamos con el objeto base, dog en este caso, y luego comenzamos a caminar por el árbol de posibilidades para asegurarnos de que todo sea válido antes de procesarlo. Si se revirtiera la orden, se podría lanzar una NPE y nuestro programa se bloquearía.


Para entender el nombre : Un seguimiento de pila es una lista de Excepciones (o puede decir una lista de "Causa por"), desde la Excepción más superficial (por ejemplo, Excepción de la capa de servicio) a la más profunda (por ejemplo, Excepción de base de datos). Al igual que la razón por la que lo llamamos 'pila' es porque la pila es Primero en Último (FILO), la excepción más profunda se produjo al principio, luego se generó una serie de consecuencias, una excepción de superficie fue la última uno sucedió en el tiempo, pero lo vemos en primer lugar.

Clave 1 : Una cosa difícil e importante que debe entenderse aquí es que: la causa más profunda puede no ser la "causa raíz", porque si escribe algún "código incorrecto", puede causar alguna excepción por debajo, que es más profunda que su capa. Por ejemplo, una consulta de SQL incorrecta puede provocar el restablecimiento de la conexión de SQLServerException en la parte inferior en lugar de un error de Syndax, que puede estar justo en el medio de la pila.

-> Localizar la causa raíz en el medio es su trabajo.

Clave 2 : Otra cosa difícil pero importante está dentro de cada bloque "Causa por", la primera línea fue la capa más profunda y sucedió en primer lugar para este bloque. Por ejemplo,

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 fue llamado por Auther.java:25 que fue llamado por Bootstrap.java:14, Book.java:16 fue la causa raíz. Aquí adjuntar un diagrama ordenar la pila de seguimiento en orden cronológico.





stack-trace