开发者

Throwing exceptions so that stack trace doesn't contain certain class types

Is it possible to do this ?

The problem is, that huge applications have tons of servlet filters for instance. And each exception that is thrown regarding http request contains 250 lines when 160 of them is from catalina/tom开发者_StackOverflow中文版cat stack, which is absolutely not important.

And having 250 lines long stack traces is very hard to work with.


Yes, it is possible to manipulate the stack trace. As said, it depends on where you want to (and can) attack the problem.


As an example:

For a remote method-call protocol I implemented for our project, in the case of an exception we catch it at the target side, cut off the lower some StackTraceElements (which are always the same, until the actual calling of the target method with reflection), and send the exception with the important part of the stack trace to the caller side.

There I reconstruct the exception with its (sent) stack trace, and then merge it with the current Stack trace. For this, we also remove the top some elements of the current stack trace (which contain only calls of the remote-call framework):

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

This gives, for example, this trace printed:

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)

Of course, for your case you would better simply iterate through your array, copy the important elements into a list, and then set this as the new stackTrace. Make sure to do this for the causes (i.e. linked throwables), too.

You can do this in the constructor of your exceptions, or where you print the stack traces, or anywhere between (where you catch, manipulate and rethrow the exception).


I empathize. How about hiding the undesired StackTraceElements, and generating your own stacktrace output?

Something like this:

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

You can use code like this in a custom logger. You can also use it to log uncaught exceptions:

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


You can remove entries from your stack trace for an Exception you catch. You can override printStackTrace for your exceptions. You can also create a custom logger to ignore certain lines. Alot depends on what you have control over.


You could create an outputstream that reads in lines and eliminates ones that match a particular regex.

Then use Exception.printStackTrace( YourFilteredOutputStream );

If you're using log4j you could write an Appender that does this.

Log4j configuration (Examples) using a hypothetical Appender:

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

Me, I would probably subclass 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); 
   }
}


It's a bit of a dated post, but for what it's worth, if you use Log4J or SL4J, you can set the properties to only show the items you want. It's quite flexible.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜