当您在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()
这种堆栈跟踪如何“伪造”完成?
我想要什么(除了被激活之外)?好吧,如果我能做到这一点,它会对我有所帮助:
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()
(以及链中的方法)。
答案 0 :(得分:10)
这很容易:
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方法中捕获异常并将外部堆栈跟踪附加到内部异常的堆栈跟踪。我真的建议放一些类型的分隔符。
答案 1 :(得分:2)
您创建一个自定义异常,从一个异常中提取堆栈跟踪,并通过 setStackTrace()将其添加到另一个异常。
当你不想保持对异常引起的硬引用时,这样做很有用或者维护堆栈跟踪。将异常信息从服务器传递到客户端时,这很方便,因为根本原因异常类可能不存在,从而导致序列化问题。
答案 2 :(得分: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();
}