2011-06-07 55 views
5

一个堆栈跟踪当您使用RMI在Java中异常的远程堆栈跟踪将预置当你收到它,有点像这样:锻造的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() 

这是怎么样的堆栈跟踪“伪造”的做了什么?


我想要什么(除了只是反复兴趣)?那么,它会帮助我,如果我能做到这一点:

outer() { 

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

).start(); 

    thread.join(); 

} 

而且让这个在inner()抛出的异常具有outer()(和方法降低下来链)的堆栈跟踪为好,可以进行日志记录。

回答

9

这是种简单:

的Throwable有方法getStackTrace()setStackTrace()

one of my projects(非开源,但也许我有一天打开远程调用引擎):

/** 
    * 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". 
    */ 

翻译为您提供方便:

融合了堆栈跟踪。低端(呼叫层次结构中的较深处,在阵列的末尾 处)是堆栈中已经存在的内容,高端 将取自当前堆栈。在他们之间,我们将把一个 远程呼叫标记

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

(在服务器端,我已经切断的堆栈跟踪不涉及到方法的调用零件本身,即一切相关的消息处理。)

这导致这样的组合堆栈跟踪:

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) 

我想RMI系统确实非常相似(只是没有══════════════════════════)的东西。


编辑: 为了您的用例,你必须保存在内部线程启动外线程的堆栈跟踪信息,然后在run方法捕获异常和外堆栈跟踪追加到内部异常的堆栈跟踪。不过,我真的会推荐使用某种类型的分隔符。

+0

我想内部异常有外附加的堆栈跟踪,即使它被什么东西深藏于内螺纹抓获。我想这是不可能的,因为我不能应用自定义异常。 – 2011-06-10 11:06:39

+0

您的捕获代码必须执行此操作(或者在抛出之前必须执行此操作,覆盖fillInStackTrace或在构造函数中执行此操作)。这些都不适用于未知代码抛出和捕获的未知异常。 – 2011-06-10 12:40:25

+0

我喜欢你在一个代码片段中有3种语言 - 英文,德文和Java :) – bacar 2012-08-17 18:29:20

2

您可以创建自定义异常,从一个异常中提取堆栈跟踪,并通过setStackTrace()将其添加到另一个异常。

当你不想维护对由异常引起的硬引用时,对于做这样的事情或维护堆栈跟踪非常有用。将异常信息从服务器传递到客户端时,这可能非常方便,因为根本原因异常类可能不存在,从而导致序列化问题。

0

我想建议一种替代解决方案,这不是OP要求的解决方案,但对于一些有类似问题的人可能会更好。像我这样的。

我建议在外部创建一个throwable,并从内部添加throwable作为throwable来自外部的原因。这也将捕捉到线程切换的地方......这可能有助于避免混淆堆栈跟踪。而且,线程信息可以存储在这个在外部创建的throwable中,这可能会有更多帮助。

这是一些代码。

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

然后使用这样的代码:

// 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(); 
}