在Java中锻造堆栈跟踪

在Java中锻造堆栈跟踪,java,exception-handling,rmi,rpc,stack-trace,Java,Exception Handling,Rmi,Rpc,Stack Trace,当您在Java中使用RMI时,当您收到异常时,异常的远程堆栈跟踪将被预先设置,有点像这样: 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.doServerMe

当您在Java中使用RMI时,当您收到异常时,异常的远程堆栈跟踪将被预先设置,有点像这样:

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()
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)
// 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();
}
这种stacktrace“伪造”是怎么做到的


我想要它做什么(除了休息)?好吧,如果我能做到这一点,我会有所帮助:

outer() {

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

  ).start();

  thread.join();

}
并使在
internal()
中抛出的异常在stacktrace中也具有
outer()
(以及链下游的方法),以便进行日志记录。

这很容易:

Throwable具有方法和
setStackTrace()

来自(非开源,但也许有一天我会打开远程呼叫引擎):

为方便您翻译:

合并堆栈跟踪。低端(在调用层次结构中较深的,在 数组的末尾/输出)是堆栈中已经存在的,即上端 将从当前堆栈中获取。在他们之间,我们将放一个 远程呼叫标记

private void mergeStackTraces(可丢弃错误)
{
StackTraceElement[]当前堆栈=
新的Throwable().getStackTrace();
int currentStackLimit=5;//TODO:raussuchen
StackTraceElement[]旧堆栈=
错误。getStackTrace();
StackTraceeElement[]zusammen=
新StackTraceElement[currentStack.length-currentStackLimit+
oldStack.length+1];
数组复制(oldStack,0,zusammen,0,oldStack.length);
祖萨曼[oldStack.length]=
新StackTraceElement(“══════════════════════════",
"",
"", -3);
System.arraycopy(currentStack、currentStackLimit、,
祖萨曼,奥德斯塔克。长度+1,
currentStack.length-currentStackLimit);
错误。setStackTrace(zusammen);
}
(在服务器端,我已经切断了堆栈跟踪中与方法调用本身无关的部分,即与消息处理相关的所有内容。)

这将产生如下组合堆栈跟踪:

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()
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)
// 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();
}
java.lang.SecurityException:Das Passwort für Nutzer»Paul«ist falsch。
在de.fenging_game.db.userdb.Db4oUserDB.login(Db4oUserDB.java:304)
在de.fenging_game.server.impl.StandardServers$SSServer$1.run(StandardServers.java:316)
在de.fenging_game.server.impl.StandardServers$SSServer$1.run(StandardServers.java:314)
位于java.security.AccessController.doPrivileged(本机方法)
在de.fenging_game.server.impl.StandardServers$SSServer.login(StandardServers.java:313)
在de.fenging_game.transport.server.ServerTransport$ConnectionInfo$4.login(ServerTransport.java:460)
在══════════════════════════.()
位于$Proxy1.login(未知来源)
在de.fenging_game.gui.basics.LoginUtils.login(LoginUtils.java:80)
在de.fenging_game.gui.lobb.connectTo(lobb.java:302)
在de.fenging_game.gui.Lobby$20.run(lobb.java:849)
在java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:226)中
位于java.awt.EventQueue.dispatchEventImpl(EventQueue.java:647)
在java.awt.EventQueue.access$000(EventQueue.java:96)
在java.awt.EventQueue$1.run(EventQueue.java:608)
在java.awt.EventQueue$1.run处(EventQueue.java:606)
位于java.security.AccessController.doPrivileged(本机方法)
位于java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
位于java.awt.EventQueue.dispatchEvent(EventQueue.java:617)
位于java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
在java.awt.EventDispatchThread.PumpeEventsforFilter(EventDispatchThread.java:200)
位于java.awt.EventDispatchThread.PumpeEventsforHierarchy(EventDispatchThread.java:190)
位于java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185)
位于java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177)
位于java.awt.EventDispatchThread.run(EventDispatchThread.java:138)
我认为RMI系统做了一些非常类似的事情(只是没有
══════════════════════════


编辑:
对于您的用例,您必须在内部线程启动时保存外部线程的堆栈跟踪,然后在run方法中捕获异常并将外部堆栈跟踪附加到内部异常的堆栈跟踪。不过,我确实建议使用某种类型的分隔符。

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


当您不想维护对“由异常引起的”的硬引用时,它对于执行类似操作或维护stacktrace非常有用。在将异常信息从服务器传递到客户端时,如果根本原因异常类可能不存在,这将非常方便,从而导致序列化问题。

我想建议一种替代方法e解决方案,这不是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;
                }
            }
        };
    }
}
然后使用如下代码:

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

我希望内部异常附加外部异常的stacktrace,即使它被内部线程中的深层内容捕获。我想这是不可能的,因为我无法应用自定义异常。Y