2011-06-07 49 views
5

Cuando se utiliza RMI en Java se antepondrá el seguimiento de la pila a distancia de una excepción cuando lo reciba, algo como esto:forja de un seguimiento de la pila en Java

ERROR Client received error when doing stuff: 
myapp.FooBarException: bla 
at server.myMethod() 
at rmi.callHandler() // and now, on the next line comes the client 
at rmi.sendCall(); 
at client.doServerMethod() 
at Thread.run() 

¿Cómo es ese tipo de StackTrace "falsificación" ¿hecho?


¿Qué es lo que quiero (aparte de solo ser iterado)? Bueno, me ayudaría si pudiera hacer esto:

outer() { 

    thread = new Thread(... 
     inner(); 
     // inner() throws 
     // RuntimeException 
     // at inner(); 
     // at Runnable.run(); 
     // at Thread.run(); 
     // at outer(); 
     // at lalalala(); 
     // ... 

).start(); 

    thread.join(); 

} 

Y hacerlo de modo que una excepción lanzada en inner() tendría outer() (y los métodos más abajo en la cadena) en el StackTrace así, para fines de registro.

Respuesta

9

es una especie de fácil:

Throwable tiene métodos getStackTrace() y setStackTrace().

De one of my projects (de código abierto no, pero tal vez voy a abrir algún día el motor remoto de llamadas):

/** 
    * Setzt den Stack-Trace zusammen. Das untere Ende (tiefer in der 
    * Aufrufhierarchie, am Anfang des Arrays/der Ausgabe) ist das, 
    * welches im Throwable schon drin ist, das obere Ende wird aus 
    * dem aktuellen Stack genommen. Dazwischen 
    * kommt ein "Remote-Aufruf-Markierer". 
    */ 

traducido para su conveniencia:

fusiona el seguimiento de la pila. El extremo inferior (más profundo en la jerarquía de llamadas, en el extremo de la matriz/la salida) es lo que ya está en la pila, el extremo superior se tomará de la pila actual. Entre ellos colocaremos un marcador de llamada remota .

private void mergeStackTraces(Throwable error) 
    { 
     StackTraceElement[] currentStack = 
      new Throwable().getStackTrace(); 
     int currentStackLimit = 5; // TODO: raussuchen 
     StackTraceElement[] oldStack = 
      error.getStackTrace(); 
     StackTraceElement[] zusammen = 
      new StackTraceElement[currentStack.length - currentStackLimit + 
            oldStack.length + 1]; 
     System.arraycopy(oldStack, 0, zusammen, 0, oldStack.length); 
     zusammen[oldStack.length] = 
      new StackTraceElement("══════════════════════════", 
            "<remote call %" +callID+ ">", 
            "", -3); 
     System.arraycopy(currentStack, currentStackLimit, 
         zusammen, oldStack.length+1, 
         currentStack.length - currentStackLimit); 
     error.setStackTrace(zusammen); 
    } 

(En el lado del servidor, ya estoy cortando las partes de la traza de la pila que no se refieren al método de llamar a sí mismo, es decir, todo lo relacionado con el manejo de mensajes.)

esto da lugar a un seguimiento de pila combinado de esta manera:

java.lang.SecurityException: Das Passwort für Nutzer »Paul« ist falsch. 
     at de.fencing_game.db.userdb.Db4oUserDB.login(Db4oUserDB.java:304) 
     at de.fencing_game.server.impl.StandardServers$SSServer$1.run(StandardServers.java:316) 
     at de.fencing_game.server.impl.StandardServers$SSServer$1.run(StandardServers.java:314) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at de.fencing_game.server.impl.StandardServers$SSServer.login(StandardServers.java:313) 
     at de.fencing_game.transport.server.ServerTransport$ConnectionInfo$4.login(ServerTransport.java:460) 
     at ══════════════════════════.<remote call %2>() 
     at $Proxy1.login(Unknown Source) 
     at de.fencing_game.gui.basics.LoginUtils.login(LoginUtils.java:80) 
     at de.fencing_game.gui.Lobby.connectTo(Lobby.java:302) 
     at de.fencing_game.gui.Lobby$20.run(Lobby.java:849) 
     at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:226) 
     at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:647) 
     at java.awt.EventQueue.access$000(EventQueue.java:96) 
     at java.awt.EventQueue$1.run(EventQueue.java:608) 
     at java.awt.EventQueue$1.run(EventQueue.java:606) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105) 
     at java.awt.EventQueue.dispatchEvent(EventQueue.java:617) 
     at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275) 
     at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200) 
     at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) 
     at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185) 
     at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177) 
     at java.awt.EventDispatchThread.run(EventDispatchThread.java:138) 

supongo que el sistema RMI hace algo bastante similar (pero sin la ══════════════════════════).


Editar: Para su caso de uso, que tendría que guardar el seguimiento de la pila de la rosca exterior cuando se inicia la rosca interior, a continuación, en el método run detectar la excepción y anexar el seguimiento de pila externa a el rastro de la pila de la excepción interna. Sin embargo, realmente recomendaría poner algún tipo de separador.

+0

me gustaría la excepción interna para tener la StackTrace del exterior anexa, incluso si es capturado por algo profundo en la rosca interior. Supongo que eso no es posible, ya que no puedo aplicar una excepción personalizada. –

+0

Su código de captura tiene que hacer esto (o la excepción debe hacerlo antes de lanzar, anular fillInStackTrace o hacer esto en el constructor). Nada de esto funcionará para excepciones desconocidas lanzadas y atrapadas por código desconocido. –

+0

Me gusta que tenga 3 idiomas en un fragmento de código: inglés, alemán y Java :) – bacar

2

Crea una excepción personalizada que extraiga el stacktrace de una excepción y lo agregue a otro a través de setStackTrace().

Es útil para hacer cosas como esta o para mantener un stacktrace cuando no desea mantener una referencia difícil a la causada por una excepción. Esto es útil cuando se pasa información de excepción del servidor al cliente donde las clases de excepción de causa raíz pueden no estar presentes, lo que causa problemas de serialización.

0

Me gustaría sugerir una solución alternativa, que no es lo que pidió el OP, pero puede ser mejor para algunas personas que tienen una pregunta muy similar. Como yo.

Sugiero crear un throwable en el exterior y agregar el throwable de inner como la causa del throwable desde el exterior. Esto también capturará el punto en el que se realizó un cambio de hilo ... que puede ser útil para evitar confusiones sobre el trazado de la pila. Además, la información del hilo se puede almacenar en este throwable creado en el exterior que puede ayudar aún más.

Aquí hay algunos códigos.

public class StackCaptor { 
    public static Runnable capture(Runnable runnable) { 
     // Capture the stack 
     final Throwable prison = new Throwable(); 
     // Wrap run method to create a new throwable representing the creator of the original Runnable. 
     return new Runnable() { 
      @Override 
      public void run() { 
       try { 
        runnable.run(); 
       } catch (Throwable originalThrowable) { 
        RuntimeException callingThreadsException = new RuntimeException(originalThrowable); 
        callingThreadsException.setStackTrace(prison.getStackTrace()); 
        throw callingThreadsException; 
       } 
      } 
     }; 
    } 
} 

continuación, utilizar el código como el siguiente:

// This code has not be compiled or tested... You may need to use your 
// smarts to get it working, but it should give you an idea. 
public void outer() { 
    Thread thread = new Thread(StackCaptor.capture(new Runnable() { 
     public void run() { throw new RuntimeException("my ex"); } 
    })); 
    thread.start(); 
} 
Cuestiones relacionadas