2011-04-15 23 views

回答

7

是的,可以操縱堆棧跟蹤。如上所述,這取決於你想要(並且可以)攻擊問題的位置。


作爲一個例子:

對於遠程方法調用協議我實現爲我們的項目,在我們捕獲它在目標側的異常的情況下,切斷下部一些StackTraceElements(其直到用反射實際調用目標方法爲止),並將堆棧跟蹤的重要部分發送到調用方。

那裏我用它的(發送的)堆棧跟蹤重建異常,然後將它與當前的堆棧跟蹤合併。 爲此,我們也除去頂部的當前棧跟蹤的一些元素(這僅包含遠程呼叫框架的呼叫):

private void mergeStackTraces(Throwable error) 
    { 
     StackTraceElement[] currentStack = 
      new Throwable().getStackTrace(); 
     int currentStackLimit = 4; // TODO: raussuchen 

     // We simply cut off the top 4 elements, which is just 
     // right for our framework. A more stable solution 
     // would be to filter by class name or such. 

     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: The user example does not exist 
    at de.fencing_game.db.userdb.Db4oUserDB.login(Db4oUserDB.java:306) 
    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) 

當然,對於您的情況,您最好只是遍歷數組,將重要元素複製到列表中,然後將其設置爲新的stackTrace。確保這樣做的原因(即鏈接throwables),也一樣。

您可以在異常的構造函數中,或者打印堆棧軌跡的位置,或者在捕捉,操作和重新拋出異常的地方之間的任何地方執行此操作。

2

您可以創建一個讀取行的輸出流,並刪除與特定正則表達式匹配的輸出流。

然後使用Exception.printStackTrace(YourFilteredOutputStream);

如果你使用的是log4j,你可以寫一個Appender來做到這一點。使用假設Appender

Log4j配置(Examples)

<appender name="TRACE" class="com.example.YourAppender"> 
      <layout class="org.apache.log4j.PatternLayout"> 
        <param name="ConversionPattern" value="[%t] %-5p %c - %m%n" /> 
      </layout> 
    </appender> 

我,我會很可能子類FileAppender:

public class YourAppender extends FileAppender { 
    public YourAppender(...) { 
     super(...); 
    } 
    public void doAppend(LoggingEvent ev) { 
     String message = ev.getMessage(); // or ev.getRenderedMessage(); 

     // build new LoggingEvent 
     LoggingEvent newEv = new LoggingEvent(/* params from old loggingg event */); 
     super.doAppend(newEv); 
    } 
} 
2

您可以從您的堆棧跟蹤刪除條目爲您捕捉異常。您可以爲您的例外覆蓋printStackTrace。您還可以創建自定義記錄器來忽略某些行。很大程度上取決於你的控制權。

3

我同情。 如何隱藏不需要的StackTraceElements,並生成自己的堆棧跟蹤輸出?

事情是這樣的:

Set<String> hideClassNames = ....; 
... 
void print(Throwable t, PrintStream out) { 
    for (Throwable c = e; c != null;) { 
    for (StackTraceElement e : c.getStackTrace()) { 
     if (!hideClassNames.contains(e.getClassName())) { 
     out.println(e.getClassName() + 
        "." + e.getMethodName() + 
        " (" + e.getFileName() + 
        ":" + e.getLineNumber()) + ")"; 
     } 
    } 
    c = c.getCause(); 
    if (c != null) { 
     out.println("Caused by"); 
    } 
} 

您可以在自定義記錄器使用這樣的代碼。您也可以使用它來記錄未捕獲的異常:

Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { 
    public void uncaughtException(Thread t, Throwable e) { 
    print(t, System.err); 
    } 
}; 
Thread.setDefaultUncaughtExceptionHandler(handler); 
+0

這仍然錯過了過濾,雖然:-p – 2011-04-15 19:37:34

0

這是有點過時的帖子,但對於它的價值,如果你使用Log4J或SL4J,你可以設置屬性來只顯示你想要的項目。它非常靈活。